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.cjs CHANGED
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
8
8
  var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
9
9
  var Dialog = require('@radix-ui/react-dialog');
10
+ var framerMotion = require('framer-motion');
10
11
  var TooltipPrimitive = require('@radix-ui/react-tooltip');
11
12
  var SelectPrimitive = require('@radix-ui/react-select');
12
13
  var SwitchPrimitive = require('@radix-ui/react-switch');
@@ -83,6 +84,11 @@ var SKETCH_COLORS = {
83
84
  };
84
85
 
85
86
  // src/utils.ts
87
+ function assignRef(ref, value) {
88
+ if (!ref) return;
89
+ if (typeof ref === "function") ref(value);
90
+ else ref.current = value;
91
+ }
86
92
  function randomSeed() {
87
93
  return Math.floor(Math.random() * 2 ** 31);
88
94
  }
@@ -283,6 +289,177 @@ function RoughSvg({
283
289
  }
284
290
  );
285
291
  }
292
+ var DoodleUIContext = react.createContext(null);
293
+ function DoodleUIProvider({
294
+ children,
295
+ animate = true,
296
+ forceAnimate = false
297
+ }) {
298
+ const value = react.useMemo(
299
+ () => ({ animate, forceAnimate }),
300
+ [animate, forceAnimate]
301
+ );
302
+ return /* @__PURE__ */ jsxRuntime.jsx(DoodleUIContext.Provider, { value, children });
303
+ }
304
+ function useDoodleUI() {
305
+ return react.useContext(DoodleUIContext) ?? { animate: true, forceAnimate: false };
306
+ }
307
+
308
+ // src/animations/resolveAnimate.ts
309
+ function resolveAnimate({
310
+ animate,
311
+ providerAnimate = true,
312
+ forceAnimate = false,
313
+ prefersReducedMotion = false
314
+ }) {
315
+ if (prefersReducedMotion && !forceAnimate) return false;
316
+ if (animate !== void 0) return animate;
317
+ return providerAnimate;
318
+ }
319
+ function subscribe(onStoreChange) {
320
+ const media = window.matchMedia("(prefers-reduced-motion: reduce)");
321
+ media.addEventListener("change", onStoreChange);
322
+ return () => media.removeEventListener("change", onStoreChange);
323
+ }
324
+ function getSnapshot() {
325
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
326
+ }
327
+ function getServerSnapshot() {
328
+ return false;
329
+ }
330
+ function usePrefersReducedMotion() {
331
+ return react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
332
+ }
333
+
334
+ // src/animations/useAnimate.ts
335
+ function useAnimate(animate) {
336
+ const { animate: providerAnimate, forceAnimate } = useDoodleUI();
337
+ const prefersReducedMotion = usePrefersReducedMotion();
338
+ return resolveAnimate({
339
+ animate,
340
+ providerAnimate,
341
+ forceAnimate,
342
+ prefersReducedMotion
343
+ });
344
+ }
345
+ function useTweenNumber(target, enabled, duration = 400) {
346
+ const [value, setValue] = react.useState(target);
347
+ const valueRef = react.useRef(target);
348
+ valueRef.current = value;
349
+ react.useEffect(() => {
350
+ if (!enabled) {
351
+ valueRef.current = target;
352
+ setValue(target);
353
+ return;
354
+ }
355
+ const from = valueRef.current;
356
+ if (from === target) return;
357
+ const start = performance.now();
358
+ let frame = 0;
359
+ const tick = (now) => {
360
+ const t = Math.min(1, (now - start) / duration);
361
+ const eased = 1 - (1 - t) ** 3;
362
+ const next = from + (target - from) * eased;
363
+ valueRef.current = next;
364
+ setValue(next);
365
+ if (t < 1) frame = requestAnimationFrame(tick);
366
+ };
367
+ frame = requestAnimationFrame(tick);
368
+ return () => cancelAnimationFrame(frame);
369
+ }, [target, enabled, duration]);
370
+ return enabled ? value : target;
371
+ }
372
+ var DRAW_IN_DURATION_MS = 400;
373
+ var DRAW_IN_ALERT_MS = 200;
374
+ var DRAW_IN_TOOLTIP_MS = 180;
375
+ var DRAW_IN_MARK_MS = 240;
376
+ var TABLE_STAGGER_MS = 40;
377
+ var DRAW_IN_EASING = "ease-out";
378
+ var running = /* @__PURE__ */ new WeakMap();
379
+ function collectStrokePaths(root) {
380
+ const groups = root.querySelectorAll("g");
381
+ if (groups.length > 0) {
382
+ const paths = [];
383
+ groups.forEach((group) => {
384
+ const children = group.querySelectorAll(":scope > path");
385
+ if (children.length === 0) return;
386
+ paths.push(children[children.length - 1]);
387
+ });
388
+ return paths;
389
+ }
390
+ return Array.from(root.querySelectorAll("path"));
391
+ }
392
+ function getStrokePaths(target) {
393
+ if (target instanceof SVGPathElement) return [target];
394
+ if (target instanceof SVGSVGElement) return collectStrokePaths(target);
395
+ const svgs = target.querySelectorAll("svg");
396
+ if (svgs.length > 0) {
397
+ return Array.from(svgs).flatMap((svg) => collectStrokePaths(svg));
398
+ }
399
+ return collectStrokePaths(target);
400
+ }
401
+ function clearDash(path) {
402
+ path.style.strokeDasharray = "";
403
+ path.style.strokeDashoffset = "";
404
+ }
405
+ function cancelDrawIn(target) {
406
+ if (!target) return;
407
+ const animations = running.get(target);
408
+ animations?.forEach((animation) => animation.cancel());
409
+ running.delete(target);
410
+ getStrokePaths(target).forEach(clearDash);
411
+ }
412
+ function drawIn(target, duration = DRAW_IN_DURATION_MS, delay = 0) {
413
+ cancelDrawIn(target);
414
+ const paths = getStrokePaths(target);
415
+ const animations = [];
416
+ for (const path of paths) {
417
+ let length = 0;
418
+ try {
419
+ length = path.getTotalLength();
420
+ } catch {
421
+ continue;
422
+ }
423
+ if (length <= 0) continue;
424
+ path.style.strokeDasharray = `${length}`;
425
+ path.style.strokeDashoffset = `${length}`;
426
+ const animation = path.animate(
427
+ [{ strokeDashoffset: String(length) }, { strokeDashoffset: "0" }],
428
+ { duration, delay, easing: DRAW_IN_EASING, fill: "forwards" }
429
+ );
430
+ animation.finished.then(() => {
431
+ clearDash(path);
432
+ }).catch(() => {
433
+ });
434
+ animations.push(animation);
435
+ }
436
+ running.set(target, animations);
437
+ }
438
+ function useDrawIn(pathRef, duration = DRAW_IN_DURATION_MS, enabled = true, replayKey, delay = 0) {
439
+ react.useLayoutEffect(() => {
440
+ const target = pathRef.current;
441
+ if (!target) return;
442
+ if (!enabled) {
443
+ cancelDrawIn(target);
444
+ return;
445
+ }
446
+ const apply = () => drawIn(target, duration, delay);
447
+ if (getStrokePaths(target).length > 0) {
448
+ apply();
449
+ return () => cancelDrawIn(target);
450
+ }
451
+ const observer = new MutationObserver(() => {
452
+ if (getStrokePaths(target).length === 0) return;
453
+ observer.disconnect();
454
+ apply();
455
+ });
456
+ observer.observe(target, { childList: true, subtree: true });
457
+ return () => {
458
+ observer.disconnect();
459
+ cancelDrawIn(target);
460
+ };
461
+ }, [pathRef, duration, enabled, replayKey, delay]);
462
+ }
286
463
  var SketchBox = react.forwardRef(
287
464
  function SketchBox2({
288
465
  children,
@@ -301,12 +478,21 @@ var SketchBox = react.forwardRef(
301
478
  strokeWidth,
302
479
  inset,
303
480
  path,
481
+ animate,
482
+ drawInDuration = DRAW_IN_DURATION_MS,
483
+ drawInKey,
304
484
  ...rest
305
485
  }, ref) {
486
+ const rootRef = react.useRef(null);
487
+ const shouldAnimate = useAnimate(animate);
488
+ useDrawIn(rootRef, drawInDuration, shouldAnimate, drawInKey);
306
489
  return /* @__PURE__ */ jsxRuntime.jsxs(
307
490
  "div",
308
491
  {
309
- ref,
492
+ ref: (node) => {
493
+ rootRef.current = node;
494
+ assignRef(ref, node);
495
+ },
310
496
  className: cn(className),
311
497
  style: { position: "relative", ...style },
312
498
  ...rest,
@@ -364,6 +550,10 @@ var SIZE_STYLES = {
364
550
  md: { fontSize: 15, padding: "8px 16px", minHeight: 38 },
365
551
  lg: { fontSize: 17, padding: "11px 22px", minHeight: 46 }
366
552
  };
553
+ function assignRef2(ref, value) {
554
+ if (typeof ref === "function") ref(value);
555
+ else if (ref) ref.current = value;
556
+ }
367
557
  var Button = react.forwardRef(
368
558
  function Button2({
369
559
  children,
@@ -378,18 +568,28 @@ var Button = react.forwardRef(
378
568
  fillStyle,
379
569
  strokeWidth,
380
570
  disabled,
571
+ animate,
381
572
  onMouseEnter,
382
573
  onMouseLeave,
383
574
  ...rest
384
575
  }, ref) {
576
+ const rootRef = react.useRef(null);
385
577
  const [hovered, setHovered] = react.useState(false);
578
+ const shouldAnimate = useAnimate(animate);
579
+ const resolvedSeed = useResolvedSeed(seed);
580
+ const hoverSeed = deriveSeed(resolvedSeed, "hover");
581
+ const sketchSeed = shouldAnimate && hovered && !disabled ? hoverSeed : resolvedSeed;
386
582
  const ink = sketchColor ?? SKETCH_COLORS.ink;
387
583
  const fill = variant === "primary" ? SKETCH_COLORS.accentFill : variant === "secondary" ? SKETCH_COLORS.secondaryFill : void 0;
388
584
  const stroke = variant === "ghost" && !hovered ? "transparent" : variant === "primary" ? sketchColor ?? SKETCH_COLORS.accent : ink;
585
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
389
586
  return /* @__PURE__ */ jsxRuntime.jsxs(
390
587
  "button",
391
588
  {
392
- ref,
589
+ ref: (node) => {
590
+ rootRef.current = node;
591
+ assignRef2(ref, node);
592
+ },
393
593
  type: "button",
394
594
  className: cn(className),
395
595
  disabled,
@@ -425,12 +625,12 @@ var Button = react.forwardRef(
425
625
  {
426
626
  shape: "rectangle",
427
627
  roughness,
428
- seed,
628
+ seed: sketchSeed,
429
629
  sketchColor: stroke,
430
630
  bowing,
431
631
  fillStyle: fillStyle ?? "hachure",
432
632
  fill,
433
- strokeWidth: hovered ? (strokeWidth ?? 1.75) + 0.4 : strokeWidth
633
+ strokeWidth: !shouldAnimate && hovered ? (strokeWidth ?? 1.75) + 0.4 : strokeWidth
434
634
  }
435
635
  ),
436
636
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", zIndex: 1 }, children })
@@ -452,11 +652,18 @@ var Input = react.forwardRef(function Input2({
452
652
  id,
453
653
  onFocus,
454
654
  onBlur,
655
+ animate,
455
656
  ...rest
456
657
  }, ref) {
457
658
  const [focused, setFocused] = react.useState(false);
659
+ const fieldRef = react.useRef(null);
660
+ const shouldAnimate = useAnimate(animate);
661
+ const resolvedSeed = useResolvedSeed(seed);
662
+ const focusSeed = deriveSeed(resolvedSeed, "focus");
663
+ const sketchSeed = shouldAnimate && focused ? focusSeed : resolvedSeed;
458
664
  const ink = sketchColor ?? SKETCH_COLORS.ink;
459
665
  const inputId = id;
666
+ useDrawIn(fieldRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
460
667
  return /* @__PURE__ */ jsxRuntime.jsxs(
461
668
  "label",
462
669
  {
@@ -480,17 +687,17 @@ var Input = react.forwardRef(function Input2({
480
687
  children: label
481
688
  }
482
689
  ) : null,
483
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { position: "relative", display: "block" }, children: [
690
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { ref: fieldRef, style: { position: "relative", display: "block" }, children: [
484
691
  /* @__PURE__ */ jsxRuntime.jsx(
485
692
  RoughSvg,
486
693
  {
487
694
  shape: "rectangle",
488
695
  roughness,
489
- seed,
696
+ seed: sketchSeed,
490
697
  sketchColor: focused ? SKETCH_COLORS.accent : ink,
491
698
  bowing,
492
699
  fillStyle,
493
- strokeWidth: focused ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth
700
+ strokeWidth: focused && !shouldAnimate ? (strokeWidth ?? 1.75) + 0.35 : focused ? (strokeWidth ?? 1.75) + 0.2 : strokeWidth
494
701
  }
495
702
  ),
496
703
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -544,10 +751,17 @@ var Textarea = react.forwardRef(
544
751
  rows = 4,
545
752
  onFocus,
546
753
  onBlur,
754
+ animate,
547
755
  ...rest
548
756
  }, ref) {
549
757
  const [focused, setFocused] = react.useState(false);
758
+ const fieldRef = react.useRef(null);
759
+ const shouldAnimate = useAnimate(animate);
760
+ const resolvedSeed = useResolvedSeed(seed);
761
+ const focusSeed = deriveSeed(resolvedSeed, "focus");
762
+ const sketchSeed = shouldAnimate && focused ? focusSeed : resolvedSeed;
550
763
  const ink = sketchColor ?? SKETCH_COLORS.ink;
764
+ useDrawIn(fieldRef, DRAW_IN_DURATION_MS, shouldAnimate, sketchSeed);
551
765
  return /* @__PURE__ */ jsxRuntime.jsxs(
552
766
  "label",
553
767
  {
@@ -571,17 +785,17 @@ var Textarea = react.forwardRef(
571
785
  children: label
572
786
  }
573
787
  ) : null,
574
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { position: "relative", display: "block" }, children: [
788
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { ref: fieldRef, style: { position: "relative", display: "block" }, children: [
575
789
  /* @__PURE__ */ jsxRuntime.jsx(
576
790
  RoughSvg,
577
791
  {
578
792
  shape: "rectangle",
579
793
  roughness,
580
- seed,
794
+ seed: sketchSeed,
581
795
  sketchColor: focused ? SKETCH_COLORS.accent : ink,
582
796
  bowing,
583
797
  fillStyle,
584
- strokeWidth: focused ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth
798
+ strokeWidth: focused && !shouldAnimate ? (strokeWidth ?? 1.75) + 0.35 : focused ? (strokeWidth ?? 1.75) + 0.2 : strokeWidth
585
799
  }
586
800
  ),
587
801
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -626,6 +840,36 @@ var Textarea = react.forwardRef(
626
840
  );
627
841
  var BOX = 20;
628
842
  var CHECK_PATH = "M 4.5 10.5 L 8.5 14.5 L 15.5 5.5";
843
+ function CheckMark({
844
+ roughness,
845
+ seed,
846
+ bowing,
847
+ strokeWidth,
848
+ shouldAnimate
849
+ }) {
850
+ const ref = react.useRef(null);
851
+ useDrawIn(ref, DRAW_IN_MARK_MS, shouldAnimate);
852
+ return /* @__PURE__ */ jsxRuntime.jsx(
853
+ "span",
854
+ {
855
+ ref,
856
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
857
+ children: /* @__PURE__ */ jsxRuntime.jsx(
858
+ RoughSvg,
859
+ {
860
+ shape: "path",
861
+ path: CHECK_PATH,
862
+ roughness: (roughness ?? 1.5) * 0.7,
863
+ seed,
864
+ sketchColor: SKETCH_COLORS.accent,
865
+ bowing,
866
+ strokeWidth: (strokeWidth ?? 1.6) + 0.4,
867
+ inset: 0
868
+ }
869
+ )
870
+ }
871
+ );
872
+ }
629
873
  var Checkbox = react.forwardRef(
630
874
  function Checkbox2({
631
875
  className,
@@ -641,11 +885,15 @@ var Checkbox = react.forwardRef(
641
885
  defaultChecked,
642
886
  onCheckedChange,
643
887
  id,
888
+ animate,
644
889
  ...rest
645
890
  }, ref) {
646
891
  const ink = sketchColor ?? SKETCH_COLORS.ink;
647
892
  const generatedId = react.useId();
648
893
  const inputId = id ?? generatedId;
894
+ const boxRef = react.useRef(null);
895
+ const shouldAnimate = useAnimate(animate);
896
+ useDrawIn(boxRef, DRAW_IN_DURATION_MS, shouldAnimate);
649
897
  return /* @__PURE__ */ jsxRuntime.jsxs(
650
898
  "span",
651
899
  {
@@ -680,16 +928,23 @@ var Checkbox = react.forwardRef(
680
928
  ...rest,
681
929
  children: [
682
930
  /* @__PURE__ */ jsxRuntime.jsx(
683
- RoughSvg,
931
+ "span",
684
932
  {
685
- shape: "rectangle",
686
- roughness,
687
- seed,
688
- sketchColor: ink,
689
- bowing,
690
- fillStyle,
691
- strokeWidth: strokeWidth ?? 1.6,
692
- inset: 1.5
933
+ ref: boxRef,
934
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
935
+ children: /* @__PURE__ */ jsxRuntime.jsx(
936
+ RoughSvg,
937
+ {
938
+ shape: "rectangle",
939
+ roughness,
940
+ seed,
941
+ sketchColor: ink,
942
+ bowing,
943
+ fillStyle,
944
+ strokeWidth: strokeWidth ?? 1.6,
945
+ inset: 1.5
946
+ }
947
+ )
693
948
  }
694
949
  ),
695
950
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -701,16 +956,13 @@ var Checkbox = react.forwardRef(
701
956
  display: "block"
702
957
  },
703
958
  children: /* @__PURE__ */ jsxRuntime.jsx(
704
- RoughSvg,
959
+ CheckMark,
705
960
  {
706
- shape: "path",
707
- path: CHECK_PATH,
708
- roughness: (roughness ?? 1.5) * 0.7,
961
+ roughness,
709
962
  seed,
710
- sketchColor: SKETCH_COLORS.accent,
711
963
  bowing,
712
- strokeWidth: (strokeWidth ?? 1.6) + 0.4,
713
- inset: 0
964
+ strokeWidth,
965
+ shouldAnimate
714
966
  }
715
967
  )
716
968
  }
@@ -751,6 +1003,53 @@ var RadioGroup = react.forwardRef(
751
1003
  }
752
1004
  );
753
1005
  var SIZE = 20;
1006
+ function RadioDot({
1007
+ roughness,
1008
+ seed,
1009
+ bowing,
1010
+ shouldAnimate
1011
+ }) {
1012
+ const ref = react.useRef(null);
1013
+ useDrawIn(ref, DRAW_IN_MARK_MS, shouldAnimate);
1014
+ react.useLayoutEffect(() => {
1015
+ const node = ref.current;
1016
+ if (!node || !shouldAnimate) return;
1017
+ const animation = node.animate(
1018
+ [
1019
+ { transform: "scale(0.35)", opacity: 0 },
1020
+ { transform: "scale(1)", opacity: 1 }
1021
+ ],
1022
+ { duration: 180, easing: "ease-out", fill: "forwards" }
1023
+ );
1024
+ return () => animation.cancel();
1025
+ }, [shouldAnimate]);
1026
+ return /* @__PURE__ */ jsxRuntime.jsx(
1027
+ "span",
1028
+ {
1029
+ ref,
1030
+ style: {
1031
+ position: "absolute",
1032
+ inset: 0,
1033
+ pointerEvents: "none",
1034
+ display: "block"
1035
+ },
1036
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1037
+ RoughSvg,
1038
+ {
1039
+ shape: "ellipse",
1040
+ roughness: (roughness ?? 1.5) + 0.2,
1041
+ seed,
1042
+ sketchColor: SKETCH_COLORS.accent,
1043
+ fill: SKETCH_COLORS.accent,
1044
+ fillStyle: "solid",
1045
+ bowing,
1046
+ strokeWidth: 1,
1047
+ inset: 6
1048
+ }
1049
+ )
1050
+ }
1051
+ );
1052
+ }
754
1053
  var Radio = react.forwardRef(function Radio2({
755
1054
  className,
756
1055
  style,
@@ -762,11 +1061,15 @@ var Radio = react.forwardRef(function Radio2({
762
1061
  fillStyle,
763
1062
  strokeWidth,
764
1063
  id,
1064
+ animate,
765
1065
  ...rest
766
1066
  }, ref) {
767
1067
  const ink = sketchColor ?? SKETCH_COLORS.ink;
768
1068
  const generatedId = react.useId();
769
1069
  const inputId = id ?? generatedId;
1070
+ const ringRef = react.useRef(null);
1071
+ const shouldAnimate = useAnimate(animate);
1072
+ useDrawIn(ringRef, DRAW_IN_DURATION_MS, shouldAnimate);
770
1073
  return /* @__PURE__ */ jsxRuntime.jsxs(
771
1074
  "span",
772
1075
  {
@@ -799,16 +1102,23 @@ var Radio = react.forwardRef(function Radio2({
799
1102
  ...rest,
800
1103
  children: [
801
1104
  /* @__PURE__ */ jsxRuntime.jsx(
802
- RoughSvg,
1105
+ "span",
803
1106
  {
804
- shape: "ellipse",
805
- roughness,
806
- seed,
807
- sketchColor: ink,
808
- bowing,
809
- fillStyle,
810
- strokeWidth: strokeWidth ?? 1.6,
811
- inset: 1.5
1107
+ ref: ringRef,
1108
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1109
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1110
+ RoughSvg,
1111
+ {
1112
+ shape: "ellipse",
1113
+ roughness,
1114
+ seed,
1115
+ sketchColor: ink,
1116
+ bowing,
1117
+ fillStyle,
1118
+ strokeWidth: strokeWidth ?? 1.6,
1119
+ inset: 1.5
1120
+ }
1121
+ )
812
1122
  }
813
1123
  ),
814
1124
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -820,17 +1130,12 @@ var Radio = react.forwardRef(function Radio2({
820
1130
  display: "block"
821
1131
  },
822
1132
  children: /* @__PURE__ */ jsxRuntime.jsx(
823
- RoughSvg,
1133
+ RadioDot,
824
1134
  {
825
- shape: "ellipse",
826
- roughness: (roughness ?? 1.5) + 0.2,
1135
+ roughness,
827
1136
  seed,
828
- sketchColor: SKETCH_COLORS.accent,
829
- fill: SKETCH_COLORS.accent,
830
- fillStyle: "solid",
831
1137
  bowing,
832
- strokeWidth: 1,
833
- inset: 6
1138
+ shouldAnimate
834
1139
  }
835
1140
  )
836
1141
  }
@@ -864,6 +1169,7 @@ var Card = react.forwardRef(function Card2({
864
1169
  bowing,
865
1170
  fillStyle,
866
1171
  strokeWidth,
1172
+ animate,
867
1173
  ...rest
868
1174
  }, ref) {
869
1175
  const boxProps = {
@@ -874,7 +1180,8 @@ var Card = react.forwardRef(function Card2({
874
1180
  sketchColor: sketchColor ?? SKETCH_COLORS.ink,
875
1181
  bowing,
876
1182
  fillStyle,
877
- strokeWidth
1183
+ strokeWidth,
1184
+ animate
878
1185
  };
879
1186
  return /* @__PURE__ */ jsxRuntime.jsxs(
880
1187
  SketchBox,
@@ -914,6 +1221,7 @@ var Badge = react.forwardRef(function Badge2({
914
1221
  bowing,
915
1222
  fillStyle,
916
1223
  strokeWidth,
1224
+ animate,
917
1225
  ...rest
918
1226
  }, ref) {
919
1227
  const ink = variant === "accent" ? sketchColor ?? SKETCH_COLORS.accent : sketchColor ?? SKETCH_COLORS.ink;
@@ -941,6 +1249,7 @@ var Badge = react.forwardRef(function Badge2({
941
1249
  sketchColor: ink,
942
1250
  bowing,
943
1251
  strokeWidth: strokeWidth ?? 1.4,
1252
+ animate,
944
1253
  ...rest,
945
1254
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { ref, children })
946
1255
  }
@@ -971,6 +1280,7 @@ var Alert = react.forwardRef(function Alert2({
971
1280
  fillStyle,
972
1281
  strokeWidth,
973
1282
  role = "status",
1283
+ animate,
974
1284
  ...rest
975
1285
  }, ref) {
976
1286
  const color = sketchColor ?? VARIANT_COLOR[variant];
@@ -989,6 +1299,8 @@ var Alert = react.forwardRef(function Alert2({
989
1299
  fillStyle: fillStyle ?? "hachure",
990
1300
  fill: VARIANT_FILL[variant],
991
1301
  strokeWidth,
1302
+ animate,
1303
+ drawInDuration: DRAW_IN_ALERT_MS,
992
1304
  ...rest,
993
1305
  children: [
994
1306
  title ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -1021,15 +1333,27 @@ function Modal({
1021
1333
  sketchColor,
1022
1334
  bowing,
1023
1335
  fillStyle,
1024
- strokeWidth
1336
+ strokeWidth,
1337
+ animate
1025
1338
  }) {
1026
1339
  const ink = sketchColor ?? SKETCH_COLORS.ink;
1027
- return /* @__PURE__ */ jsxRuntime.jsxs(Dialog__namespace.Root, { open, defaultOpen, onOpenChange, children: [
1340
+ const [uncontrolled, setUncontrolled] = react.useState(defaultOpen === true);
1341
+ const isOpen = open ?? uncontrolled;
1342
+ const shouldAnimate = useAnimate(animate);
1343
+ function handleOpenChange(next) {
1344
+ setUncontrolled(next);
1345
+ onOpenChange?.(next);
1346
+ }
1347
+ return /* @__PURE__ */ jsxRuntime.jsxs(Dialog__namespace.Root, { open: isOpen, onOpenChange: handleOpenChange, children: [
1028
1348
  trigger ? /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Trigger, { asChild: true, children: trigger }) : null,
1029
- /* @__PURE__ */ jsxRuntime.jsxs(Dialog__namespace.Portal, { children: [
1030
- /* @__PURE__ */ jsxRuntime.jsx(
1031
- Dialog__namespace.Overlay,
1349
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isOpen ? /* @__PURE__ */ jsxRuntime.jsxs(Dialog__namespace.Portal, { forceMount: true, children: [
1350
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Overlay, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1351
+ framerMotion.motion.div,
1032
1352
  {
1353
+ initial: shouldAnimate ? { opacity: 0 } : false,
1354
+ animate: { opacity: 1 },
1355
+ exit: shouldAnimate ? { opacity: 0 } : void 0,
1356
+ transition: shouldAnimate ? { duration: 0.2, ease: "easeOut" } : { duration: 0 },
1033
1357
  style: {
1034
1358
  position: "fixed",
1035
1359
  inset: 0,
@@ -1037,92 +1361,121 @@ function Modal({
1037
1361
  zIndex: 70
1038
1362
  }
1039
1363
  }
1040
- ),
1364
+ ) }),
1041
1365
  /* @__PURE__ */ jsxRuntime.jsx(
1042
- Dialog__namespace.Content,
1366
+ "div",
1043
1367
  {
1044
- className: cn(className),
1045
1368
  style: {
1046
1369
  position: "fixed",
1047
- left: "50%",
1048
- top: "50%",
1049
- transform: "translate(-50%, -50%)",
1370
+ inset: 0,
1050
1371
  zIndex: 80,
1051
- width: "min(480px, calc(100vw - 32px))",
1052
- outline: "none"
1372
+ display: "flex",
1373
+ alignItems: "center",
1374
+ justifyContent: "center",
1375
+ pointerEvents: "none",
1376
+ padding: 16
1053
1377
  },
1054
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1055
- SketchBox,
1378
+ children: /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Content, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1379
+ framerMotion.motion.div,
1056
1380
  {
1057
- roughness,
1058
- seed,
1059
- sketchColor: ink,
1060
- bowing,
1061
- fillStyle: fillStyle ?? "solid",
1062
- fill: "#f7f6f2",
1063
- strokeWidth: strokeWidth ?? 2,
1064
- shadow: true,
1065
- contentStyle: { padding: "20px 22px 18px" },
1066
- children: [
1067
- /* @__PURE__ */ jsxRuntime.jsxs(
1068
- "div",
1069
- {
1070
- style: {
1071
- display: "flex",
1072
- alignItems: "flex-start",
1073
- justifyContent: "space-between",
1074
- gap: 12,
1075
- marginBottom: title ? 10 : 0
1076
- },
1077
- children: [
1078
- title ? /* @__PURE__ */ jsxRuntime.jsx(
1079
- Dialog__namespace.Title,
1080
- {
1081
- style: {
1082
- margin: 0,
1083
- fontSize: 18,
1084
- fontWeight: doodleUiFontWeight(700),
1085
- fontFamily: doodleUiFontFamily,
1086
- color: ink
1087
- },
1088
- children: title
1089
- }
1090
- ) : /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Title, { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: "Dialog" }),
1091
- /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1092
- Button,
1093
- {
1094
- variant: "ghost",
1095
- size: "sm",
1096
- roughness,
1097
- seed,
1098
- sketchColor: ink,
1099
- "aria-label": "Close",
1100
- children: "Close"
1101
- }
1102
- ) })
1103
- ]
1104
- }
1105
- ),
1106
- description ? /* @__PURE__ */ jsxRuntime.jsx(
1107
- Dialog__namespace.Description,
1108
- {
1109
- style: {
1110
- margin: "0 0 14px",
1111
- fontSize: 14,
1112
- lineHeight: 1.5,
1113
- color: ink,
1114
- opacity: 0.8
1115
- },
1116
- children: description
1117
- }
1118
- ) : null,
1119
- /* @__PURE__ */ jsxRuntime.jsx("div", { children })
1120
- ]
1381
+ className: cn(className),
1382
+ initial: shouldAnimate ? { opacity: 0, scale: 0.96, y: 10 } : false,
1383
+ animate: { opacity: 1, scale: 1, y: 0 },
1384
+ exit: shouldAnimate ? { opacity: 0, scale: 0.96, y: 8 } : void 0,
1385
+ transition: shouldAnimate ? { duration: 0.22, ease: "easeOut" } : { duration: 0 },
1386
+ style: {
1387
+ width: "min(480px, calc(100vw - 32px))",
1388
+ outline: "none",
1389
+ pointerEvents: "auto"
1390
+ },
1391
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1392
+ SketchBox,
1393
+ {
1394
+ roughness,
1395
+ seed,
1396
+ sketchColor: ink,
1397
+ bowing,
1398
+ fillStyle: fillStyle ?? "solid",
1399
+ fill: "#f7f6f2",
1400
+ strokeWidth: strokeWidth ?? 2,
1401
+ shadow: true,
1402
+ animate,
1403
+ contentStyle: { padding: "20px 22px 18px" },
1404
+ children: [
1405
+ /* @__PURE__ */ jsxRuntime.jsxs(
1406
+ "div",
1407
+ {
1408
+ style: {
1409
+ display: "flex",
1410
+ alignItems: "flex-start",
1411
+ justifyContent: "space-between",
1412
+ gap: 12,
1413
+ marginBottom: title ? 10 : 0
1414
+ },
1415
+ children: [
1416
+ title ? /* @__PURE__ */ jsxRuntime.jsx(
1417
+ Dialog__namespace.Title,
1418
+ {
1419
+ style: {
1420
+ margin: 0,
1421
+ fontSize: 18,
1422
+ fontWeight: doodleUiFontWeight(700),
1423
+ fontFamily: doodleUiFontFamily,
1424
+ color: ink
1425
+ },
1426
+ children: title
1427
+ }
1428
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1429
+ Dialog__namespace.Title,
1430
+ {
1431
+ style: {
1432
+ position: "absolute",
1433
+ width: 1,
1434
+ height: 1,
1435
+ overflow: "hidden",
1436
+ clip: "rect(0 0 0 0)"
1437
+ },
1438
+ children: "Dialog"
1439
+ }
1440
+ ),
1441
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1442
+ Button,
1443
+ {
1444
+ variant: "ghost",
1445
+ size: "sm",
1446
+ roughness,
1447
+ seed,
1448
+ sketchColor: ink,
1449
+ animate,
1450
+ "aria-label": "Close",
1451
+ children: "Close"
1452
+ }
1453
+ ) })
1454
+ ]
1455
+ }
1456
+ ),
1457
+ description ? /* @__PURE__ */ jsxRuntime.jsx(
1458
+ Dialog__namespace.Description,
1459
+ {
1460
+ style: {
1461
+ margin: "0 0 14px",
1462
+ fontSize: 14,
1463
+ lineHeight: 1.5,
1464
+ color: ink,
1465
+ opacity: 0.8
1466
+ },
1467
+ children: description
1468
+ }
1469
+ ) : null,
1470
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children })
1471
+ ]
1472
+ }
1473
+ )
1121
1474
  }
1122
- )
1475
+ ) })
1123
1476
  }
1124
1477
  )
1125
- ] })
1478
+ ] }) : null })
1126
1479
  ] });
1127
1480
  }
1128
1481
  var ModalRoot = Dialog__namespace.Root;
@@ -1140,13 +1493,20 @@ var Divider = react.forwardRef(
1140
1493
  sketchColor,
1141
1494
  bowing,
1142
1495
  strokeWidth,
1496
+ animate,
1143
1497
  ...rest
1144
1498
  }, ref) {
1499
+ const rootRef = react.useRef(null);
1500
+ const shouldAnimate = useAnimate(animate);
1145
1501
  const horizontal = orientation === "horizontal";
1502
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate);
1146
1503
  return /* @__PURE__ */ jsxRuntime.jsx(
1147
1504
  "div",
1148
1505
  {
1149
- ref,
1506
+ ref: (node) => {
1507
+ rootRef.current = node;
1508
+ assignRef(ref, node);
1509
+ },
1150
1510
  role: "separator",
1151
1511
  "aria-orientation": orientation,
1152
1512
  className: cn(className),
@@ -1186,15 +1546,27 @@ var Progress = react.forwardRef(
1186
1546
  bowing,
1187
1547
  fillStyle,
1188
1548
  strokeWidth,
1549
+ animate,
1189
1550
  ...rest
1190
1551
  }, ref) {
1552
+ const rootRef = react.useRef(null);
1553
+ const trackRef = react.useRef(null);
1191
1554
  const ink = sketchColor ?? SKETCH_COLORS.accent;
1192
1555
  const clamped = Math.min(max, Math.max(0, value));
1193
1556
  const percent = max === 0 ? 0 : clamped / max * 100;
1557
+ const shouldAnimate = useAnimate(animate);
1558
+ const displayed = useTweenNumber(percent, shouldAnimate, 420);
1559
+ const resolvedSeed = useResolvedSeed(seed);
1560
+ const fillSeed = shouldAnimate ? deriveSeed(resolvedSeed, `fill-${Math.round(displayed / 6)}`) : resolvedSeed;
1561
+ const fillRoughness = (roughness ?? 1.5) + 0.4 + displayed / 100 * 0.7;
1562
+ useDrawIn(trackRef, DRAW_IN_DURATION_MS, shouldAnimate);
1194
1563
  return /* @__PURE__ */ jsxRuntime.jsxs(
1195
1564
  "div",
1196
1565
  {
1197
- ref,
1566
+ ref: (node) => {
1567
+ rootRef.current = node;
1568
+ assignRef(ref, node);
1569
+ },
1198
1570
  role: "progressbar",
1199
1571
  "aria-valuemin": 0,
1200
1572
  "aria-valuemax": max,
@@ -1209,18 +1581,25 @@ var Progress = react.forwardRef(
1209
1581
  ...rest,
1210
1582
  children: [
1211
1583
  /* @__PURE__ */ jsxRuntime.jsx(
1212
- RoughSvg,
1584
+ "span",
1213
1585
  {
1214
- shape: "rectangle",
1215
- roughness,
1216
- seed,
1217
- sketchColor: SKETCH_COLORS.ink,
1218
- bowing,
1219
- strokeWidth: strokeWidth ?? 1.5,
1220
- inset: 2
1586
+ ref: trackRef,
1587
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1588
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1589
+ RoughSvg,
1590
+ {
1591
+ shape: "rectangle",
1592
+ roughness,
1593
+ seed: resolvedSeed,
1594
+ sketchColor: SKETCH_COLORS.ink,
1595
+ bowing,
1596
+ strokeWidth: strokeWidth ?? 1.5,
1597
+ inset: 2
1598
+ }
1599
+ )
1221
1600
  }
1222
1601
  ),
1223
- percent > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1602
+ displayed > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1224
1603
  "div",
1225
1604
  {
1226
1605
  style: {
@@ -1228,16 +1607,16 @@ var Progress = react.forwardRef(
1228
1607
  left: 5,
1229
1608
  top: 4,
1230
1609
  bottom: 4,
1231
- width: `calc(${percent}% - 10px)`,
1232
- minWidth: percent > 0 ? 8 : 0,
1610
+ width: `calc(${displayed}% - 10px)`,
1611
+ minWidth: displayed > 0 ? 8 : 0,
1233
1612
  overflow: "hidden"
1234
1613
  },
1235
1614
  children: /* @__PURE__ */ jsxRuntime.jsx(
1236
1615
  RoughSvg,
1237
1616
  {
1238
1617
  shape: "rectangle",
1239
- roughness: (roughness ?? 1.5) + 0.55,
1240
- seed,
1618
+ roughness: fillRoughness,
1619
+ seed: fillSeed,
1241
1620
  sketchColor: ink,
1242
1621
  fill: ink,
1243
1622
  fillStyle: fillStyle ?? "hachure",
@@ -1265,7 +1644,8 @@ function Tooltip({
1265
1644
  sketchColor,
1266
1645
  bowing,
1267
1646
  fillStyle,
1268
- strokeWidth
1647
+ strokeWidth,
1648
+ animate
1269
1649
  }) {
1270
1650
  const ink = sketchColor ?? SKETCH_COLORS.ink;
1271
1651
  return /* @__PURE__ */ jsxRuntime.jsxs(TooltipPrimitive__namespace.Root, { delayDuration, children: [
@@ -1287,6 +1667,8 @@ function Tooltip({
1287
1667
  fillStyle: fillStyle ?? "solid",
1288
1668
  fill: "#f7f6f2",
1289
1669
  strokeWidth: strokeWidth ?? 1.4,
1670
+ animate,
1671
+ drawInDuration: DRAW_IN_TOOLTIP_MS,
1290
1672
  contentStyle: {
1291
1673
  padding: "6px 10px",
1292
1674
  fontSize: 13,
@@ -1328,6 +1710,7 @@ function Select({
1328
1710
  value,
1329
1711
  defaultValue,
1330
1712
  onValueChange,
1713
+ animate,
1331
1714
  "aria-label": ariaLabel,
1332
1715
  ...rest
1333
1716
  }) {
@@ -1350,7 +1733,8 @@ function Select({
1350
1733
  ink,
1351
1734
  selectedValue,
1352
1735
  selectedLabel,
1353
- placeholder
1736
+ placeholder,
1737
+ animate
1354
1738
  },
1355
1739
  children: /* @__PURE__ */ jsxRuntime.jsxs(
1356
1740
  SelectPrimitive__namespace.Root,
@@ -1391,10 +1775,16 @@ var SelectTrigger = react.forwardRef(
1391
1775
  function SelectTrigger2({ className, style, placeholder, children, ...rest }, ref) {
1392
1776
  const sketch = useSelectSketch();
1393
1777
  const [openish, setOpenish] = react.useState(false);
1778
+ const rootRef = react.useRef(null);
1779
+ const shouldAnimate = useAnimate(sketch.animate);
1780
+ useDrawIn(rootRef, DRAW_IN_DURATION_MS, shouldAnimate);
1394
1781
  return /* @__PURE__ */ jsxRuntime.jsxs(
1395
1782
  SelectPrimitive__namespace.Trigger,
1396
1783
  {
1397
- ref,
1784
+ ref: (node) => {
1785
+ rootRef.current = node;
1786
+ assignRef(ref, node);
1787
+ },
1398
1788
  className: cn(className),
1399
1789
  onPointerDown: () => setOpenish(true),
1400
1790
  onBlur: () => setOpenish(false),
@@ -1503,6 +1893,7 @@ var SelectContent = react.forwardRef(
1503
1893
  fillStyle: sketch.fillStyle ?? "solid",
1504
1894
  fill: "#f7f6f2",
1505
1895
  strokeWidth: sketch.strokeWidth ?? 1.5,
1896
+ animate: sketch.animate,
1506
1897
  contentStyle: { padding: "6px 4px" },
1507
1898
  children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Viewport, { children })
1508
1899
  }
@@ -1593,12 +1984,17 @@ var Switch = react.forwardRef(
1593
1984
  defaultChecked,
1594
1985
  onCheckedChange,
1595
1986
  id,
1987
+ animate,
1596
1988
  ...rest
1597
1989
  }, ref) {
1598
1990
  const [uncontrolled, setUncontrolled] = react.useState(defaultChecked === true);
1599
1991
  const isOn = checked ?? uncontrolled;
1600
1992
  const ink = sketchColor ?? SKETCH_COLORS.ink;
1601
1993
  const resolvedSeed = useResolvedSeed(seed);
1994
+ const shouldAnimate = useAnimate(animate);
1995
+ const trackRef = react.useRef(null);
1996
+ const trackSeed = shouldAnimate ? deriveSeed(resolvedSeed, isOn ? "on" : "off") : resolvedSeed;
1997
+ useDrawIn(trackRef, DRAW_IN_DURATION_MS, shouldAnimate, trackSeed);
1602
1998
  return /* @__PURE__ */ jsxRuntime.jsxs(
1603
1999
  "span",
1604
2000
  {
@@ -1637,22 +2033,32 @@ var Switch = react.forwardRef(
1637
2033
  ...rest,
1638
2034
  children: [
1639
2035
  /* @__PURE__ */ jsxRuntime.jsx(
1640
- RoughSvg,
2036
+ "span",
1641
2037
  {
1642
- shape: "rectangle",
1643
- roughness,
1644
- seed: resolvedSeed,
1645
- sketchColor: isOn ? SKETCH_COLORS.accent : ink,
1646
- bowing: bowing ?? 1.4,
1647
- fillStyle: fillStyle ?? (isOn ? "hachure" : void 0),
1648
- fill: isOn ? SKETCH_COLORS.accentFill : void 0,
1649
- strokeWidth: strokeWidth ?? 1.6,
1650
- inset: 1.5
2038
+ ref: trackRef,
2039
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
2040
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2041
+ RoughSvg,
2042
+ {
2043
+ shape: "rectangle",
2044
+ roughness,
2045
+ seed: trackSeed,
2046
+ sketchColor: isOn ? SKETCH_COLORS.accent : ink,
2047
+ bowing: bowing ?? 1.4,
2048
+ fillStyle: fillStyle ?? (isOn ? "hachure" : void 0),
2049
+ fill: isOn ? SKETCH_COLORS.accentFill : void 0,
2050
+ strokeWidth: strokeWidth ?? 1.6,
2051
+ inset: 1.5
2052
+ }
2053
+ )
1651
2054
  }
1652
2055
  ),
1653
- /* @__PURE__ */ jsxRuntime.jsx(
1654
- SwitchPrimitive__namespace.Thumb,
2056
+ /* @__PURE__ */ jsxRuntime.jsx(SwitchPrimitive__namespace.Thumb, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
2057
+ framerMotion.motion.span,
1655
2058
  {
2059
+ initial: false,
2060
+ animate: { x: isOn ? THUMB_ON : THUMB_OFF },
2061
+ transition: shouldAnimate ? { type: "spring", stiffness: 420, damping: 30 } : { duration: 0 },
1656
2062
  style: {
1657
2063
  position: "absolute",
1658
2064
  top: (TRACK_H - THUMB) / 2,
@@ -1660,8 +2066,6 @@ var Switch = react.forwardRef(
1660
2066
  width: THUMB,
1661
2067
  height: THUMB,
1662
2068
  display: "block",
1663
- transform: `translateX(${isOn ? THUMB_ON : THUMB_OFF}px)`,
1664
- transition: "transform 160ms ease",
1665
2069
  zIndex: 1
1666
2070
  },
1667
2071
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1679,7 +2083,7 @@ var Switch = react.forwardRef(
1679
2083
  }
1680
2084
  )
1681
2085
  }
1682
- )
2086
+ ) })
1683
2087
  ]
1684
2088
  }
1685
2089
  ),
@@ -1721,12 +2125,14 @@ var Tabs = react.forwardRef(function Tabs2({
1721
2125
  bowing,
1722
2126
  fillStyle,
1723
2127
  strokeWidth,
2128
+ animate,
1724
2129
  ...rest
1725
2130
  }, ref) {
1726
2131
  const [uncontrolled, setUncontrolled] = react.useState(defaultValue);
1727
2132
  const current = value ?? uncontrolled;
1728
2133
  const resolvedSeed = useResolvedSeed(seed);
1729
2134
  const ink = sketchColor ?? SKETCH_COLORS.ink;
2135
+ const shouldAnimate = useAnimate(animate);
1730
2136
  return /* @__PURE__ */ jsxRuntime.jsx(
1731
2137
  TabsSketchContext.Provider,
1732
2138
  {
@@ -1739,7 +2145,8 @@ var Tabs = react.forwardRef(function Tabs2({
1739
2145
  strokeWidth,
1740
2146
  resolvedSeed,
1741
2147
  ink,
1742
- current
2148
+ current,
2149
+ shouldAnimate
1743
2150
  },
1744
2151
  children: /* @__PURE__ */ jsxRuntime.jsx(
1745
2152
  TabsPrimitive__namespace.Root,
@@ -1760,21 +2167,94 @@ var Tabs = react.forwardRef(function Tabs2({
1760
2167
  }
1761
2168
  );
1762
2169
  });
2170
+ function TabUnderline({
2171
+ box,
2172
+ seed,
2173
+ sketch
2174
+ }) {
2175
+ const ref = react.useRef(null);
2176
+ useDrawIn(ref, DRAW_IN_MARK_MS, sketch.shouldAnimate, seed);
2177
+ return /* @__PURE__ */ jsxRuntime.jsx(
2178
+ "span",
2179
+ {
2180
+ ref,
2181
+ "aria-hidden": "true",
2182
+ style: {
2183
+ position: "absolute",
2184
+ left: box.left,
2185
+ top: box.top,
2186
+ width: box.width,
2187
+ height: 10,
2188
+ pointerEvents: "none",
2189
+ transition: sketch.shouldAnimate ? "left 220ms ease, width 220ms ease, top 220ms ease" : void 0
2190
+ },
2191
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2192
+ RoughSvg,
2193
+ {
2194
+ shape: "line",
2195
+ roughness: (sketch.roughness ?? 1.5) + 0.35,
2196
+ seed,
2197
+ sketchColor: SKETCH_COLORS.accent,
2198
+ bowing: sketch.bowing ?? 1.8,
2199
+ strokeWidth: (sketch.strokeWidth ?? 1.75) + 0.4,
2200
+ inset: 2
2201
+ }
2202
+ )
2203
+ }
2204
+ );
2205
+ }
1763
2206
  var TabList = react.forwardRef(
1764
2207
  function TabList2({ className, style, children, ...rest }, ref) {
1765
- return /* @__PURE__ */ jsxRuntime.jsx(
2208
+ const sketch = useTabsSketch();
2209
+ const listRef = react.useRef(null);
2210
+ const [underline, setUnderline] = react.useState(null);
2211
+ react.useLayoutEffect(() => {
2212
+ const list = listRef.current;
2213
+ if (!list) return;
2214
+ const measure = () => {
2215
+ const active = list.querySelector('[data-state="active"]');
2216
+ if (!active) {
2217
+ setUnderline(null);
2218
+ return;
2219
+ }
2220
+ setUnderline({
2221
+ left: active.offsetLeft + 8,
2222
+ top: active.offsetTop + active.offsetHeight - 10,
2223
+ width: Math.max(12, active.offsetWidth - 16)
2224
+ });
2225
+ };
2226
+ measure();
2227
+ const observer = new ResizeObserver(measure);
2228
+ observer.observe(list);
2229
+ return () => observer.disconnect();
2230
+ }, [sketch.current, children]);
2231
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1766
2232
  TabsPrimitive__namespace.List,
1767
2233
  {
1768
- ref,
2234
+ ref: (node) => {
2235
+ listRef.current = node;
2236
+ assignRef(ref, node);
2237
+ },
1769
2238
  className: cn(className),
1770
2239
  style: {
1771
2240
  display: "flex",
1772
2241
  flexWrap: "wrap",
1773
2242
  gap: 4,
2243
+ position: "relative",
1774
2244
  ...style
1775
2245
  },
1776
2246
  ...rest,
1777
- children
2247
+ children: [
2248
+ children,
2249
+ underline && sketch.current ? /* @__PURE__ */ jsxRuntime.jsx(
2250
+ TabUnderline,
2251
+ {
2252
+ box: underline,
2253
+ seed: deriveSeed(sketch.resolvedSeed, sketch.current),
2254
+ sketch
2255
+ }
2256
+ ) : null
2257
+ ]
1778
2258
  }
1779
2259
  );
1780
2260
  }
@@ -1782,7 +2262,7 @@ var TabList = react.forwardRef(
1782
2262
  var Tab = react.forwardRef(function Tab2({ className, style, children, value, ...rest }, ref) {
1783
2263
  const sketch = useTabsSketch();
1784
2264
  const active = sketch.current === value;
1785
- return /* @__PURE__ */ jsxRuntime.jsxs(
2265
+ return /* @__PURE__ */ jsxRuntime.jsx(
1786
2266
  TabsPrimitive__namespace.Trigger,
1787
2267
  {
1788
2268
  ref,
@@ -1802,34 +2282,7 @@ var Tab = react.forwardRef(function Tab2({ className, style, children, value, ..
1802
2282
  ...style
1803
2283
  },
1804
2284
  ...rest,
1805
- children: [
1806
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", zIndex: 1 }, children }),
1807
- active ? /* @__PURE__ */ jsxRuntime.jsx(
1808
- "span",
1809
- {
1810
- style: {
1811
- position: "absolute",
1812
- left: 8,
1813
- right: 8,
1814
- bottom: 2,
1815
- height: 10,
1816
- pointerEvents: "none"
1817
- },
1818
- children: /* @__PURE__ */ jsxRuntime.jsx(
1819
- RoughSvg,
1820
- {
1821
- shape: "line",
1822
- roughness: (sketch.roughness ?? 1.5) + 0.35,
1823
- seed: deriveSeed(sketch.resolvedSeed, value),
1824
- sketchColor: SKETCH_COLORS.accent,
1825
- bowing: sketch.bowing ?? 1.8,
1826
- strokeWidth: (sketch.strokeWidth ?? 1.75) + 0.4,
1827
- inset: 2
1828
- }
1829
- )
1830
- }
1831
- ) : null
1832
- ]
2285
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", zIndex: 1 }, children })
1833
2286
  }
1834
2287
  );
1835
2288
  });
@@ -1864,6 +2317,46 @@ function useTableSketch() {
1864
2317
  }
1865
2318
  return ctx;
1866
2319
  }
2320
+ function TableRule({
2321
+ line,
2322
+ headerUnderline,
2323
+ roughness,
2324
+ resolvedSeed,
2325
+ ink,
2326
+ bowing,
2327
+ strokeWidth,
2328
+ shouldAnimate
2329
+ }) {
2330
+ const ref = react.useRef(null);
2331
+ const delay = shouldAnimate ? Math.min(line.index, 6) * TABLE_STAGGER_MS : 0;
2332
+ useDrawIn(ref, DRAW_IN_DURATION_MS, shouldAnimate, void 0, delay);
2333
+ if (line.header && !headerUnderline) return null;
2334
+ return /* @__PURE__ */ jsxRuntime.jsx(
2335
+ "div",
2336
+ {
2337
+ ref,
2338
+ style: {
2339
+ position: "absolute",
2340
+ left: 0,
2341
+ width: line.width,
2342
+ top: line.y - 6,
2343
+ height: 12
2344
+ },
2345
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2346
+ RoughSvg,
2347
+ {
2348
+ shape: "line",
2349
+ roughness: line.header ? (roughness ?? 1.5) + 0.2 : roughness ?? 1.7,
2350
+ seed: deriveSeed(resolvedSeed, line.key),
2351
+ sketchColor: ink,
2352
+ bowing: bowing ?? (line.header ? 1.4 : 2),
2353
+ strokeWidth: line.header ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth ?? 1.35,
2354
+ inset: 6
2355
+ }
2356
+ )
2357
+ }
2358
+ );
2359
+ }
1867
2360
  var Table = react.forwardRef(function Table2({
1868
2361
  className,
1869
2362
  style,
@@ -1875,6 +2368,7 @@ var Table = react.forwardRef(function Table2({
1875
2368
  bowing,
1876
2369
  fillStyle,
1877
2370
  strokeWidth,
2371
+ animate,
1878
2372
  ...rest
1879
2373
  }, ref) {
1880
2374
  const wrapperRef = react.useRef(null);
@@ -1882,6 +2376,7 @@ var Table = react.forwardRef(function Table2({
1882
2376
  const [lines, setLines] = react.useState([]);
1883
2377
  const resolvedSeed = useResolvedSeed(seed);
1884
2378
  const ink = sketchColor ?? SKETCH_COLORS.ink;
2379
+ const shouldAnimate = useAnimate(animate);
1885
2380
  react.useLayoutEffect(() => {
1886
2381
  const wrapper = wrapperRef.current;
1887
2382
  const table = tableRef.current;
@@ -1897,7 +2392,8 @@ var Table = react.forwardRef(function Table2({
1897
2392
  y: row.offsetTop + row.offsetHeight,
1898
2393
  width: table.offsetWidth,
1899
2394
  key: isHeader ? `header-${index}` : `row-${index}`,
1900
- header: Boolean(isHeader)
2395
+ header: Boolean(isHeader),
2396
+ index
1901
2397
  });
1902
2398
  });
1903
2399
  setLines(next);
@@ -1919,7 +2415,8 @@ var Table = react.forwardRef(function Table2({
1919
2415
  fillStyle,
1920
2416
  strokeWidth,
1921
2417
  resolvedSeed,
1922
- ink
2418
+ ink,
2419
+ shouldAnimate
1923
2420
  },
1924
2421
  children: /* @__PURE__ */ jsxRuntime.jsxs(
1925
2422
  "div",
@@ -1939,34 +2436,20 @@ var Table = react.forwardRef(function Table2({
1939
2436
  zIndex: 1,
1940
2437
  overflow: "visible"
1941
2438
  },
1942
- children: lines.map((line) => {
1943
- if (line.header && !headerUnderline) return null;
1944
- return /* @__PURE__ */ jsxRuntime.jsx(
1945
- "div",
1946
- {
1947
- style: {
1948
- position: "absolute",
1949
- left: 0,
1950
- width: line.width,
1951
- top: line.y - 6,
1952
- height: 12
1953
- },
1954
- children: /* @__PURE__ */ jsxRuntime.jsx(
1955
- RoughSvg,
1956
- {
1957
- shape: "line",
1958
- roughness: line.header ? (roughness ?? 1.5) + 0.2 : roughness ?? 1.7,
1959
- seed: deriveSeed(resolvedSeed, line.key),
1960
- sketchColor: ink,
1961
- bowing: bowing ?? (line.header ? 1.4 : 2),
1962
- strokeWidth: line.header ? (strokeWidth ?? 1.75) + 0.35 : strokeWidth ?? 1.35,
1963
- inset: 6
1964
- }
1965
- )
1966
- },
1967
- line.key
1968
- );
1969
- })
2439
+ children: lines.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
2440
+ TableRule,
2441
+ {
2442
+ line,
2443
+ headerUnderline,
2444
+ roughness,
2445
+ resolvedSeed,
2446
+ ink,
2447
+ bowing,
2448
+ strokeWidth,
2449
+ shouldAnimate
2450
+ },
2451
+ line.key
2452
+ ))
1970
2453
  }
1971
2454
  ),
1972
2455
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1974,8 +2457,7 @@ var Table = react.forwardRef(function Table2({
1974
2457
  {
1975
2458
  ref: (node) => {
1976
2459
  tableRef.current = node;
1977
- if (typeof ref === "function") ref(node);
1978
- else if (ref) ref.current = node;
2460
+ assignRef(ref, node);
1979
2461
  },
1980
2462
  style: {
1981
2463
  width: "100%",
@@ -2137,84 +2619,89 @@ function Toast({
2137
2619
  sketchColor,
2138
2620
  bowing,
2139
2621
  fillStyle,
2140
- strokeWidth
2622
+ strokeWidth,
2623
+ animate
2141
2624
  }) {
2142
2625
  const position = react.useContext(ToastPositionContext);
2143
2626
  const fromTop = position.startsWith("top");
2144
- const [entered, setEntered] = react.useState(false);
2627
+ const [uncontrolled, setUncontrolled] = react.useState(defaultOpen ?? false);
2628
+ const isOpen = open ?? uncontrolled;
2629
+ const shouldAnimate = useAnimate(animate);
2145
2630
  const color = sketchColor ?? VARIANT_COLOR2[variant];
2146
- const visible = open ?? true;
2147
- react.useLayoutEffect(() => {
2148
- if (!visible) {
2149
- setEntered(false);
2150
- return;
2151
- }
2152
- setEntered(false);
2153
- const frame = requestAnimationFrame(() => {
2154
- requestAnimationFrame(() => setEntered(true));
2155
- });
2156
- return () => cancelAnimationFrame(frame);
2157
- }, [visible]);
2158
- return /* @__PURE__ */ jsxRuntime.jsx(
2631
+ const offset = fromTop ? -14 : 14;
2632
+ function handleOpenChange(next) {
2633
+ setUncontrolled(next);
2634
+ onOpenChange?.(next);
2635
+ }
2636
+ return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isOpen ? /* @__PURE__ */ jsxRuntime.jsx(
2159
2637
  ToastPrimitive__namespace.Root,
2160
2638
  {
2161
- open,
2162
- defaultOpen,
2163
- onOpenChange,
2639
+ open: isOpen,
2640
+ onOpenChange: handleOpenChange,
2164
2641
  duration,
2165
- className: cn(className),
2166
- style: {
2167
- transform: entered ? "translateY(0)" : `translateY(${fromTop ? "-10px" : "10px"})`,
2168
- opacity: entered ? 1 : 0,
2169
- transition: "transform 200ms ease, opacity 200ms ease",
2170
- outline: "none",
2171
- ...style
2172
- },
2173
- children: /* @__PURE__ */ jsxRuntime.jsxs(
2174
- SketchBox,
2642
+ forceMount: true,
2643
+ asChild: true,
2644
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2645
+ framerMotion.motion.li,
2175
2646
  {
2176
- roughness,
2177
- seed,
2178
- sketchColor: color,
2179
- bowing,
2180
- fillStyle: fillStyle ?? "hachure",
2181
- fill: VARIANT_FILL2[variant],
2182
- strokeWidth: strokeWidth ?? 1.7,
2183
- shadow: true,
2184
- contentStyle: { padding: "12px 16px" },
2185
- children: [
2186
- title ? /* @__PURE__ */ jsxRuntime.jsx(
2187
- ToastPrimitive__namespace.Title,
2188
- {
2189
- style: {
2190
- margin: 0,
2191
- fontWeight: doodleUiFontWeight(700),
2192
- fontSize: 15,
2193
- fontFamily: doodleUiFontFamily,
2194
- color,
2195
- marginBottom: children ? 4 : 0
2196
- },
2197
- children: title
2198
- }
2199
- ) : null,
2200
- children ? /* @__PURE__ */ jsxRuntime.jsx(
2201
- ToastPrimitive__namespace.Description,
2202
- {
2203
- style: {
2204
- margin: 0,
2205
- fontSize: 14,
2206
- lineHeight: 1.5,
2207
- fontFamily: doodleUiFontFamily,
2208
- color: SKETCH_COLORS.ink
2209
- },
2210
- children
2211
- }
2212
- ) : null
2213
- ]
2647
+ className: cn(className),
2648
+ initial: shouldAnimate ? { y: offset, opacity: 0, rotate: -1.4 } : false,
2649
+ animate: shouldAnimate ? { y: 0, opacity: 1, rotate: [-1.2, 0.9, -0.35, 0] } : { y: 0, opacity: 1, rotate: 0 },
2650
+ exit: shouldAnimate ? { y: offset, opacity: 0, rotate: 1.2 } : { opacity: 0 },
2651
+ transition: shouldAnimate ? { duration: 0.38, ease: "easeOut" } : { duration: 0 },
2652
+ style: {
2653
+ listStyle: "none",
2654
+ outline: "none",
2655
+ ...style
2656
+ },
2657
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2658
+ SketchBox,
2659
+ {
2660
+ roughness,
2661
+ seed,
2662
+ sketchColor: color,
2663
+ bowing,
2664
+ fillStyle: fillStyle ?? "hachure",
2665
+ fill: VARIANT_FILL2[variant],
2666
+ strokeWidth: strokeWidth ?? 1.7,
2667
+ shadow: true,
2668
+ animate,
2669
+ contentStyle: { padding: "12px 16px" },
2670
+ children: [
2671
+ title ? /* @__PURE__ */ jsxRuntime.jsx(
2672
+ ToastPrimitive__namespace.Title,
2673
+ {
2674
+ style: {
2675
+ margin: 0,
2676
+ fontWeight: doodleUiFontWeight(700),
2677
+ fontSize: 15,
2678
+ fontFamily: doodleUiFontFamily,
2679
+ color,
2680
+ marginBottom: children ? 4 : 0
2681
+ },
2682
+ children: title
2683
+ }
2684
+ ) : null,
2685
+ children ? /* @__PURE__ */ jsxRuntime.jsx(
2686
+ ToastPrimitive__namespace.Description,
2687
+ {
2688
+ style: {
2689
+ margin: 0,
2690
+ fontSize: 14,
2691
+ lineHeight: 1.5,
2692
+ fontFamily: doodleUiFontFamily,
2693
+ color: SKETCH_COLORS.ink
2694
+ },
2695
+ children
2696
+ }
2697
+ ) : null
2698
+ ]
2699
+ }
2700
+ )
2214
2701
  }
2215
2702
  )
2216
2703
  }
2217
- );
2704
+ ) : null });
2218
2705
  }
2219
2706
  var ToastRoot = ToastPrimitive__namespace.Root;
2220
2707
  var ToastTitle = ToastPrimitive__namespace.Title;
@@ -3262,7 +3749,12 @@ exports.DEFAULT_BOWING = DEFAULT_BOWING;
3262
3749
  exports.DEFAULT_INK = DEFAULT_INK;
3263
3750
  exports.DEFAULT_ROUGHNESS = DEFAULT_ROUGHNESS;
3264
3751
  exports.DEFAULT_STROKE_WIDTH = DEFAULT_STROKE_WIDTH;
3752
+ exports.DRAW_IN_ALERT_MS = DRAW_IN_ALERT_MS;
3753
+ exports.DRAW_IN_DURATION_MS = DRAW_IN_DURATION_MS;
3754
+ exports.DRAW_IN_MARK_MS = DRAW_IN_MARK_MS;
3755
+ exports.DRAW_IN_TOOLTIP_MS = DRAW_IN_TOOLTIP_MS;
3265
3756
  exports.Divider = Divider;
3757
+ exports.DoodleUIProvider = DoodleUIProvider;
3266
3758
  exports.Input = Input;
3267
3759
  exports.Modal = Modal;
3268
3760
  exports.ModalClose = ModalClose;
@@ -3291,6 +3783,7 @@ exports.SketchSeedProvider = SketchSeedProvider;
3291
3783
  exports.Slider = Slider;
3292
3784
  exports.Stepper = Stepper;
3293
3785
  exports.Switch = Switch;
3786
+ exports.TABLE_STAGGER_MS = TABLE_STAGGER_MS;
3294
3787
  exports.Tab = Tab;
3295
3788
  exports.TabList = TabList;
3296
3789
  exports.TabPanel = TabPanel;
@@ -3314,6 +3807,9 @@ exports.Tooltip = Tooltip;
3314
3807
  exports.TooltipProvider = TooltipProvider;
3315
3808
  exports.deriveSeed = deriveSeed;
3316
3809
  exports.toRoughOptions = toRoughOptions;
3810
+ exports.useAnimate = useAnimate;
3811
+ exports.useDoodleUI = useDoodleUI;
3812
+ exports.useDrawIn = useDrawIn;
3317
3813
  exports.useResolvedSeed = useResolvedSeed;
3318
3814
  exports.useSketchSeed = useSketchSeed;
3319
3815
  //# sourceMappingURL=index.cjs.map