doodleui-react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { createContext, forwardRef, useState, useId, useRef, useLayoutEffect, useContext, Children, isValidElement, cloneElement, useEffect, useCallback, useMemo } from 'react';
2
+ import { createContext, forwardRef, useRef, useState, useId, useLayoutEffect, useContext, Children, isValidElement, cloneElement, useEffect, useSyncExternalStore, useCallback, useMemo } from 'react';
3
3
  import rough from 'roughjs';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
6
6
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
7
7
  import * as Dialog from '@radix-ui/react-dialog';
8
+ import { motion, AnimatePresence } from 'framer-motion';
8
9
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
9
10
  import * as SelectPrimitive from '@radix-ui/react-select';
10
11
  import * as SwitchPrimitive from '@radix-ui/react-switch';
@@ -48,6 +49,11 @@ var SKETCH_COLORS = {
48
49
  };
49
50
 
50
51
  // src/utils.ts
52
+ function assignRef(ref, value) {
53
+ if (!ref) return;
54
+ if (typeof ref === "function") ref(value);
55
+ else ref.current = value;
56
+ }
51
57
  function randomSeed() {
52
58
  return Math.floor(Math.random() * 2 ** 31);
53
59
  }
@@ -248,6 +254,177 @@ function RoughSvg({
248
254
  }
249
255
  );
250
256
  }
