add keyframe popover
add shadcn ui stuff
This commit is contained in:
parent
e3098c4400
commit
ebb2408a68
1343
app/package-lock.json
generated
1343
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
|||||||
"@radix-ui/react-form": "^0.0.2",
|
"@radix-ui/react-form": "^0.0.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-menubar": "^1.0.2",
|
"@radix-ui/react-menubar": "^1.0.2",
|
||||||
|
"@radix-ui/react-popover": "^1.0.6",
|
||||||
"@radix-ui/react-slider": "^1.1.1",
|
"@radix-ui/react-slider": "^1.1.1",
|
||||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||||
"@radix-ui/react-toolbar": "^1.0.3",
|
"@radix-ui/react-toolbar": "^1.0.3",
|
||||||
@ -22,11 +23,16 @@
|
|||||||
"@types/lodash.set": "^4.3.7",
|
"@types/lodash.set": "^4.3.7",
|
||||||
"@unom/style": "^0.2.14",
|
"@unom/style": "^0.2.14",
|
||||||
"canvaskit-wasm": "^0.38.1",
|
"canvaskit-wasm": "^0.38.1",
|
||||||
|
"class-variance-authority": "^0.6.0",
|
||||||
|
"clsx": "^1.2.1",
|
||||||
"framer-motion": "^10.12.12",
|
"framer-motion": "^10.12.12",
|
||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
|
"lucide-react": "^0.229.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"tailwind-merge": "^1.12.0",
|
||||||
|
"tailwindcss-animate": "^1.0.5",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"zod": "^3.21.4",
|
"zod": "^3.21.4",
|
||||||
"zustand": "^4.3.8"
|
"zustand": "^4.3.8"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use font_kit::source::SystemSource;
|
use font_kit::source::SystemSource;
|
||||||
use rayon::prelude::*;
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_system_fonts() -> Option<Vec<String>> {
|
pub fn get_system_fonts() -> Option<Vec<String>> {
|
||||||
@ -10,7 +9,7 @@ pub fn get_system_fonts() -> Option<Vec<String>> {
|
|||||||
match found_fonts {
|
match found_fonts {
|
||||||
Ok(found_fonts) => {
|
Ok(found_fonts) => {
|
||||||
let font_names: Vec<String> = found_fonts
|
let font_names: Vec<String> = found_fonts
|
||||||
.par_iter()
|
.iter()
|
||||||
.map(|f| f.load())
|
.map(|f| f.load())
|
||||||
.filter(|f| f.is_ok())
|
.filter(|f| f.is_ok())
|
||||||
.map(|f| f.unwrap())
|
.map(|f| f.unwrap())
|
||||||
@ -54,6 +53,6 @@ pub fn get_system_font(font_name: String) -> Option<Vec<u8>> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => None,
|
Err(_) => panic!("Err"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
app/src/components/Popover.tsx
Normal file
28
app/src/components/Popover.tsx
Normal 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 };
|
@ -2,12 +2,13 @@ import { ease } from "@unom/style";
|
|||||||
import { PanInfo, motion } from "framer-motion";
|
import { PanInfo, motion } from "framer-motion";
|
||||||
import { AnimationData } from "primitives/AnimatedEntities";
|
import { AnimationData } from "primitives/AnimatedEntities";
|
||||||
import { Keyframe } from "primitives/Keyframe";
|
import { Keyframe } from "primitives/Keyframe";
|
||||||
import { FC, useCallback, useState } from "react";
|
import { FC, useCallback, useMemo, useState } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { TIMELINE_SCALE, calculateOffset } from "./common";
|
import { TIMELINE_SCALE, calculateOffset } from "./common";
|
||||||
import { AnimatedNumber, AnimatedVec2, AnimatedVec3 } from "primitives/Values";
|
import { AnimatedNumber, AnimatedVec2, AnimatedVec3 } from "primitives/Values";
|
||||||
import { useKeyframeStore } from "stores/keyframe.store";
|
import { useKeyframeStore } from "stores/keyframe.store";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
import KeyframePopover from "./KeyframePopover";
|
||||||
|
|
||||||
const KeyframeIndicator: FC<{
|
const KeyframeIndicator: FC<{
|
||||||
keyframe: z.input<typeof Keyframe>;
|
keyframe: z.input<typeof Keyframe>;
|
||||||
@ -17,8 +18,6 @@ const KeyframeIndicator: FC<{
|
|||||||
const { selectedKeyframe, selectKeyframe, deselectKeyframe } =
|
const { selectedKeyframe, selectKeyframe, deselectKeyframe } =
|
||||||
useKeyframeStore();
|
useKeyframeStore();
|
||||||
|
|
||||||
const selected = selectedKeyframe === keyframe.id;
|
|
||||||
|
|
||||||
const handleUpdate = useCallback(
|
const handleUpdate = useCallback(
|
||||||
(info: PanInfo) => {
|
(info: PanInfo) => {
|
||||||
if (onUpdate) {
|
if (onUpdate) {
|
||||||
@ -34,59 +33,85 @@ const KeyframeIndicator: FC<{
|
|||||||
[onUpdate, animationData, keyframe]
|
[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);
|
const [isDragged, setIsDragged] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<>
|
||||||
drag="x"
|
<motion.div
|
||||||
variants={{
|
drag="x"
|
||||||
enter: {},
|
variants={{
|
||||||
from: {},
|
enter: {},
|
||||||
exit: {},
|
from: {},
|
||||||
tap: {},
|
exit: {},
|
||||||
drag: {},
|
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
|
|
||||||
data-selected={selected}
|
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
|
data-[selected=true]:bg-indigo-600
|
||||||
h-full transition-colors"
|
h-full transition-colors"
|
||||||
style={{
|
style={{
|
||||||
width: 10,
|
width: 10,
|
||||||
height: 10,
|
height: 10,
|
||||||
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
|
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
31
app/src/components/Timeline/KeyframePopover.tsx
Normal file
31
app/src/components/Timeline/KeyframePopover.tsx
Normal 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;
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from "primitives/AnimatedEntities";
|
} from "primitives/AnimatedEntities";
|
||||||
import { AnimatedProperty } from "primitives/AnimatedProperty";
|
import { AnimatedProperty } from "primitives/AnimatedProperty";
|
||||||
import { AnimatedVec2, ValueType } from "primitives/Values";
|
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 { z } from "zod";
|
||||||
import {
|
import {
|
||||||
AnimatedNumberKeyframeIndicator,
|
AnimatedNumberKeyframeIndicator,
|
||||||
@ -84,20 +84,32 @@ const TrackAnimatedProperty: FC<{
|
|||||||
<h4>{animatedProperty.label}</h4>
|
<h4>{animatedProperty.label}</h4>
|
||||||
<ToggleGroup>
|
<ToggleGroup>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
onClick={() => setSelectedDimension("x")}
|
onClick={() =>
|
||||||
|
selectedDimension === "x"
|
||||||
|
? setSelectedDimension(undefined)
|
||||||
|
: setSelectedDimension("x")
|
||||||
|
}
|
||||||
selected={selectedDimension === "x"}
|
selected={selectedDimension === "x"}
|
||||||
>
|
>
|
||||||
X
|
X
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
onClick={() => setSelectedDimension("y")}
|
onClick={() =>
|
||||||
|
selectedDimension === "y"
|
||||||
|
? setSelectedDimension(undefined)
|
||||||
|
: setSelectedDimension("y")
|
||||||
|
}
|
||||||
selected={selectedDimension === "y"}
|
selected={selectedDimension === "y"}
|
||||||
>
|
>
|
||||||
Y
|
Y
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
{animatedProperty.animatedValue.type === ValueType.Enum.Vec3 && (
|
{animatedProperty.animatedValue.type === ValueType.Enum.Vec3 && (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
onClick={() => setSelectedDimension("z")}
|
onClick={() =>
|
||||||
|
selectedDimension === "z"
|
||||||
|
? setSelectedDimension(undefined)
|
||||||
|
: setSelectedDimension("z")
|
||||||
|
}
|
||||||
selected={selectedDimension === "z"}
|
selected={selectedDimension === "z"}
|
||||||
>
|
>
|
||||||
Z
|
Z
|
||||||
@ -141,6 +153,8 @@ const TrackPropertiesEditor: FC<{
|
|||||||
|
|
||||||
const parsedEntity = AnimatedEntity.parse(nextValue);
|
const parsedEntity = AnimatedEntity.parse(nextValue);
|
||||||
|
|
||||||
|
console.log("reacreated callback");
|
||||||
|
|
||||||
entitiesStore.updateEntityById(parsedEntity.id, parsedEntity);
|
entitiesStore.updateEntityById(parsedEntity.id, parsedEntity);
|
||||||
},
|
},
|
||||||
[entity]
|
[entity]
|
||||||
@ -167,4 +181,4 @@ const TrackPropertiesEditor: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TrackPropertiesEditor;
|
export default memo(TrackPropertiesEditor);
|
||||||
|
@ -14,7 +14,7 @@ const ToggleGroupItem: FC<{
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="hover:bg-indigo-600 text-white data-[selected=true]:bg-indigo-700
|
className="hover:bg-indigo-600 text-white data-[selected=true]:bg-indigo-700
|
||||||
data-[selected=true]:text-indigo-200 flex h-6 w-6
|
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
|
first:rounded-l last:rounded-r focus:z-10 focus:shadow-[0_0_0_2px] focus:shadow-black
|
||||||
focus:outline-none transition-colors"
|
focus:outline-none transition-colors"
|
||||||
value="left"
|
value="left"
|
||||||
|
@ -156,7 +156,7 @@ function buildText(
|
|||||||
type: "Fill",
|
type: "Fill",
|
||||||
color,
|
color,
|
||||||
},
|
},
|
||||||
font_name: "Arial",
|
font_name: "Gilroy-Regular",
|
||||||
size,
|
size,
|
||||||
align: "Center",
|
align: "Center",
|
||||||
},
|
},
|
||||||
@ -226,7 +226,7 @@ function buildStaggeredText(
|
|||||||
stagger: 0.1,
|
stagger: 0.1,
|
||||||
letter: {
|
letter: {
|
||||||
paint: {
|
paint: {
|
||||||
font_name: "Arial",
|
font_name: "Gilroy-Regular",
|
||||||
style: {
|
style: {
|
||||||
type: "Fill",
|
type: "Fill",
|
||||||
color,
|
color,
|
||||||
|
@ -44,6 +44,82 @@
|
|||||||
select {
|
select {
|
||||||
@apply appearance-none;
|
@apply appearance-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
--input: 214.3 31.8% 91.4%;
|
||||||
|
|
||||||
|
--primary: 222.2 47.4% 11.2%;
|
||||||
|
--primary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--secondary: 210 40% 96.1%;
|
||||||
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--destructive: 0 100% 50%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 215 20.2% 65.1%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 224 71% 4%;
|
||||||
|
--foreground: 213 31% 91%;
|
||||||
|
|
||||||
|
--muted: 223 47% 11%;
|
||||||
|
--muted-foreground: 215.4 16.3% 56.9%;
|
||||||
|
|
||||||
|
--popover: 224 71% 4%;
|
||||||
|
--popover-foreground: 215 20.2% 65.1%;
|
||||||
|
|
||||||
|
--card: 224 71% 4%;
|
||||||
|
--card-foreground: 213 31% 91%;
|
||||||
|
|
||||||
|
--border: 216 34% 17%;
|
||||||
|
--input: 216 34% 17%;
|
||||||
|
|
||||||
|
--primary: 210 40% 98%;
|
||||||
|
--primary-foreground: 222.2 47.4% 1.2%;
|
||||||
|
|
||||||
|
--secondary: 222.2 47.4% 11.2%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--accent: 216 34% 17%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 63% 31%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 216 34% 17%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
font-feature-settings: "rlig" 1, "calt" 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
|
@ -2,6 +2,8 @@ import { AnimatedEntity } from "primitives/AnimatedEntities";
|
|||||||
import { Keyframe } from "primitives/Keyframe";
|
import { Keyframe } from "primitives/Keyframe";
|
||||||
import { AnimatedNumber, AnimatedVec2, AnimatedVec3 } from "primitives/Values";
|
import { AnimatedNumber, AnimatedVec2, AnimatedVec3 } from "primitives/Values";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { ClassValue, clsx } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export function flattenAnimatedNumberKeyframes(
|
export function flattenAnimatedNumberKeyframes(
|
||||||
aNumber: z.input<typeof AnimatedNumber>
|
aNumber: z.input<typeof AnimatedNumber>
|
||||||
@ -112,3 +114,7 @@ export function set(object: any, path: string, value: any) {
|
|||||||
[base]: value,
|
[base]: value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
@ -1,8 +1,71 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: "hsl(var(--border))",
|
||||||
|
input: "hsl(var(--input))",
|
||||||
|
ring: "hsl(var(--ring))",
|
||||||
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary))",
|
||||||
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted))",
|
||||||
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent))",
|
||||||
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover))",
|
||||||
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card))",
|
||||||
|
foreground: "hsl(var(--card-foreground))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
"accordion-down": {
|
||||||
|
from: { height: 0 },
|
||||||
|
to: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
},
|
||||||
|
"accordion-up": {
|
||||||
|
from: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
to: { height: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [require("tailwindcss-animate")],
|
||||||
};
|
};
|
||||||
|
769
app/yarn.lock
769
app/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user