add keyframe popover

add shadcn ui stuff
This commit is contained in:
2023-05-31 16:59:43 +02:00
parent e3098c4400
commit ebb2408a68
13 changed files with 671 additions and 1815 deletions

View File

@@ -0,0 +1,28 @@
import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "utils";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };

View File

@@ -2,12 +2,13 @@ import { ease } from "@unom/style";
import { PanInfo, motion } from "framer-motion";
import { AnimationData } from "primitives/AnimatedEntities";
import { Keyframe } from "primitives/Keyframe";
import { FC, useCallback, useState } from "react";
import { FC, useCallback, useMemo, useState } from "react";
import { z } from "zod";
import { TIMELINE_SCALE, calculateOffset } from "./common";
import { AnimatedNumber, AnimatedVec2, AnimatedVec3 } from "primitives/Values";
import { useKeyframeStore } from "stores/keyframe.store";
import { produce } from "immer";
import KeyframePopover from "./KeyframePopover";
const KeyframeIndicator: FC<{
keyframe: z.input<typeof Keyframe>;
@@ -17,8 +18,6 @@ const KeyframeIndicator: FC<{
const { selectedKeyframe, selectKeyframe, deselectKeyframe } =
useKeyframeStore();
const selected = selectedKeyframe === keyframe.id;
const handleUpdate = useCallback(
(info: PanInfo) => {
if (onUpdate) {
@@ -34,59 +33,85 @@ const KeyframeIndicator: FC<{
[onUpdate, animationData, keyframe]
);
const handleValueUpdate = useCallback(
(keyframe: z.input<typeof Keyframe>) => {
if (onUpdate) {
onUpdate(keyframe);
}
},
[onUpdate]
);
const selected = useMemo(
() => selectedKeyframe === keyframe.id,
[keyframe.id, selectedKeyframe]
);
const [isDragged, setIsDragged] = useState(false);
return (
<motion.div
drag="x"
variants={{
enter: {},
from: {},
exit: {},
tap: {},
drag: {},
}}
data-selected={selected}
onDragStart={() => setIsDragged(true)}
onDragEnd={(e, info) => {
e.preventDefault();
setIsDragged(false);
if (onUpdate) {
handleUpdate(info);
}
}}
onMouseDown={(e) => e.preventDefault()}
dragConstraints={{ left: 0 }}
initial={{
x: (animationData.offset + keyframe.offset) * TIMELINE_SCALE + 4,
scale: 0,
}}
whileTap={{ scale: 1.1 }}
animate={{
x: (animationData.offset + keyframe.offset) * TIMELINE_SCALE + 4,
scale: 1,
}}
transition={ease.quint(0.4).out}
onClick={() => {
if (!isDragged) {
selected ? deselectKeyframe() : selectKeyframe(keyframe.id);
}
}}
className="h-full absolute z-30 select-none w-3 flex items-center justify-center filter
data-[selected=true]:drop-shadow-[0px_2px_6px_rgba(255,255,255,1)] transition-colors"
>
<span
<>
<motion.div
drag="x"
variants={{
enter: {},
from: {},
exit: {},
tap: {},
drag: {},
}}
data-selected={selected}
className="bg-gray-200
onDragStart={() => setIsDragged(true)}
onDragEnd={(e, info) => {
e.preventDefault();
setIsDragged(false);
if (onUpdate) {
handleUpdate(info);
}
}}
onMouseDown={(e) => e.preventDefault()}
dragConstraints={{ left: 0 }}
initial={{
x: (animationData.offset + keyframe.offset) * TIMELINE_SCALE + 4,
scale: 0,
}}
whileTap={{
scale: 1.6,
}}
animate={{
x: (animationData.offset + keyframe.offset) * TIMELINE_SCALE + 4,
scale: 1,
}}
transition={ease.quint(0.4).out}
onClick={() => {
if (isDragged) {
if (!selected) selectKeyframe(keyframe.id);
} else {
selected ? deselectKeyframe() : selectKeyframe(keyframe.id);
}
}}
className="h-full absolute z-30 select-none w-3 flex items-center justify-center filter
data-[selected=true]:drop-shadow-[0px_2px_6px_rgba(230,230,255,1)] transition-colors"
>
<KeyframePopover
onUpdate={handleValueUpdate}
keyframe={keyframe}
open={selected}
/>
<motion.span
data-selected={selected}
className="bg-gray-200
data-[selected=true]:bg-indigo-600
h-full transition-colors"
style={{
width: 10,
height: 10,
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
}}
/>
</motion.div>
style={{
width: 10,
height: 10,
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
}}
/>
</motion.div>
</>
);
};

View File

@@ -0,0 +1,31 @@
import { PopoverContent, PopoverPortal } from "@radix-ui/react-popover";
import { Popover } from "components/Popover";
import { Keyframe } from "primitives/Keyframe";
import { FC } from "react";
import { z } from "zod";
const KeyframePopover: FC<{
open: boolean;
keyframe: z.input<typeof Keyframe>;
onUpdate: (k: z.input<typeof Keyframe>) => void;
}> = ({ open, keyframe, onUpdate }) => {
return (
<div>
<Popover open={open}>
<PopoverContent>
<label>
<span className="label">Value</span>
<input
onChange={(e) =>
onUpdate({ ...keyframe, value: Number(e.target.value) })
}
value={keyframe.value}
/>
</label>
</PopoverContent>
</Popover>
</div>
);
};
export default KeyframePopover;

View File

@@ -5,7 +5,7 @@ import {
} from "primitives/AnimatedEntities";
import { AnimatedProperty } from "primitives/AnimatedProperty";
import { AnimatedVec2, ValueType } from "primitives/Values";
import { FC, useCallback, useMemo, useState } from "react";
import { FC, memo, useCallback, useMemo, useState } from "react";
import { z } from "zod";
import {
AnimatedNumberKeyframeIndicator,
@@ -84,20 +84,32 @@ const TrackAnimatedProperty: FC<{
<h4>{animatedProperty.label}</h4>
<ToggleGroup>
<ToggleGroupItem
onClick={() => setSelectedDimension("x")}
onClick={() =>
selectedDimension === "x"
? setSelectedDimension(undefined)
: setSelectedDimension("x")
}
selected={selectedDimension === "x"}
>
X
</ToggleGroupItem>
<ToggleGroupItem
onClick={() => setSelectedDimension("y")}
onClick={() =>
selectedDimension === "y"
? setSelectedDimension(undefined)
: setSelectedDimension("y")
}
selected={selectedDimension === "y"}
>
Y
</ToggleGroupItem>
{animatedProperty.animatedValue.type === ValueType.Enum.Vec3 && (
<ToggleGroupItem
onClick={() => setSelectedDimension("z")}
onClick={() =>
selectedDimension === "z"
? setSelectedDimension(undefined)
: setSelectedDimension("z")
}
selected={selectedDimension === "z"}
>
Z
@@ -141,6 +153,8 @@ const TrackPropertiesEditor: FC<{
const parsedEntity = AnimatedEntity.parse(nextValue);
console.log("reacreated callback");
entitiesStore.updateEntityById(parsedEntity.id, parsedEntity);
},
[entity]
@@ -167,4 +181,4 @@ const TrackPropertiesEditor: FC<{
);
};
export default TrackPropertiesEditor;
export default memo(TrackPropertiesEditor);

View File

@@ -14,7 +14,7 @@ const ToggleGroupItem: FC<{
onClick={onClick}
className="hover:bg-indigo-600 text-white data-[selected=true]:bg-indigo-700
data-[selected=true]:text-indigo-200 flex h-6 w-6
items-center justify-center bg-slate-900 text-sm leading-4
items-center justify-center bg-slate-800 text-sm leading-4
first:rounded-l last:rounded-r focus:z-10 focus:shadow-[0_0_0_2px] focus:shadow-black
focus:outline-none transition-colors"
value="left"