257
+ var DoodleUIContext = createContext(null);
258
+ function DoodleUIProvider({
259
+ children,
260
+ animate = true,
261
+ forceAnimate = false
262
+ }) {
263
+ const value = useMemo(
264
+ () => ({ animate, forceAnimate }),
265
+ [animate, forceAnimate]
266
+ );
267
+ return /* @__PURE__ */ jsx(DoodleUIContext.Provider, { value, children });
268
+ }
269
+ function useDoodleUI() {
270
+ return useContext(DoodleUIContext) ?? { animate: true, forceAnimate: false };
271
+ }
272
+
273
+ // src/animations/resolveAnimate.ts
274
+ function resolveAnimate({
275
+ animate,
276
+ providerAnimate = true,
277
+ forceAnimate = false,
278
+ prefersReducedMotion = false
279
+ }) {
280
+ if (prefersReducedMotion && !forceAnimate) return false;
281
+ if (animate !== void 0) return animate;
282
+ return providerAnimate;
283
+ }
284
+ function subscribe(onStoreChange) {
285
+ const media = window.matchMedia("(prefers-reduced-motion: reduce)");
286
+ media.addEventListener("change", onStoreChange);
287
+ return () => media.removeEventListener("change", onStoreChange);
288
+ }
289
+ function getSnapshot() {
290
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
291
+ }
292
+ function getServerSnapshot() {
293
+ return false;
294
+ }
295
+ function usePrefersReducedMotion() {
296
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
297
+ }
298
+
299
+ // src/animations/useAnimate.ts
300
+ function useAnimate(animate) {
301
+ const { animate: providerAnimate, forceAnimate } = useDoodleUI();
302
+ const prefersReducedMotion = usePrefersReducedMotion();
303
+ return resolveAnimate({
304
+ animate,
305
+ providerAnimate,
306
+ forceAnimate,
307
+ prefersReducedMotion
308
+ });
309
+ }
310
+ function useTweenNumber(target, enabled, duration = 400) {
311
+ const [value, setValue] = useState(target);
312
+ const valueRef = useRef(target);
313
+ valueRef.current = value;
314
+ useEffect(() => {
315
+ if (!enabled) {
316
+ valueRef.current = target;
317
+ setValue(target);
318
+ return;
319
+ }
320
+ const from = valueRef.current;
321
+ if (from === target) return;
322
+ const start = performance.now();
323
+ let frame = 0;
324
+ const tick = (now) => {
325
+ const t = Math.min(1, (now - start) / duration);
326
+ const eased = 1 - (1 - t) ** 3;
327
+ const next = from + (target - from) * eased;
328
+ valueRef.current = next;
329
+ setValue(next);
330
+ if (t < 1) frame = requestAnimationFrame(tick);
331
+ };
332
+ frame = requestAnimationFrame(tick);
333
+ return () => cancelAnimationFrame(frame);
334
+ }, [target, enabled, duration]);
335
+ return enabled ? value : target;
336
+ }
337
+ var DRAW_IN_DURATION_MS = 400;
338
+ var DRAW_IN_ALERT_MS = 200;
339
+ var DRAW_IN_TOOLTIP_MS = 180;
340
+ var DRAW_IN_MARK_MS = 240;
341
+ var TABLE_STAGGER_MS = 40;
342
+ var DRAW_IN_EASING = "ease-out";
343
+ var running = /* @__PURE__ */ new WeakMap();
344
+ function collectStrokePaths(root) {
345
+ const groups = root.querySelectorAll("g");
346
+ if (groups.length > 0) {
347
+ const paths = [];
348
+ groups.forEach((group) => {
349
+ const children = group.querySelectorAll(":scope > path");
350
+ if (children.length === 0) return;
351
+ paths.push(children[children.length - 1]);
352
+ });
353
+ return paths;
354
+ }
355
+ return Array.from(root.querySelectorAll("path"));
356
+ }
357
+ function getStrokePaths(target) {
358
+ if (target instanceof SVGPathElement) return [target];
359
+ if (target instanceof SVGSVGElement) return collectStrokePaths(target);
360
+ const svgs = target.querySelectorAll("svg");
361
+ if (svgs.length > 0) {
362
+ return Array.from(svgs).flatMap((svg) => collectStrokePaths(svg));
363
+ }
364
+ return collectStrokePaths(target);
365
+ }
366
+ function clearDash(path) {
367
+ path.style.strokeDasharray = "";
368
+ path.style.strokeDashoffset = "";
369
+ }
370
+ function cancelDrawIn(target) {
371
+ if (!target) return;
372
+ const animations = running.get(target);
373
+ animations?.forEach((animation) => animation.cancel());
374
+ running.delete(target);
375
+ getStrokePaths(target).forEach(clearDash);
376
+ }
377
+ function drawIn(target, duration = DRAW_IN_DURATION_MS, delay = 0) {
378
+ cancelDrawIn(target);
379
+ const paths = getStrokePaths(target);
380
+ const animations = [];
381
+ for (const path of paths) {
382
+ let length = 0;
383
+ try {
384
+ length = path.getTotalLength();
385
+ } catch {
386
+ continue;
387
+ }
388
+ if (length <= 0) continue;
389
+ path.style.strokeDasharray = `${length}`;
390
+ path.style.strokeDashoffset = `${length}`;
391
+ const animation = path.animate(
392
+ [{ strokeDashoffset: String(length) }, { strokeDashoffset: "0" }],
393
+ { duration, delay, easing: DRAW_IN_EASING, fill: "forwards" }
394
+ );
395
+ animation.finished.then(() => {
396
+ clearDash(path);
397
+ }).catch(() => {
398
+ });
399
+ animations.push(animation);
400
+ }
401
+ running.set(target, animations);
402
+ }
403
+ function useDrawIn(pathRef, duration = DRAW_IN_DURATION_MS, enabled = true, replayKey, delay = 0) {
404
+ useLayoutEffect(() => {
405
+ const target = pathRef.current;
406
+ if (!target) return;
407
+ if (!enabled) {
408
+ cancelDrawIn(target);
409
+ return;
410
+ }
411
+ const apply = () => drawIn(target, duration, delay);
412
+ if (getStrokePaths(target).length > 0) {
413
+ apply();
414
+ return () => cancelDrawIn(target);
415
+ }
416
+ const observer = new MutationObserver(() => {
417
+ if (getStrokePaths(target).length === 0) return;
418
+ observer.disconnect();
419
+ apply();
420
+ });
421
+ observer.observe(target, { childList: true, subtree: true });
422
+ return () => {
423
+ observer.disconnect();
424
+ cancelDrawIn(target);
425
+ };
426
+ }, [pathRef, duration, enabled, replayKey, delay]);
427
+ }
251
428
  var SketchBox = forwardRef(
252
429
  function SketchBox2({
253
430
  children,
@@ -266,12 +443,21 @@ var SketchBox = forwardRef(
266
443
  strokeWidth,
267
444
  inset,
268
445
  path,
446
+ animate,
447
+ drawInDuration = DRAW_IN_DURATION_MS,
448
+ drawInKey,
269
449
  ...rest
270
450
  }, ref) {
451
+ const rootRef = useRef(null);
452
+ const shouldAnimate = useAnimate(animate);
453
+ useDrawIn(rootRef, drawInDuration, shouldAnimate, drawInKey);
271
454
  return /* @__PURE__ */ jsxs(
272
455
  "div",
273
456
  {
274
- ref,
457
+ ref: (node) => {
458
+ rootRef.current = node;
459
+ assignRef(ref, node);
460
+ },
275
461
  className: cn(className),
276
462
  style: { position: "relative", ...style },
277
463
  ...rest,
@@ -329,6 +515,10 @@ var SIZE_STYLES = {
329
515
  md: { fontSize: 15, padding: "8px 16px", minHeight: 38 },
330
516
  lg: { fontSize: 17, padding: "11px 22px", minHeight: 46 }
331
517
  };
518
+ function assignRef2(ref, value) {
519
+ if (typeof ref === "function") ref(value);
520
+ else if (ref) ref.current = value;
521
+ }
332
522
  var Button = forwardRef(
333
523
  function Button2({
334
524
  children,
@@ -343,18 +533,28 @@ var Button = forwardRef(
343
533
  fillStyle,
344
534
  strokeWidth,
345
535
  disabled,
536
+ animate,
346
537
  onMouseEnter,
347
538
  onMouseLeave,
348
539
  ...rest
349
540
  }, ref) {
541
+ const rootRef = useRef(null);
350
542
  const [hovered, setHovered] = useState(false);
543
+ const shouldAnimate = useAnimate(animate);
544
+ const resolvedSeed = useResolvedSeed(seed);
545
+ const hoverSeed = deriveSeed(resolvedSeed, "hover");
546
+ const sketchSeed = shouldAnimate && hovered && !disabled ? hoverSeed : resolvedSeed;
351
547
  const ink = sketchColor ?? SKETCH_COLORS.ink;
352
548
  const fill = variant === "primary" ? SKETCH_COLORS.accentFill : variant === "secondary" ? SKETCH_COLORS.secondaryFill : void 0;
353
549
  const stroke = variant === "ghost" && !hovered ? "transparent" : variant === "primary" ? sketchColor ?? SKETCH_COLORS.accent : ink;
550
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
354
551
  return /* @__PURE__ */ jsxs(
355
552
  "button",
356
553
  {
357
- ref,
554
+ ref: (node) => {
555
+ rootRef.current = node;
556
+ assignRef2(ref, node);
557
+ },
358
558
  type: "button",
359
559
  className: cn(className),
360
560
  disabled,
@@ -390,12 +590,12 @@ var Button = forwardRef(
390
590
  {
391
591
  shape: "rectangle",
392
592
  roughness,
393
- seed,
593
+ seed: sketchSeed,
394
594
  sketchColor: stroke,
395
595
  bowing,
396
596
  fillStyle: fillStyle ?? "hachure",
397
597
  fill,
398
- strokeWidth: hovered ? (strokeWidth ?? 1.75) + 0.4 : strokeWidth
598
+ strokeWidth: !shouldAnimate && hovered ? (strokeWidth ?? 1.75) + 0.4 : strokeWidth
399
599
  }
400
600
  ),
401
601
  /* @__PURE__ */ jsx("span", { style: { position: "relative", zIndex: 1 }, children })
@@ -417,11 +617,18 @@ var Input = forwardRef(function Input2({
417
617
  id,
418
618
  onFocus,
419
619
  onBlur,
620
+ animate,
420
621
  ...rest
421
622
  }, ref) {
422
623
  const [focused, setFocused] = useState(false);
624
+ const fieldRef = useRef(null);
625
+ const shouldAnimate = useAnimate(animate);
626
+ const resolvedSeed = useResolvedSeed(seed);
627
+ const focusSeed = deriveSeed(resolvedSeed, "focus");
628
+ const sketchSeed = shouldAnimate && focused ? focusSeed : resolvedSeed;
423
629
  const ink = sketchColor ?? SKETCH_COLORS.ink;
424
630
  const inputId = id;
631
+ useDrawIn(fieldRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
425
632
  return /* @__PURE__ */ jsxs(
426
633
  "label",
427
634
  {
@@ -445,17 +652,17 @@ var Input = forwardRef(function Input2({
445
652
  children: label
446
653
  }
447
654
  ) : null,
448
- /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "block" }, children: [
655
+ /* @__PURE__ */ jsxs("span", { ref: fieldRef, style: { position: "relative", display: "block" }, children: [
449
656
  /* @__PURE__ */ jsx(
450
657
  RoughSvg,
451
658
  {
452
659
  shape: "rectangle",
453
660
  roughness,
454
- seed,
661
+ seed: sketchSeed,
455
662
  sketchColor: focused ? SKETCH_COLORS.accent : ink,
456
663
  bowing,
457
664
  fillStyle,
458
- strokeWidth: focused ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth
665
+ strokeWidth: focused && !shouldAnimate ? (strokeWidth ?? 1.75) + 0.35 : focused ? (strokeWidth ?? 1.75) + 0.2 : strokeWidth
459
666
  }
460
667
  ),
461
668
  /* @__PURE__ */ jsx(
@@ -509,10 +716,17 @@ var Textarea = forwardRef(
509
716
  rows = 4,
510
717
  onFocus,
511
718
  onBlur,
719
+ animate,
512
720
  ...rest
513
721
  }, ref) {
514
722
  const [focused, setFocused] = useState(false);
723
+ const fieldRef = useRef(null);
724
+ const shouldAnimate = useAnimate(animate);
725
+ const resolvedSeed = useResolvedSeed(seed);
726
+ const focusSeed = deriveSeed(resolvedSeed, "focus");
727
+ const sketchSeed = shouldAnimate && focused ? focusSeed : resolvedSeed;
515
728
  const ink = sketchColor ?? SKETCH_COLORS.ink;
729
+ useDrawIn(fieldRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
516
730
  return /* @__PURE__ */ jsxs(
517
731
  "label",
518
732
  {
@@ -536,17 +750,17 @@ var Textarea = forwardRef(
536
750
  children: label
537
751
  }
538
752
  ) : null,
539
- /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "block" }, children: [
753
+ /* @__PURE__ */ jsxs("span", { ref: fieldRef, style: { position: "relative", display: "block" }, children: [
540
754
  /* @__PURE__ */ jsx(
541
755
  RoughSvg,
542
756
  {
543
757
  shape: "rectangle",
544
758
  roughness,
545
- seed,
759
+ seed: sketchSeed,
546
760
  sketchColor: focused ? SKETCH_COLORS.accent : ink,
547
761
  bowing,
548
762
  fillStyle,
549
- strokeWidth: focused ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth
763
+ strokeWidth: focused && !shouldAnimate ? (strokeWidth ?? 1.75) + 0.35 : focused ? (strokeWidth ?? 1.75) + 0.2 : strokeWidth
550
764
  }
551
765
  ),
552
766
  /* @__PURE__ */ jsx(
@@ -591,6 +805,36 @@ var Textarea = forwardRef(
591
805
  );
592
806
  var BOX = 20;
593
807
  var CHECK_PATH = "M 4.5 10.5 L 8.5 14.5 L 15.5 5.5";
808
+ function CheckMark({
809
+ roughness,
810
+ seed,
811
+ bowing,
812
+ strokeWidth,
813
+ shouldAnimate
814
+ }) {
815
+ const ref = useRef(null);
816
+ useDrawIn(ref, DRAW_IN_MARK_MS, shouldAnimate);
817
+ return /* @__PURE__ */ jsx(
818
+ "span",
819
+ {
820
+ ref,
821
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
822
+ children: /* @__PURE__ */ jsx(
823
+ RoughSvg,
824
+ {
825
+ shape: "path",
826
+ path: CHECK_PATH,
827
+ roughness: (roughness ?? 1.5) * 0.7,
828
+ seed,
829
+ sketchColor: SKETCH_COLORS.accent,
830
+ bowing,
831
+ strokeWidth: (strokeWidth ?? 1.6) + 0.4,
832
+ inset: 0
833
+ }
834
+ )
835
+ }
836
+ );
837
+ }
594
838
  var Checkbox = forwardRef(
595
839
  function Checkbox2({
596
840
  className,
@@ -606,11 +850,15 @@ var Checkbox = forwardRef(
606
850
  defaultChecked,
607
851
  onCheckedChange,
608
852
  id,
853
+ animate,
609
854
  ...rest
610
855
  }, ref) {
611
856
  const ink = sketchColor ?? SKETCH_COLORS.ink;
612
857
  const generatedId = useId();
613
858
  const inputId = id ?? generatedId;
859
+ const boxRef = useRef(null);
860
+ const shouldAnimate = useAnimate(animate);
861
+ useDrawIn(boxRef, DRAW_IN_DURATION_MS, shouldAnimate);
614
862
  return /* @__PURE__ */ jsxs(
615
863
  "span",
616
864
  {
@@ -645,16 +893,23 @@ var Checkbox = forwardRef(
645
893
  ...rest,
646
894
  children: [
647
895
  /* @__PURE__ */ jsx(
648
- RoughSvg,
896
+ "span",
649
897
  {
650
- shape: "rectangle",
651
- roughness,
652
- seed,
653
- sketchColor: ink,
654
- bowing,
655
- fillStyle,
656
- strokeWidth: strokeWidth ?? 1.6,
657
- inset: 1.5
898
+ ref: boxRef,
899
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
900
+ children: /* @__PURE__ */ jsx(
901
+ RoughSvg,
902
+ {
903
+ shape: "rectangle",
904
+ roughness,
905
+ seed,
906
+ sketchColor: ink,
907
+ bowing,
908
+ fillStyle,
909
+ strokeWidth: strokeWidth ?? 1.6,
910
+ inset: 1.5
911
+ }
912
+ )
658
913
  }
659
914
  ),
660
915
  /* @__PURE__ */ jsx(
@@ -666,16 +921,13 @@ var Checkbox = forwardRef(
666
921
  display: "block"
667
922
  },
668
923
  children: /* @__PURE__ */ jsx(
669
- RoughSvg,
924
+ CheckMark,
670
925
  {
671
- shape: "path",
672
- path: CHECK_PATH,
673
- roughness: (roughness ?? 1.5) * 0.7,
926
+ roughness,
674
927
  seed,
675
- sketchColor: SKETCH_COLORS.accent,
676
928
  bowing,
677
- strokeWidth: (strokeWidth ?? 1.6) + 0.4,
678
- inset: 0
929
+ strokeWidth,
930
+ shouldAnimate
679
931
  }
680
932
  )
681
933
  }
@@ -716,6 +968,53 @@ var RadioGroup = forwardRef(
716
968
  }
717
969
  );
718
970
  var SIZE = 20;
971
+ function RadioDot({
972
+ roughness,
973
+ seed,
974
+ bowing,
975
+ shouldAnimate
976
+ }) {
977
+ const ref = useRef(null);
978
+ useDrawIn(ref, DRAW_IN_MARK_MS, shouldAnimate);
979
+ useLayoutEffect(() => {
980
+ const node = ref.current;
981
+ if (!node || !shouldAnimate) return;
982
+ const animation = node.animate(
983
+ [
984
+ { transform: "scale(0.35)", opacity: 0 },
985
+ { transform: "scale(1)", opacity: 1 }
986
+ ],
987
+ { duration: 180, easing: "ease-out", fill: "forwards" }
988
+ );
989
+ return () => animation.cancel();
990
+ }, [shouldAnimate]);
991
+ return /* @__PURE__ */ jsx(
992
+ "span",
993
+ {
994
+ ref,
995
+ style: {
996
+ position: "absolute",
997
+ inset: 0,
998
+ pointerEvents: "none",
999
+ display: "block"
1000
+ },
1001
+ children: /* @__PURE__ */ jsx(
1002
+ RoughSvg,
1003
+ {
1004
+ shape: "ellipse",
1005
+ roughness: (roughness ?? 1.5) + 0.2,
1006
+ seed,
1007
+ sketchColor: SKETCH_COLORS.accent,
1008
+ fill: SKETCH_COLORS.accent,
1009
+ fillStyle: "solid",
1010
+ bowing,
1011
+ strokeWidth: 1,
1012
+ inset: 6
1013
+ }
1014
+ )
1015
+ }
1016
+ );
1017
+ }
719
1018
  var Radio = forwardRef(function Radio2({
720
1019
  className,
721
1020
  style,
@@ -727,11 +1026,15 @@ var Radio = forwardRef(function Radio2({
727
1026
  fillStyle,
728
1027
  strokeWidth,
729
1028
  id,
1029
+ animate,
730
1030
  ...rest
731
1031
  }, ref) {
732
1032
  const ink = sketchColor ?? SKETCH_COLORS.ink;
733
1033
  const generatedId = useId();
734
1034
  const inputId = id ?? generatedId;
1035
+ const ringRef = useRef(null);
1036
+ const shouldAnimate = useAnimate(animate);
1037
+ useDrawIn(ringRef, DRAW_IN_DURATION_MS, shouldAnimate);
735
1038
  return /* @__PURE__ */ jsxs(
736
1039
  "span",
737
1040
  {
@@ -764,16 +1067,23 @@ var Radio = forwardRef(function Radio2({
764
1067
  ...rest,
765
1068
  children: [
766
1069
  /* @__PURE__ */ jsx(
767
- RoughSvg,
1070
+ "span",
768
1071
  {
769
- shape: "ellipse",
770
- roughness,
771
- seed,
772
- sketchColor: ink,
773
- bowing,
774
- fillStyle,
775
- strokeWidth: strokeWidth ?? 1.6,
776
- inset: 1.5
1072
+ ref: ringRef,
1073
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1074
+ children: /* @__PURE__ */ jsx(
1075
+ RoughSvg,
1076
+ {
1077
+ shape: "ellipse",
1078
+ roughness,
1079
+ seed,
1080
+ sketchColor: ink,
1081
+ bowing,
1082
+ fillStyle,
1083
+ strokeWidth: strokeWidth ?? 1.6,
1084
+ inset: 1.5
1085
+ }
1086
+ )
777
1087
  }
778
1088
  ),
779
1089
  /* @__PURE__ */ jsx(
@@ -785,17 +1095,12 @@ var Radio = forwardRef(function Radio2({
785
1095
  display: "block"
786
1096
  },
787
1097
  children: /* @__PURE__ */ jsx(
788
- RoughSvg,
1098
+ RadioDot,
789
1099
  {
790
- shape: "ellipse",
791
- roughness: (roughness ?? 1.5) + 0.2,
1100
+ roughness,
792
1101
  seed,
793
- sketchColor: SKETCH_COLORS.accent,
794
- fill: SKETCH_COLORS.accent,
795
- fillStyle: "solid",
796
1102
  bowing,
797
- strokeWidth: 1,
798
- inset: 6
1103
+ shouldAnimate
799
1104
  }
800
1105
  )
801
1106
  }
@@ -829,6 +1134,7 @@ var Card = forwardRef(function Card2({
829
1134
  bowing,
830
1135
  fillStyle,
831
1136
  strokeWidth,
1137
+ animate,
832
1138
  ...rest
833
1139
  }, ref) {
834
1140
  const boxProps = {
@@ -839,7 +1145,8 @@ var Card = forwardRef(function Card2({
839
1145
  sketchColor: sketchColor ?? SKETCH_COLORS.ink,
840
1146
  bowing,
841
1147
  fillStyle,
842
- strokeWidth
1148
+ strokeWidth,
1149
+ animate
843
1150
  };
844
1151
  return /* @__PURE__ */ jsxs(
845
1152
  SketchBox,
@@ -879,6 +1186,7 @@ var Badge = forwardRef(function Badge2({
879
1186
  bowing,
880
1187
  fillStyle,
881
1188
  strokeWidth,
1189
+ animate,
882
1190
  ...rest
883
1191
  }, ref) {
884
1192
  const ink = variant === "accent" ? sketchColor ?? SKETCH_COLORS.accent : sketchColor ?? SKETCH_COLORS.ink;
@@ -906,6 +1214,7 @@ var Badge = forwardRef(function Badge2({
906
1214
  sketchColor: ink,
907
1215
  bowing,
908
1216
  strokeWidth: strokeWidth ?? 1.4,
1217
+ animate,
909
1218
  ...rest,
910
1219
  children: /* @__PURE__ */ jsx("span", { ref, children })
911
1220
  }
@@ -936,6 +1245,7 @@ var Alert = forwardRef(function Alert2({
936
1245
  fillStyle,
937
1246
  strokeWidth,
938
1247
  role = "status",
1248
+ animate,
939
1249
  ...rest
940
1250
  }, ref) {
941
1251
  const color = sketchColor ?? VARIANT_COLOR[variant];
@@ -954,6 +1264,8 @@ var Alert = forwardRef(function Alert2({
954
1264
  fillStyle: fillStyle ?? "hachure",
955
1265
  fill: VARIANT_FILL[variant],
956
1266
  strokeWidth,
1267
+ animate,
1268
+ drawInDuration: DRAW_IN_ALERT_MS,
957
1269
  ...rest,
958
1270
  children: [
959
1271
  title ? /* @__PURE__ */ jsx(
@@ -986,15 +1298,27 @@ function Modal({
986
1298
  sketchColor,
987
1299
  bowing,
988
1300
  fillStyle,
989
- strokeWidth
1301
+ strokeWidth,
1302
+ animate
990
1303
  }) {
991
1304
  const ink = sketchColor ?? SKETCH_COLORS.ink;
992
- return /* @__PURE__ */ jsxs(Dialog.Root, { open, defaultOpen, onOpenChange, children: [
1305
+ const [uncontrolled, setUncontrolled] = useState(defaultOpen === true);
1306
+ const isOpen = open ?? uncontrolled;
1307
+ const shouldAnimate = useAnimate(animate);
1308
+ function handleOpenChange(next) {
1309
+ setUncontrolled(next);
1310
+ onOpenChange?.(next);
1311
+ }
1312
+ return /* @__PURE__ */ jsxs(Dialog.Root, { open: isOpen, onOpenChange: handleOpenChange, children: [
993
1313
  trigger ? /* @__PURE__ */ jsx(Dialog.Trigger, { asChild: true, children: trigger }) : null,
994
- /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
995
- /* @__PURE__ */ jsx(
996
- Dialog.Overlay,
1314
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen ? /* @__PURE__ */ jsxs(Dialog.Portal, { forceMount: true, children: [
1315
+ /* @__PURE__ */ jsx(Dialog.Overlay, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsx(
1316
+ motion.div,
997
1317
  {
1318
+ initial: shouldAnimate ? { opacity: 0 } : false,
1319
+ animate: { opacity: 1 },
1320
+ exit: shouldAnimate ? { opacity: 0 } : void 0,
1321
+ transition: shouldAnimate ? { duration: 0.2, ease: "easeOut" } : { duration: 0 },
998
1322
  style: {
999
1323
  position: "fixed",
1000
1324
  inset: 0,
@@ -1002,92 +1326,121 @@ function Modal({
1002
1326
  zIndex: 70
1003
1327
  }
1004
1328
  }
1005
- ),
1329
+ ) }),
1006
1330
  /* @__PURE__ */ jsx(
1007
- Dialog.Content,
1331
+ "div",
1008
1332
  {
1009
- className: cn(className),
1010
1333
  style: {
1011
1334
  position: "fixed",
1012
- left: "50%",
1013
- top: "50%",
1014
- transform: "translate(-50%, -50%)",
1335
+ inset: 0,
1015
1336
  zIndex: 80,
1016
- width: "min(480px, calc(100vw - 32px))",
1017
- outline: "none"
1337
+ display: "flex",
1338
+ alignItems: "center",
1339
+ justifyContent: "center",
1340
+ pointerEvents: "none",
1341
+ padding: 16
1018
1342
  },
1019
- children: /* @__PURE__ */ jsxs(
1020
- SketchBox,
1343
+ children: /* @__PURE__ */ jsx(Dialog.Content, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsx(
1344
+ motion.div,
1021
1345
  {
1022
- roughness,
1023
- seed,
1024
- sketchColor: ink,
1025
- bowing,
1026
- fillStyle: fillStyle ?? "solid",
1027
- fill: "#f7f6f2",
1028
- strokeWidth: strokeWidth ?? 2,
1029
- shadow: true,
1030
- contentStyle: { padding: "20px 22px 18px" },
1031
- children: [
1032
- /* @__PURE__ */ jsxs(
1033
- "div",
1034
- {
1035
- style: {
1036
- display: "flex",
1037
- alignItems: "flex-start",
1038
- justifyContent: "space-between",
1039
- gap: 12,
1040
- marginBottom: title ? 10 : 0
1041
- },
1042
- children: [
1043
- title ? /* @__PURE__ */ jsx(
1044
- Dialog.Title,
1045
- {
1046
- style: {
1047
- margin: 0,
1048
- fontSize: 18,
1049
- fontWeight: doodleUiFontWeight(700),
1050
- fontFamily: doodleUiFontFamily,
1051
- color: ink
1052
- },
1053
- children: title
1054
- }
1055
- ) : /* @__PURE__ */ jsx(Dialog.Title, { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: "Dialog" }),
1056
- /* @__PURE__ */ jsx(Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsx(
1057
- Button,
1058
- {
1059
- variant: "ghost",
1060
- size: "sm",
1061
- roughness,
1062
- seed,
1063
- sketchColor: ink,
1064
- "aria-label": "Close",
1065
- children: "Close"
1066
- }
1067
- ) })
1068
- ]
1069
- }
1070
- ),
1071
- description ? /* @__PURE__ */ jsx(
1072
- Dialog.Description,
1073
- {
1074
- style: {
1075
- margin: "0 0 14px",
1076
- fontSize: 14,
1077
- lineHeight: 1.5,
1078
- color: ink,
1079
- opacity: 0.8
1080
- },
1081
- children: description
1082
- }
1083
- ) : null,
1084
- /* @__PURE__ */ jsx("div", { children })
1085
- ]
1346
+ className: cn(className),
1347
+ initial: shouldAnimate ? { opacity: 0, scale: 0.96, y: 10 } : false,
1348
+ animate: { opacity: 1, scale: 1, y: 0 },
1349
+ exit: shouldAnimate ? { opacity: 0, scale: 0.96, y: 8 } : void 0,
1350
+ transition: shouldAnimate ? { duration: 0.22, ease: "easeOut" } : { duration: 0 },
1351
+ style: {
1352
+ width: "min(480px, calc(100vw - 32px))",
1353
+ outline: "none",
1354
+ pointerEvents: "auto"
1355
+ },
1356
+ children: /* @__PURE__ */ jsxs(
1357
+ SketchBox,
1358
+ {
1359
+ roughness,
1360
+ seed,
1361
+ sketchColor: ink,
1362
+ bowing,
1363
+ fillStyle: fillStyle ?? "solid",
1364
+ fill: "#f7f6f2",
1365
+ strokeWidth: strokeWidth ?? 2,
1366
+ shadow: true,
1367
+ animate,
1368
+ contentStyle: { padding: "20px 22px 18px" },
1369
+ children: [
1370
+ /* @__PURE__ */ jsxs(
1371
+ "div",
1372
+ {
1373
+ style: {
1374
+ display: "flex",
1375
+ alignItems: "flex-start",
1376
+ justifyContent: "space-between",
1377
+ gap: 12,
1378
+ marginBottom: title ? 10 : 0
1379
+ },
1380
+ children: [
1381
+ title ? /* @__PURE__ */ jsx(
1382
+ Dialog.Title,
1383
+ {
1384
+ style: {
1385
+ margin: 0,
1386
+ fontSize: 18,
1387
+ fontWeight: doodleUiFontWeight(700),
1388
+ fontFamily: doodleUiFontFamily,
1389
+ color: ink
1390
+ },
1391
+ children: title
1392
+ }
1393
+ ) : /* @__PURE__ */ jsx(
1394
+ Dialog.Title,
1395
+ {
1396
+ style: {
1397
+ position: "absolute",
1398
+ width: 1,
1399
+ height: 1,
1400
+ overflow: "hidden",
1401
+ clip: "rect(0 0 0 0)"
1402
+ },
1403
+ children: "Dialog"
1404
+ }
1405
+ ),
1406
+ /* @__PURE__ */ jsx(Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsx(
1407
+ Button,
1408
+ {
1409
+ variant: "ghost",
1410
+ size: "sm",
1411
+ roughness,
1412
+ seed,
1413
+ sketchColor: ink,
1414
+ animate,
1415
+ "aria-label": "Close",
1416
+ children: "Close"
1417
+ }
1418
+ ) })
1419
+ ]
1420
+ }
1421
+ ),
1422
+ description ? /* @__PURE__ */ jsx(
1423
+ Dialog.Description,
1424
+ {
1425
+ style: {
1426
+ margin: "0 0 14px",
1427
+ fontSize: 14,
1428
+ lineHeight: 1.5,
1429
+ color: ink,
1430
+ opacity: 0.8
1431
+ },
1432
+ children: description
1433
+ }
1434
+ ) : null,
1435
+ /* @__PURE__ */ jsx("div", { children })
1436
+ ]
1437
+ }
1438
+ )
1086
1439
  }
1087
- )
1440
+ ) })
1088
1441
  }
1089
1442
  )
1090
- ] })
1443
+ ] }) : null })
1091
1444
  ] });
1092
1445
  }
1093
1446
  var ModalRoot = Dialog.Root;
@@ -1105,13 +1458,20 @@ var Divider = forwardRef(
1105
1458
  sketchColor,
1106
1459
  bowing,
1107
1460
  strokeWidth,
1461
+ animate,
1108
1462
  ...rest
1109
1463
  }, ref) {
1464
+ const rootRef = useRef(null);
1465
+ const shouldAnimate = useAnimate(animate);
1110
1466
  const horizontal = orientation === "horizontal";
1467
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate);
1111
1468
  return /* @__PURE__ */ jsx(
1112
1469
  "div",
1113
1470
  {
1114
- ref,
1471
+ ref: (node) => {
1472
+ rootRef.current = node;
1473
+ assignRef(ref, node);
1474
+ },
1115
1475
  role: "separator",
1116
1476
  "aria-orientation": orientation,
1117
1477
  className: cn(className),
@@ -1151,15 +1511,27 @@ var Progress = forwardRef(
1151
1511
  bowing,
1152
1512
  fillStyle,
1153
1513
  strokeWidth,
1514
+ animate,
1154
1515
  ...rest
1155
1516
  }, ref) {
1517
+ const rootRef = useRef(null);
1518
+ const trackRef = useRef(null);
1156
1519
  const ink = sketchColor ?? SKETCH_COLORS.accent;
1157
1520
  const clamped = Math.min(max, Math.max(0, value));
1158
1521
  const percent = max === 0 ? 0 : clamped / max * 100;
1522
+ const shouldAnimate = useAnimate(animate);
1523
+ const displayed = useTweenNumber(percent, shouldAnimate, 420);
1524
+ const resolvedSeed = useResolvedSeed(seed);
1525
+ const fillSeed = shouldAnimate ? deriveSeed(resolvedSeed, `fill-${Math.round(displayed / 6)}`) : resolvedSeed;
1526
+ const fillRoughness = (roughness ?? 1.5) + 0.4 + displayed / 100 * 0.7;
1527
+ useDrawIn(trackRef, DRAW_IN_DURATION_MS, shouldAnimate);
1159
1528
  return /* @__PURE__ */ jsxs(
1160
1529
  "div",
1161
1530
  {
1162
- ref,
1531
+ ref: (node) => {
1532
+ rootRef.current = node;
1533
+ assignRef(ref, node);
1534
+ },
1163
1535
  role: "progressbar",
1164
1536
  "aria-valuemin": 0,
1165
1537
  "aria-valuemax": max,
@@ -1174,18 +1546,25 @@ var Progress = forwardRef(
1174
1546
  ...rest,
1175
1547
  children: [
1176
1548
  /* @__PURE__ */ jsx(
1177
- RoughSvg,
1549
+ "span",
1178
1550
  {
1179
- shape: "rectangle",
1180
- roughness,
1181
- seed,
1182
- sketchColor: SKETCH_COLORS.ink,
1183
- bowing,
1184
- strokeWidth: strokeWidth ?? 1.5,
1185
- inset: 2
1551
+ ref: trackRef,
1552
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1553
+ children: /* @__PURE__ */ jsx(
1554
+ RoughSvg,
1555
+ {
1556
+ shape: "rectangle",
1557
+ roughness,
1558
+ seed: resolvedSeed,
1559
+ sketchColor: SKETCH_COLORS.ink,
1560
+ bowing,
1561
+ strokeWidth: strokeWidth ?? 1.5,
1562
+ inset: 2
1563
+ }
1564
+ )
1186
1565
  }
1187
1566
  ),
1188
- percent > 0 ? /* @__PURE__ */ jsx(
1567
+ displayed > 0 ? /* @__PURE__ */ jsx(
1189
1568
  "div",
1190
1569
  {
1191
1570
  style: {
@@ -1193,16 +1572,16 @@ var Progress = forwardRef(
1193
1572
  left: 5,
1194
1573
  top: 4,
1195
1574
  bottom: 4,
1196
- width: `calc(${percent}% - 10px)`,
1197
- minWidth: percent > 0 ? 8 : 0,
1575
+ width: `calc(${displayed}% - 10px)`,
1576
+ minWidth: displayed > 0 ? 8 : 0,
1198
1577
  overflow: "hidden"
1199
1578
  },
1200
1579
  children: /* @__PURE__ */ jsx(
1201
1580
  RoughSvg,
1202
1581
  {
1203
1582
  shape: "rectangle",
1204
- roughness: (roughness ?? 1.5) + 0.55,
1205
- seed,
1583
+ roughness: fillRoughness,
1584
+ seed: fillSeed,
1206
1585
  sketchColor: ink,
1207
1586
  fill: ink,
1208
1587
  fillStyle: fillStyle ?? "hachure",
@@ -1230,7 +1609,8 @@ function Tooltip({
1230
1609
  sketchColor,
1231
1610
  bowing,
1232
1611
  fillStyle,
1233
- strokeWidth
1612
+ strokeWidth,
1613
+ animate
1234
1614
  }) {
1235
1615
  const ink = sketchColor ?? SKETCH_COLORS.ink;
1236
1616
  return /* @__PURE__ */ jsxs(TooltipPrimitive.Root, { delayDuration, children: [
@@ -1252,6 +1632,8 @@ function Tooltip({
1252
1632
  fillStyle: fillStyle ?? "solid",
1253
1633
  fill: "#f7f6f2",
1254
1634
  strokeWidth: strokeWidth ?? 1.4,
1635
+ animate,
1636
+ drawInDuration: DRAW_IN_TOOLTIP_MS,
1255
1637
  contentStyle: {
1256
1638
  padding: "6px 10px",
1257
1639
  fontSize: 13,
@@ -1293,6 +1675,7 @@ function Select({
1293
1675
  value,
1294
1676
  defaultValue,
1295
1677
  onValueChange,
1678
+ animate,
1296
1679
  "aria-label": ariaLabel,
1297
1680
  ...rest
1298
1681
  }) {
@@ -1315,7 +1698,8 @@ function Select({
1315
1698
  ink,
1316
1699
  selectedValue,
1317
1700
  selectedLabel,
1318
- placeholder
1701
+ placeholder,
1702
+ animate
1319
1703
  },
1320
1704
  children: /* @__PURE__ */ jsxs(
1321
1705
  SelectPrimitive.Root,
@@ -1356,10 +1740,16 @@ var SelectTrigger = forwardRef(
1356
1740
  function SelectTrigger2({ className, style, placeholder, children, ...rest }, ref) {
1357
1741
  const sketch = useSelectSketch();
1358
1742
  const [openish, setOpenish] = useState(false);
1743
+ const rootRef = useRef(null);
1744
+ const shouldAnimate = useAnimate(sketch.animate);
1745
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate);
1359
1746
  return /* @__PURE__ */ jsxs(
1360
1747
  SelectPrimitive.Trigger,
1361
1748
  {
1362
- ref,
1749
+ ref: (node) => {
1750
+ rootRef.current = node;
1751
+ assignRef(ref, node);
1752
+ },
1363
1753
  className: cn(className),
1364
1754
  onPointerDown: () => setOpenish(true),
1365
1755
  onBlur: () => setOpenish(false),
@@ -1468,6 +1858,7 @@ var SelectContent = forwardRef(
1468
1858
  fillStyle: sketch.fillStyle ?? "solid",
1469
1859
  fill: "#f7f6f2",
1470
1860
  strokeWidth: sketch.strokeWidth ?? 1.5,
1861
+ animate: sketch.animate,
1471
1862
  contentStyle: { padding: "6px 4px" },
1472
1863
  children: /* @__PURE__ */ jsx(SelectPrimitive.Viewport, { children })
1473
1864
  }
@@ -1558,12 +1949,17 @@ var Switch = forwardRef(
1558
1949
  defaultChecked,
1559
1950
  onCheckedChange,
1560
1951
  id,
1952
+ animate,
1561
1953
  ...rest
1562
1954
  }, ref) {
1563
1955
  const [uncontrolled, setUncontrolled] = useState(defaultChecked === true);
1564
1956
  const isOn = checked ?? uncontrolled;
1565
1957
  const ink = sketchColor ?? SKETCH_COLORS.ink;
1566
1958
  const resolvedSeed = useResolvedSeed(seed);
1959
+ const shouldAnimate = useAnimate(animate);
1960
+ const trackRef = useRef(null);
1961
+ const trackSeed = shouldAnimate ? deriveSeed(resolvedSeed, isOn ? "on" : "off") : resolvedSeed;
1962
+ useDrawIn(trackRef, DRAW_IN_DURATION_MS, shouldAnimate, trackSeed);
1567
1963
  return /* @__PURE__ */ jsxs(
1568
1964
  "span",
1569
1965
  {
@@ -1602,22 +1998,32 @@ var Switch = forwardRef(
1602
1998
  ...rest,
1603
1999
  children: [
1604
2000
  /* @__PURE__ */ jsx(
1605
- RoughSvg,
2001
+ "span",
1606
2002
  {
1607
- shape: "rectangle",
1608
- roughness,
1609
- seed: resolvedSeed,
1610
- sketchColor: isOn ? SKETCH_COLORS.accent : ink,
1611
- bowing: bowing ?? 1.4,
1612
- fillStyle: fillStyle ?? (isOn ? "hachure" : void 0),
1613
- fill: isOn ? SKETCH_COLORS.accentFill : void 0,
1614
- strokeWidth: strokeWidth ?? 1.6,
1615
- inset: 1.5
2003
+ ref: trackRef,
2004
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
2005
+ children: /* @__PURE__ */ jsx(
2006
+ RoughSvg,
2007
+ {
2008
+ shape: "rectangle",
2009
+ roughness,
2010
+ seed: trackSeed,
2011
+ sketchColor: isOn ? SKETCH_COLORS.accent : ink,
2012
+ bowing: bowing ?? 1.4,
2013
+ fillStyle: fillStyle ?? (isOn ? "hachure" : void 0),
2014
+ fill: isOn ? SKETCH_COLORS.accentFill : void 0,
2015
+ strokeWidth: strokeWidth ?? 1.6,
2016
+ inset: 1.5
2017
+ }
2018
+ )
1616
2019
  }
1617
2020
  ),
1618
- /* @__PURE__ */ jsx(
1619
- SwitchPrimitive.Thumb,
2021
+ /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, { asChild: true, children: /* @__PURE__ */ jsx(
2022
+ motion.span,
1620
2023
  {
2024
+ initial: false,
2025
+ animate: { x: isOn ? THUMB_ON : THUMB_OFF },
2026
+ transition: shouldAnimate ? { type: "spring", stiffness: 420, damping: 30 } : { duration: 0 },
1621
2027
  style: {
1622
2028
  position: "absolute",
1623
2029
  top: (TRACK_H - THUMB) / 2,
@@ -1625,8 +2031,6 @@ var Switch = forwardRef(
1625
2031
  width: THUMB,
1626
2032
  height: THUMB,
1627
2033
  display: "block",
1628
- transform: `translateX(${isOn ? THUMB_ON : THUMB_OFF}px)`,
1629
- transition: "transform 160ms ease",
1630
2034
  zIndex: 1
1631
2035
  },
1632
2036
  children: /* @__PURE__ */ jsx(
@@ -1644,7 +2048,7 @@ var Switch = forwardRef(
1644
2048
  }
1645
2049
  )
1646
2050
  }
1647
- )
2051
+ ) })
1648
2052
  ]
1649
2053
  }
1650
2054
  ),
@@ -1686,12 +2090,14 @@ var Tabs = forwardRef(function Tabs2({
1686
2090
  bowing,
1687
2091
  fillStyle,
1688
2092
  strokeWidth,
2093
+ animate,
1689
2094
  ...rest
1690
2095
  }, ref) {
1691
2096
  const [uncontrolled, setUncontrolled] = useState(defaultValue);
1692
2097
  const current = value ?? uncontrolled;
1693
2098
  const resolvedSeed = useResolvedSeed(seed);
1694
2099
  const ink = sketchColor ?? SKETCH_COLORS.ink;
2100
+ const shouldAnimate = useAnimate(animate);
1695
2101
  return /* @__PURE__ */ jsx(
1696
2102
  TabsSketchContext.Provider,
1697
2103
  {
@@ -1704,7 +2110,8 @@ var Tabs = forwardRef(function Tabs2({
1704
2110
  strokeWidth,
1705
2111
  resolvedSeed,
1706
2112
  ink,
1707
- current
2113
+ current,
2114
+ shouldAnimate
1708
2115
  },
1709
2116
  children: /* @__PURE__ */ jsx(
1710
2117
  TabsPrimitive.Root,
@@ -1725,21 +2132,94 @@ var Tabs = forwardRef(function Tabs2({
1725
2132
  }
1726
2133
  );
1727
2134
  });
2135
+ function TabUnderline({
2136
+ box,
2137
+ seed,
2138
+ sketch
2139
+ }) {
2140
+ const ref = useRef(null);
2141
+ useDrawIn(ref, DRAW_IN_MARK_MS, sketch.shouldAnimate, seed);
2142
+ return /* @__PURE__ */ jsx(
2143
+ "span",
2144
+ {
2145
+ ref,
2146
+ "aria-hidden": "true",
2147
+ style: {
2148
+ position: "absolute",
2149
+ left: box.left,
2150
+ top: box.top,
2151
+ width: box.width,
2152
+ height: 10,
2153
+ pointerEvents: "none",
2154
+ transition: sketch.shouldAnimate ? "left 220ms ease, width 220ms ease, top 220ms ease" : void 0
2155
+ },
2156
+ children: /* @__PURE__ */ jsx(
2157
+ RoughSvg,
2158
+ {
2159
+ shape: "line",
2160
+ roughness: (sketch.roughness ?? 1.5) + 0.35,
2161
+ seed,
2162
+ sketchColor: SKETCH_COLORS.accent,
2163
+ bowing: sketch.bowing ?? 1.8,
2164
+ strokeWidth: (sketch.strokeWidth ?? 1.75) + 0.4,
2165
+ inset: 2
2166
+ }
2167
+ )
2168
+ }
2169
+ );
2170
+ }
1728
2171
  var TabList = forwardRef(
1729
2172
  function TabList2({ className, style, children, ...rest }, ref) {
1730
- return /* @__PURE__ */ jsx(
2173
+ const sketch = useTabsSketch();
2174
+ const listRef = useRef(null);
2175
+ const [underline, setUnderline] = useState(null);
2176
+ useLayoutEffect(() => {
2177
+ const list = listRef.current;
2178
+ if (!list) return;
2179
+ const measure = () => {
2180
+ const active = list.querySelector('[data-state="active"]');
2181
+ if (!active) {
2182
+ setUnderline(null);
2183
+ return;
2184
+ }
2185
+ setUnderline({
2186
+ left: active.offsetLeft + 8,
2187
+ top: active.offsetTop + active.offsetHeight - 10,
2188
+ width: Math.max(12, active.offsetWidth - 16)
2189
+ });
2190
+ };
2191
+ measure();
2192
+ const observer = new ResizeObserver(measure);
2193
+ observer.observe(list);
2194
+ return () => observer.disconnect();
2195
+ }, [sketch.current, children]);
2196
+ return /* @__PURE__ */ jsxs(
1731
2197
  TabsPrimitive.List,
1732
2198
  {
1733
- ref,
2199
+ ref: (node) => {
2200
+ listRef.current = node;
2201
+ assignRef(ref, node);
2202
+ },
1734
2203
  className: cn(className),
1735
2204
  style: {
1736
2205
  display: "flex",
1737
2206
  flexWrap: "wrap",
1738
2207
  gap: 4,
2208
+ position: "relative",
1739
2209
  ...style
1740
2210
  },
1741
2211
  ...rest,
1742
- children
2212
+ children: [
2213
+ children,
2214
+ underline && sketch.current ? /* @__PURE__ */ jsx(
2215
+ TabUnderline,
2216
+ {
2217
+ box: underline,
2218
+ seed: deriveSeed(sketch.resolvedSeed, sketch.current),
2219
+ sketch
2220
+ }
2221
+ ) : null
2222
+ ]
1743
2223
  }
1744
2224
  );
1745
2225
  }
@@ -1747,7 +2227,7 @@ var TabList = forwardRef(
1747
2227
  var Tab = forwardRef(function Tab2({ className, style, children, value, ...rest }, ref) {
1748
2228
  const sketch = useTabsSketch();
1749
2229
  const active = sketch.current === value;
1750
- return /* @__PURE__ */ jsxs(
2230
+ return /* @__PURE__ */ jsx(
1751
2231
  TabsPrimitive.Trigger,
1752
2232
  {
1753
2233
  ref,
@@ -1767,34 +2247,7 @@ var Tab = forwardRef(function Tab2({ className, style, children, value, ...rest
1767
2247
  ...style
1768
2248
  },
1769
2249
  ...rest,
1770
- children: [
1771
- /* @__PURE__ */ jsx("span", { style: { position: "relative", zIndex: 1 }, children }),
1772
- active ? /* @__PURE__ */ jsx(
1773
- "span",
1774
- {
1775
- style: {
1776
- position: "absolute",
1777
- left: 8,
1778
- right: 8,
1779
- bottom: 2,
1780
- height: 10,
1781
- pointerEvents: "none"
1782
- },
1783
- children: /* @__PURE__ */ jsx(
1784
- RoughSvg,
1785
- {
1786
- shape: "line",
1787
- roughness: (sketch.roughness ?? 1.5) + 0.35,
1788
- seed: deriveSeed(sketch.resolvedSeed, value),
1789
- sketchColor: SKETCH_COLORS.accent,
1790
- bowing: sketch.bowing ?? 1.8,
1791
- strokeWidth: (sketch.strokeWidth ?? 1.75) + 0.4,
1792
- inset: 2
1793
- }
1794
- )
1795
- }
1796
- ) : null
1797
- ]
2250
+ children: /* @__PURE__ */ jsx("span", { style: { position: "relative", zIndex: 1 }, children })
1798
2251
  }
1799
2252
  );
1800
2253
  });
@@ -1829,6 +2282,46 @@ function useTableSketch() {
1829
2282
  }
1830
2283
  return ctx;
1831
2284
  }
2285
+ function TableRule({
2286
+ line,
2287
+ headerUnderline,
2288
+ roughness,
2289
+ resolvedSeed,
2290
+ ink,
2291
+ bowing,
2292
+ strokeWidth,
2293
+ shouldAnimate
2294
+ }) {
2295
+ const ref = useRef(null);
2296
+ const delay = shouldAnimate ? Math.min(line.index, 6) * TABLE_STAGGER_MS : 0;
2297
+ useDrawIn(ref, DRAW_IN_DURATION_MS, shouldAnimate, void 0, delay);
2298
+ if (line.header && !headerUnderline) return null;
2299
+ return /* @__PURE__ */ jsx(
2300
+ "div",
2301
+ {
2302
+ ref,
2303
+ style: {
2304
+ position: "absolute",
2305
+ left: 0,
2306
+ width: line.width,
2307
+ top: line.y - 6,
2308
+ height: 12
2309
+ },
2310
+ children: /* @__PURE__ */ jsx(
2311
+ RoughSvg,
2312
+ {
2313
+ shape: "line",
2314
+ roughness: line.header ? (roughness ?? 1.5) + 0.2 : roughness ?? 1.7,
2315
+ seed: deriveSeed(resolvedSeed, line.key),
2316
+ sketchColor: ink,
2317
+ bowing: bowing ?? (line.header ? 1.4 : 2),
2318
+ strokeWidth: line.header ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth ?? 1.35,
2319
+ inset: 6
2320
+ }
2321
+ )
2322
+ }
2323
+ );
2324
+ }
1832
2325
  var Table = forwardRef(function Table2({
1833
2326
  className,
1834
2327
  style,
@@ -1840,6 +2333,7 @@ var Table = forwardRef(function Table2({
1840
2333
  bowing,
1841
2334
  fillStyle,
1842
2335
  strokeWidth,
2336
+ animate,
1843
2337
  ...rest
1844
2338
  }, ref) {
1845
2339
  const wrapperRef = useRef(null);
@@ -1847,6 +2341,7 @@ var Table = forwardRef(function Table2({
1847
2341
  const [lines, setLines] = useState([]);
1848
2342
  const resolvedSeed = useResolvedSeed(seed);
1849
2343
  const ink = sketchColor ?? SKETCH_COLORS.ink;
2344
+ const shouldAnimate = useAnimate(animate);
1850
2345
  useLayoutEffect(() => {
1851
2346
  const wrapper = wrapperRef.current;
1852
2347
  const table = tableRef.current;
@@ -1862,7 +2357,8 @@ var Table = forwardRef(function Table2({
1862
2357
  y: row.offsetTop + row.offsetHeight,
1863
2358
  width: table.offsetWidth,
1864
2359
  key: isHeader ? `header-${index}` : `row-${index}`,
1865
- header: Boolean(isHeader)
2360
+ header: Boolean(isHeader),
2361
+ index
1866
2362
  });
1867
2363
  });
1868
2364
  setLines(next);
@@ -1884,7 +2380,8 @@ var Table = forwardRef(function Table2({
1884
2380
  fillStyle,
1885
2381
  strokeWidth,
1886
2382
  resolvedSeed,
1887
- ink
2383
+ ink,
2384
+ shouldAnimate
1888
2385
  },
1889
2386
  children: /* @__PURE__ */ jsxs(
1890
2387
  "div",
@@ -1904,34 +2401,20 @@ var Table = forwardRef(function Table2({
1904
2401
  zIndex: 1,
1905
2402
  overflow: "visible"
1906
2403
  },
1907
- children: lines.map((line) => {
1908
- if (line.header && !headerUnderline) return null;
1909
- return /* @__PURE__ */ jsx(
1910
- "div",
1911
- {
1912
- style: {
1913
- position: "absolute",
1914
- left: 0,
1915
- width: line.width,
1916
- top: line.y - 6,
1917
- height: 12
1918
- },
1919
- children: /* @__PURE__ */ jsx(
1920
- RoughSvg,
1921
- {
1922
- shape: "line",
1923
- roughness: line.header ? (roughness ?? 1.5) + 0.2 : roughness ?? 1.7,
1924
- seed: deriveSeed(resolvedSeed, line.key),
1925
- sketchColor: ink,
1926
- bowing: bowing ?? (line.header ? 1.4 : 2),
1927
- strokeWidth: line.header ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth ?? 1.35,
1928
- inset: 6
1929
- }
1930
- )
1931
- },
1932
- line.key
1933
- );
1934
- })
2404
+ children: lines.map((line) => /* @__PURE__ */ jsx(
2405
+ TableRule,
2406
+ {
2407
+ line,
2408
+ headerUnderline,
2409
+ roughness,
2410
+ resolvedSeed,
2411
+ ink,
2412
+ bowing,
2413
+ strokeWidth,
2414
+ shouldAnimate
2415
+ },
2416
+ line.key
2417
+ ))
1935
2418
  }
1936
2419
  ),
1937
2420
  /* @__PURE__ */ jsx(
@@ -1939,8 +2422,7 @@ var Table = forwardRef(function Table2({
1939
2422
  {
1940
2423
  ref: (node) => {
1941
2424
  tableRef.current = node;
1942
- if (typeof ref === "function") ref(node);
1943
- else if (ref) ref.current = node;
2425
+ assignRef(ref, node);
1944
2426
  },
1945
2427
  style: {
1946
2428
  width: "100%",
@@ -2102,84 +2584,89 @@ function Toast({
2102
2584
  sketchColor,
2103
2585
  bowing,
2104
2586
  fillStyle,
2105
- strokeWidth
2587
+ strokeWidth,
2588
+ animate
2106
2589
  }) {
2107
2590
  const position = useContext(ToastPositionContext);
2108
2591
  const fromTop = position.startsWith("top");
2109
- const [entered, setEntered] = useState(false);
2592
+ const [uncontrolled, setUncontrolled] = useState(defaultOpen ?? false);
2593
+ const isOpen = open ?? uncontrolled;
2594
+ const shouldAnimate = useAnimate(animate);
2110
2595
  const color = sketchColor ?? VARIANT_COLOR2[variant];
2111
- const visible = open ?? true;
2112
- useLayoutEffect(() => {
2113
- if (!visible) {
2114
- setEntered(false);
2115
- return;
2116
- }
2117
- setEntered(false);
2118
- const frame = requestAnimationFrame(() => {
2119
- requestAnimationFrame(() => setEntered(true));
2120
- });
2121
- return () => cancelAnimationFrame(frame);
2122
- }, [visible]);
2123
- return /* @__PURE__ */ jsx(
2596
+ const offset = fromTop ? -14 : 14;
2597
+ function handleOpenChange(next) {
2598
+ setUncontrolled(next);
2599
+ onOpenChange?.(next);
2600
+ }
2601
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen ? /* @__PURE__ */ jsx(
2124
2602
  ToastPrimitive.Root,
2125
2603
  {
2126
- open,
2127
- defaultOpen,
2128
- onOpenChange,
2604
+ open: isOpen,
2605
+ onOpenChange: handleOpenChange,
2129
2606
  duration,
2130
- className: cn(className),
2131
- style: {
2132
- transform: entered ? "translateY(0)" : `translateY(${fromTop ? "-10px" : "10px"})`,
2133
- opacity: entered ? 1 : 0,
2134
- transition: "transform 200ms ease, opacity 200ms ease",
2135
- outline: "none",
2136
- ...style
2137
- },
2138
- children: /* @__PURE__ */ jsxs(
2139
- SketchBox,
2607
+ forceMount: true,
2608
+ asChild: true,
2609
+ children: /* @__PURE__ */ jsx(
2610
+ motion.li,
2140
2611
  {
2141
- roughness,
2142
- seed,
2143
- sketchColor: color,
2144
- bowing,
2145
- fillStyle: fillStyle ?? "hachure",
2146
- fill: VARIANT_FILL2[variant],
2147
- strokeWidth: strokeWidth ?? 1.7,
2148
- shadow: true,
2149
- contentStyle: { padding: "12px 16px" },
2150
- children: [
2151
- title ? /* @__PURE__ */ jsx(
2152
- ToastPrimitive.Title,
2153
- {
2154
- style: {
2155
- margin: 0,
2156
- fontWeight: doodleUiFontWeight(700),
2157
- fontSize: 15,
2158
- fontFamily: doodleUiFontFamily,
2159
- color,
2160
- marginBottom: children ? 4 : 0
2161
- },
2162
- children: title
2163
- }
2164
- ) : null,
2165
- children ? /* @__PURE__ */ jsx(
2166
- ToastPrimitive.Description,
2167
- {
2168
- style: {
2169
- margin: 0,
2170
- fontSize: 14,
2171
- lineHeight: 1.5,
2172
- fontFamily: doodleUiFontFamily,
2173
- color: SKETCH_COLORS.ink
2174
- },
2175
- children
2176
- }
2177
- ) : null
2178
- ]
2612
+ className: cn(className),
2613
+ initial: shouldAnimate ? { y: offset, opacity: 0, rotate: -1.4 } : false,
2614
+ animate: shouldAnimate ? { y: 0, opacity: 1, rotate: [-1.2, 0.9, -0.35, 0] } : { y: 0, opacity: 1, rotate: 0 },
2615
+ exit: shouldAnimate ? { y: offset, opacity: 0, rotate: 1.2 } : { opacity: 0 },
2616
+ transition: shouldAnimate ? { duration: 0.38, ease: "easeOut" } : { duration: 0 },
2617
+ style: {
2618
+ listStyle: "none",
2619
+ outline: "none",
2620
+ ...style
2621
+ },
2622
+ children: /* @__PURE__ */ jsxs(
2623
+ SketchBox,
2624
+ {
2625
+ roughness,
2626
+ seed,
2627
+ sketchColor: color,
2628
+ bowing,
2629
+ fillStyle: fillStyle ?? "hachure",
2630
+ fill: VARIANT_FILL2[variant],
2631
+ strokeWidth: strokeWidth ?? 1.7,
2632
+ shadow: true,
2633
+ animate,
2634
+ contentStyle: { padding: "12px 16px" },
2635
+ children: [
2636
+ title ? /* @__PURE__ */ jsx(
2637
+ ToastPrimitive.Title,
2638
+ {
2639
+ style: {
2640
+ margin: 0,
2641
+ fontWeight: doodleUiFontWeight(700),
2642
+ fontSize: 15,
2643
+ fontFamily: doodleUiFontFamily,
2644
+ color,
2645
+ marginBottom: children ? 4 : 0
2646
+ },
2647
+ children: title
2648
+ }
2649
+ ) : null,
2650
+ children ? /* @__PURE__ */ jsx(
2651
+ ToastPrimitive.Description,
2652
+ {
2653
+ style: {
2654
+ margin: 0,
2655
+ fontSize: 14,
2656
+ lineHeight: 1.5,
2657
+ fontFamily: doodleUiFontFamily,
2658
+ color: SKETCH_COLORS.ink
2659
+ },
2660
+ children
2661
+ }
2662
+ ) : null
2663
+ ]
2664
+ }
2665
+ )
2179
2666
  }
2180
2667
  )
2181
2668
  }
2182
- );
2669
+ ) : null });
2183
2670
  }
2184
2671
  var ToastRoot = ToastPrimitive.Root;
2185
2672
  var ToastTitle = ToastPrimitive.Title;
@@ -3211,6 +3698,6 @@ var Stepper = forwardRef(
3211
3698
  }
3212
3699
  );
3213
3700
 
3214
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, Avatar, Badge, Breadcrumb, BreadcrumbItem, Button, Card, Checkbox, DEFAULT_BOWING, DEFAULT_INK, DEFAULT_ROUGHNESS, DEFAULT_STROKE_WIDTH, Divider, Input, Modal, ModalClose, ModalDescription, ModalRoot, ModalTitle, ModalTrigger, Pagination, Progress, Radio, RadioGroup, RoughSvg, SKETCH_COLORS, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectSeparator, SelectTrigger, SelectValue, Skeleton, SketchBox, SketchSeedProvider, Slider, Stepper, Switch, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Tabs, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastRoot, ToastTitle, ToastViewport, Tooltip, TooltipProvider, deriveSeed, toRoughOptions, useResolvedSeed, useSketchSeed };
3701
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, Avatar, Badge, Breadcrumb, BreadcrumbItem, Button, Card, Checkbox, DEFAULT_BOWING, DEFAULT_INK, DEFAULT_ROUGHNESS, DEFAULT_STROKE_WIDTH, DRAW_IN_ALERT_MS, DRAW_IN_DURATION_MS, DRAW_IN_MARK_MS, DRAW_IN_TOOLTIP_MS, Divider, DoodleUIProvider, Input, Modal, ModalClose, ModalDescription, ModalRoot, ModalTitle, ModalTrigger, Pagination, Progress, Radio, RadioGroup, RoughSvg, SKETCH_COLORS, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectSeparator, SelectTrigger, SelectValue, Skeleton, SketchBox, SketchSeedProvider, Slider, Stepper, Switch, TABLE_STAGGER_MS, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Tabs, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastRoot, ToastTitle, ToastViewport, Tooltip, TooltipProvider, deriveSeed, toRoughOptions, useAnimate, useDoodleUI, useDrawIn, useResolvedSeed, useSketchSeed };
3215
3702
  //# sourceMappingURL=index.js.map
3216
3703
  //# sourceMappingURL=index.js.map