dialkit 1.3.0 → 1.4.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/README.md +433 -3
- package/dist/icons.d.ts +4 -1
- package/dist/icons.js +13 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +3118 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +274 -6
- package/dist/index.d.ts +274 -6
- package/dist/index.js +3050 -417
- package/dist/index.js.map +1 -1
- package/dist/solid/index.d.ts +225 -3
- package/dist/solid/index.js +5697 -2508
- package/dist/solid/index.js.map +1 -1
- package/dist/store/index.cjs +57 -16
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +14 -2
- package/dist/store/index.d.ts +14 -2
- package/dist/store/index.js +52 -16
- package/dist/store/index.js.map +1 -1
- package/dist/styles.css +704 -0
- package/dist/svelte/components/ControlRenderer.svelte +5 -2
- package/dist/svelte/components/ControlRenderer.svelte.d.ts +2 -0
- package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/DialRoot.svelte +43 -6
- package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Panel.svelte +7 -1
- package/dist/svelte/components/Panel.svelte.d.ts +2 -0
- package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Timeline/ClipPopover.svelte +206 -0
- package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts +26 -0
- package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte +76 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts +13 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte +233 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts +24 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte +756 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts +12 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte +25 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts +4 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TransitionControl.svelte +26 -11
- package/dist/svelte/components/TransitionControl.svelte.d.ts +9 -0
- package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -1
- package/dist/svelte/createDialTimeline.svelte.d.ts +4 -0
- package/dist/svelte/createDialTimeline.svelte.d.ts.map +1 -0
- package/dist/svelte/createDialTimeline.svelte.js +73 -0
- package/dist/svelte/index.d.ts +4 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +3 -0
- package/dist/svelte/theme-css.d.ts +1 -1
- package/dist/svelte/theme-css.d.ts.map +1 -1
- package/dist/svelte/theme-css.js +704 -0
- package/dist/timeline/index.cjs +1288 -0
- package/dist/timeline/index.cjs.map +1 -0
- package/dist/timeline/index.d.cts +443 -0
- package/dist/timeline/index.d.ts +443 -0
- package/dist/timeline/index.js +1233 -0
- package/dist/timeline/index.js.map +1 -0
- package/dist/vue/index.d.ts +273 -7
- package/dist/vue/index.js +2867 -361
- package/dist/vue/index.js.map +1 -1
- package/package.json +23 -13
- package/dist/solid/index.cjs +0 -3536
- package/dist/solid/index.cjs.map +0 -1
- package/dist/solid/index.d.cts +0 -295
- package/dist/vue/index.cjs +0 -3497
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.cts +0 -722
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/hooks/useDialKit.ts
|
|
4
|
-
import { useCallback, useEffect
|
|
4
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/store/DialStore.ts
|
|
7
7
|
var EMPTY_VALUES = Object.freeze({});
|
|
@@ -71,6 +71,19 @@ function isSpringConfigValue(value) {
|
|
|
71
71
|
function isEasingConfigValue(value) {
|
|
72
72
|
return hasType(value, "easing");
|
|
73
73
|
}
|
|
74
|
+
function isHexColor(value) {
|
|
75
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value);
|
|
76
|
+
}
|
|
77
|
+
function formatLabel(key) {
|
|
78
|
+
return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
79
|
+
}
|
|
80
|
+
function inferStep(min, max) {
|
|
81
|
+
const range = max - min;
|
|
82
|
+
if (range <= 1) return 0.01;
|
|
83
|
+
if (range <= 10) return 0.1;
|
|
84
|
+
if (range <= 100) return 1;
|
|
85
|
+
return 10;
|
|
86
|
+
}
|
|
74
87
|
function isActionConfigValue(value) {
|
|
75
88
|
return hasType(value, "action");
|
|
76
89
|
}
|
|
@@ -91,6 +104,9 @@ function getFirstOptionValue(options) {
|
|
|
91
104
|
var DialStoreClass = class {
|
|
92
105
|
constructor() {
|
|
93
106
|
this.panels = /* @__PURE__ */ new Map();
|
|
107
|
+
this.panelsSnapshot = [];
|
|
108
|
+
this.standardPanelsSnapshot = [];
|
|
109
|
+
this.timelinePanelsSnapshot = [];
|
|
94
110
|
this.listeners = /* @__PURE__ */ new Map();
|
|
95
111
|
this.globalListeners = /* @__PURE__ */ new Set();
|
|
96
112
|
this.snapshots = /* @__PURE__ */ new Map();
|
|
@@ -104,6 +120,12 @@ var DialStoreClass = class {
|
|
|
104
120
|
this.persistConfigs = /* @__PURE__ */ new Map();
|
|
105
121
|
}
|
|
106
122
|
registerPanel(id, name, config, shortcuts, options = {}) {
|
|
123
|
+
const existingPanel = this.panels.get(id);
|
|
124
|
+
if (existingPanel && existingPanel.kind !== options.kind) {
|
|
125
|
+
console.warn(
|
|
126
|
+
`[dialkit] Panel id "${id}" cannot be shared by a timeline and a standard panel; the most recent registration controls where it renders.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
107
129
|
this.configurePanelRetention(id, options);
|
|
108
130
|
this.registrationCounts.set(id, (this.registrationCounts.get(id) ?? 0) + 1);
|
|
109
131
|
const controls = this.parseConfig(config, "", shortcuts);
|
|
@@ -115,7 +137,7 @@ var DialStoreClass = class {
|
|
|
115
137
|
const values = this.reconcileValues(defaultValues, previousValues, controlsByPath);
|
|
116
138
|
const previousBaseValues = this.baseValues.get(id) ?? persisted?.baseValues ?? persisted?.values ?? {};
|
|
117
139
|
const baseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
|
|
118
|
-
this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {} });
|
|
140
|
+
this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {}, kind: options.kind });
|
|
119
141
|
this.snapshots.set(id, { ...values });
|
|
120
142
|
this.baseValues.set(id, baseValues);
|
|
121
143
|
this.defaultValues.set(id, { ...defaultValues });
|
|
@@ -142,7 +164,7 @@ var DialStoreClass = class {
|
|
|
142
164
|
const defaultValues = this.flattenValues(config, "");
|
|
143
165
|
this.initTransitionModes(config, "", defaultValues);
|
|
144
166
|
const nextValues = this.reconcileValues(defaultValues, existing.values, controlsByPath);
|
|
145
|
-
const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
|
|
167
|
+
const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts, kind: options.kind ?? existing.kind };
|
|
146
168
|
this.panels.set(id, nextPanel);
|
|
147
169
|
this.snapshots.set(id, { ...nextValues });
|
|
148
170
|
const previousBaseValues = this.baseValues.get(id) ?? {};
|
|
@@ -167,8 +189,8 @@ var DialStoreClass = class {
|
|
|
167
189
|
}
|
|
168
190
|
this.registrationCounts.delete(id);
|
|
169
191
|
this.panels.delete(id);
|
|
170
|
-
this.listeners.delete(id);
|
|
171
|
-
this.actionListeners.delete(id);
|
|
192
|
+
if (this.listeners.get(id)?.size === 0) this.listeners.delete(id);
|
|
193
|
+
if (this.actionListeners.get(id)?.size === 0) this.actionListeners.delete(id);
|
|
172
194
|
if (!this.retainedPanels.has(id)) {
|
|
173
195
|
this.snapshots.delete(id);
|
|
174
196
|
this.baseValues.delete(id);
|
|
@@ -260,8 +282,10 @@ var DialStoreClass = class {
|
|
|
260
282
|
getValues(panelId) {
|
|
261
283
|
return this.snapshots.get(panelId) ?? EMPTY_VALUES;
|
|
262
284
|
}
|
|
263
|
-
getPanels() {
|
|
264
|
-
return
|
|
285
|
+
getPanels(kind) {
|
|
286
|
+
if (kind === "panel") return this.standardPanelsSnapshot;
|
|
287
|
+
if (kind === "timeline") return this.timelinePanelsSnapshot;
|
|
288
|
+
return this.panelsSnapshot;
|
|
265
289
|
}
|
|
266
290
|
getPanel(id) {
|
|
267
291
|
return this.panels.get(id);
|
|
@@ -272,7 +296,11 @@ var DialStoreClass = class {
|
|
|
272
296
|
}
|
|
273
297
|
this.listeners.get(panelId).add(listener);
|
|
274
298
|
return () => {
|
|
275
|
-
this.listeners.get(panelId)
|
|
299
|
+
const listeners = this.listeners.get(panelId);
|
|
300
|
+
listeners?.delete(listener);
|
|
301
|
+
if (listeners?.size === 0 && !this.panels.has(panelId)) {
|
|
302
|
+
this.listeners.delete(panelId);
|
|
303
|
+
}
|
|
276
304
|
};
|
|
277
305
|
}
|
|
278
306
|
subscribeGlobal(listener) {
|
|
@@ -285,7 +313,11 @@ var DialStoreClass = class {
|
|
|
285
313
|
}
|
|
286
314
|
this.actionListeners.get(panelId).add(listener);
|
|
287
315
|
return () => {
|
|
288
|
-
this.actionListeners.get(panelId)
|
|
316
|
+
const listeners = this.actionListeners.get(panelId);
|
|
317
|
+
listeners?.delete(listener);
|
|
318
|
+
if (listeners?.size === 0 && !this.panels.has(panelId)) {
|
|
319
|
+
this.actionListeners.delete(panelId);
|
|
320
|
+
}
|
|
289
321
|
};
|
|
290
322
|
}
|
|
291
323
|
triggerAction(panelId, path) {
|
|
@@ -480,6 +512,9 @@ var DialStoreClass = class {
|
|
|
480
512
|
this.listeners.get(panelId)?.forEach((fn) => fn());
|
|
481
513
|
}
|
|
482
514
|
notifyGlobal() {
|
|
515
|
+
this.panelsSnapshot = Array.from(this.panels.values());
|
|
516
|
+
this.standardPanelsSnapshot = this.panelsSnapshot.filter((panel) => panel.kind !== "timeline");
|
|
517
|
+
this.timelinePanelsSnapshot = this.panelsSnapshot.filter((panel) => panel.kind === "timeline");
|
|
483
518
|
this.globalListeners.forEach((fn) => fn());
|
|
484
519
|
}
|
|
485
520
|
initTransitionModes(config, prefix, values) {
|
|
@@ -595,10 +630,10 @@ var DialStoreClass = class {
|
|
|
595
630
|
return typeof value === "object" && value !== null && "type" in value && value.type === "text";
|
|
596
631
|
}
|
|
597
632
|
isHexColor(value) {
|
|
598
|
-
return
|
|
633
|
+
return isHexColor(value);
|
|
599
634
|
}
|
|
600
635
|
formatLabel(key) {
|
|
601
|
-
return key
|
|
636
|
+
return formatLabel(key);
|
|
602
637
|
}
|
|
603
638
|
inferRange(value) {
|
|
604
639
|
if (value >= 0 && value <= 1) {
|
|
@@ -614,11 +649,7 @@ var DialStoreClass = class {
|
|
|
614
649
|
}
|
|
615
650
|
}
|
|
616
651
|
inferStep(min, max) {
|
|
617
|
-
|
|
618
|
-
if (range <= 1) return 0.01;
|
|
619
|
-
if (range <= 10) return 0.1;
|
|
620
|
-
if (range <= 100) return 1;
|
|
621
|
-
return 10;
|
|
652
|
+
return inferStep(min, max);
|
|
622
653
|
}
|
|
623
654
|
normalizePreservedValue(existingValue, defaultValue, control) {
|
|
624
655
|
if (existingValue === void 0 || !control) {
|
|
@@ -690,31 +721,33 @@ var DialStoreClass = class {
|
|
|
690
721
|
return map;
|
|
691
722
|
}
|
|
692
723
|
};
|
|
693
|
-
var DialStore = new DialStoreClass();
|
|
724
|
+
var DialStore = /* @__PURE__ */ new DialStoreClass();
|
|
694
725
|
|
|
695
|
-
// src/hooks/
|
|
696
|
-
|
|
697
|
-
|
|
726
|
+
// src/hooks/useDialStorePanel.ts
|
|
727
|
+
import { useCallback, useEffect, useId, useRef, useSyncExternalStore } from "react";
|
|
728
|
+
function useSerialized(value) {
|
|
729
|
+
const ref = useRef();
|
|
730
|
+
if (!ref.current || !Object.is(ref.current.value, value)) {
|
|
731
|
+
ref.current = { value, text: JSON.stringify(value) };
|
|
732
|
+
}
|
|
733
|
+
return ref.current.text;
|
|
698
734
|
}
|
|
699
|
-
function
|
|
735
|
+
function useDialStorePanel(name, config, options = {}) {
|
|
700
736
|
const instanceId = useId();
|
|
701
|
-
const hasStableId = options
|
|
702
|
-
const panelId = options
|
|
737
|
+
const hasStableId = options.id !== void 0;
|
|
738
|
+
const panelId = options.id ?? `${name}-${instanceId}`;
|
|
703
739
|
const configRef = useRef(config);
|
|
704
|
-
const serializedConfig = JSON.stringify(config);
|
|
705
740
|
configRef.current = config;
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
const
|
|
711
|
-
persistRef.current = options?.persist;
|
|
712
|
-
const serializedShortcuts = JSON.stringify(options?.shortcuts);
|
|
713
|
-
const serializedPersist = JSON.stringify(options?.persist);
|
|
741
|
+
const optionsRef = useRef(options);
|
|
742
|
+
optionsRef.current = options;
|
|
743
|
+
const serializedConfig = useSerialized(config);
|
|
744
|
+
const serializedShortcuts = useSerialized(options.shortcuts);
|
|
745
|
+
const serializedPersist = useSerialized(options.persist);
|
|
714
746
|
useEffect(() => {
|
|
715
|
-
DialStore.registerPanel(panelId, name, configRef.current,
|
|
747
|
+
DialStore.registerPanel(panelId, name, configRef.current, optionsRef.current.shortcuts, {
|
|
716
748
|
retainOnUnmount: hasStableId,
|
|
717
|
-
persist:
|
|
749
|
+
persist: optionsRef.current.persist,
|
|
750
|
+
kind: optionsRef.current.kind
|
|
718
751
|
});
|
|
719
752
|
return () => DialStore.unregisterPanel(panelId);
|
|
720
753
|
}, [hasStableId, panelId, name]);
|
|
@@ -724,45 +757,61 @@ function useDialKitController(name, config, options) {
|
|
|
724
757
|
mountedRef.current = true;
|
|
725
758
|
return;
|
|
726
759
|
}
|
|
727
|
-
DialStore.updatePanel(panelId, name, configRef.current,
|
|
760
|
+
DialStore.updatePanel(panelId, name, configRef.current, optionsRef.current.shortcuts, {
|
|
728
761
|
retainOnUnmount: hasStableId,
|
|
729
|
-
persist:
|
|
762
|
+
persist: optionsRef.current.persist,
|
|
763
|
+
kind: optionsRef.current.kind
|
|
730
764
|
});
|
|
731
765
|
}, [hasStableId, panelId, name, serializedConfig, serializedShortcuts, serializedPersist]);
|
|
732
|
-
useEffect(() => {
|
|
733
|
-
return DialStore.subscribeActions(panelId, (action) => {
|
|
734
|
-
onActionRef.current?.(action);
|
|
735
|
-
});
|
|
736
|
-
}, [panelId]);
|
|
737
766
|
const subscribe = useCallback(
|
|
738
767
|
(callback) => DialStore.subscribe(panelId, callback),
|
|
739
768
|
[panelId]
|
|
740
769
|
);
|
|
741
|
-
const getSnapshot = useCallback(
|
|
742
|
-
() => DialStore.getValues(panelId),
|
|
743
|
-
[panelId]
|
|
744
|
-
);
|
|
770
|
+
const getSnapshot = useCallback(() => DialStore.getValues(panelId), [panelId]);
|
|
745
771
|
const flatValues = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
772
|
+
return { panelId, flatValues, serializedConfig };
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/hooks/useDialKit.ts
|
|
776
|
+
function useDialKit(name, config, options) {
|
|
777
|
+
return useDialKitController(name, config, options).values;
|
|
778
|
+
}
|
|
779
|
+
function useDialKitController(name, config, options) {
|
|
780
|
+
const { panelId, flatValues, serializedConfig } = useDialStorePanel(name, config, {
|
|
781
|
+
id: options?.id,
|
|
782
|
+
persist: options?.persist,
|
|
783
|
+
shortcuts: options?.shortcuts
|
|
784
|
+
});
|
|
785
|
+
const configRef = useRef2(config);
|
|
786
|
+
configRef.current = config;
|
|
787
|
+
const onActionRef = useRef2(options?.onAction);
|
|
788
|
+
onActionRef.current = options?.onAction;
|
|
789
|
+
useEffect2(() => {
|
|
790
|
+
return DialStore.subscribeActions(panelId, (action) => {
|
|
791
|
+
onActionRef.current?.(action);
|
|
792
|
+
});
|
|
793
|
+
}, [panelId]);
|
|
746
794
|
const values = useMemo(
|
|
747
795
|
() => resolveDialValues(configRef.current, flatValues),
|
|
796
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
748
797
|
[flatValues, serializedConfig]
|
|
749
798
|
);
|
|
750
|
-
const setValue =
|
|
799
|
+
const setValue = useCallback2(
|
|
751
800
|
(path, value) => {
|
|
752
801
|
DialStore.updateValue(panelId, path, value);
|
|
753
802
|
},
|
|
754
803
|
[panelId]
|
|
755
804
|
);
|
|
756
|
-
const setValues =
|
|
805
|
+
const setValues = useCallback2(
|
|
757
806
|
(nextValues) => {
|
|
758
807
|
DialStore.updateValues(panelId, flattenDialValueUpdates(configRef.current, nextValues));
|
|
759
808
|
},
|
|
760
809
|
[panelId]
|
|
761
810
|
);
|
|
762
|
-
const resetValues =
|
|
811
|
+
const resetValues = useCallback2(() => {
|
|
763
812
|
DialStore.resetValues(panelId);
|
|
764
813
|
}, [panelId]);
|
|
765
|
-
const getValues =
|
|
814
|
+
const getValues = useCallback2(
|
|
766
815
|
() => resolveDialValues(configRef.current, DialStore.getValues(panelId)),
|
|
767
816
|
[panelId]
|
|
768
817
|
);
|
|
@@ -779,16 +828,221 @@ function useDialKitController(name, config, options) {
|
|
|
779
828
|
}
|
|
780
829
|
|
|
781
830
|
// src/components/DialRoot.tsx
|
|
782
|
-
import { useEffect as
|
|
831
|
+
import { useEffect as useEffect9, useState as useState10, useRef as useRef12, useCallback as useCallback12 } from "react";
|
|
783
832
|
import { createPortal as createPortal3 } from "react-dom";
|
|
784
833
|
|
|
834
|
+
// src/store/TimelineStore.ts
|
|
835
|
+
function loopSpan(duration, loopStart) {
|
|
836
|
+
if (!Number.isFinite(duration) || duration <= 0) return 0;
|
|
837
|
+
if (!Number.isFinite(loopStart)) loopStart = 0;
|
|
838
|
+
const start = Math.min(Math.max(0, loopStart), duration);
|
|
839
|
+
return duration - start > 0 ? duration - start : duration;
|
|
840
|
+
}
|
|
841
|
+
function foldLoopTime(time, duration, loopStart = 0) {
|
|
842
|
+
if (!Number.isFinite(time) || !Number.isFinite(duration) || duration <= 0) {
|
|
843
|
+
return { time: 0, wraps: 0 };
|
|
844
|
+
}
|
|
845
|
+
if (time < duration) return { time, wraps: 0 };
|
|
846
|
+
const span = loopSpan(duration, loopStart);
|
|
847
|
+
const base = duration - span;
|
|
848
|
+
const over = time - base;
|
|
849
|
+
return { time: base + over % span, wraps: Math.floor(over / span) };
|
|
850
|
+
}
|
|
851
|
+
var TIMELINE_CLIP_COLORS = [
|
|
852
|
+
"#E8E8E8"
|
|
853
|
+
// neutral white — slightly off-white so the pure-white selection ring still reads
|
|
854
|
+
];
|
|
855
|
+
var EMPTY_TRANSPORT = Object.freeze({ time: 0, playing: false, duration: 0, wraps: 0 });
|
|
856
|
+
var TimelineStoreClass = class {
|
|
857
|
+
constructor() {
|
|
858
|
+
this.timelines = /* @__PURE__ */ new Map();
|
|
859
|
+
this.transports = /* @__PURE__ */ new Map();
|
|
860
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
861
|
+
this.globalListeners = /* @__PURE__ */ new Set();
|
|
862
|
+
this.registrationCounts = /* @__PURE__ */ new Map();
|
|
863
|
+
this.listCache = null;
|
|
864
|
+
this.rafId = null;
|
|
865
|
+
this.lastTick = 0;
|
|
866
|
+
this.tick = (now) => {
|
|
867
|
+
const dt = Math.max(0, (now - this.lastTick) / 1e3);
|
|
868
|
+
this.lastTick = now;
|
|
869
|
+
let anyPlaying = false;
|
|
870
|
+
for (const [id, transport] of this.transports) {
|
|
871
|
+
if (!transport.playing) continue;
|
|
872
|
+
const meta = this.timelines.get(id);
|
|
873
|
+
const duration = meta?.duration ?? transport.duration;
|
|
874
|
+
if (!Number.isFinite(duration) || duration <= 0) {
|
|
875
|
+
this.transports.set(id, { time: 0, playing: false, duration: 0, wraps: 0 });
|
|
876
|
+
this.notify(id);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
let time = transport.time + dt;
|
|
880
|
+
let playing = true;
|
|
881
|
+
let wraps = transport.wraps;
|
|
882
|
+
if (time >= duration) {
|
|
883
|
+
if (meta?.loop) {
|
|
884
|
+
const folded = foldLoopTime(time, duration, meta.loopStart);
|
|
885
|
+
time = folded.time;
|
|
886
|
+
wraps += folded.wraps;
|
|
887
|
+
} else {
|
|
888
|
+
time = duration;
|
|
889
|
+
playing = false;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
this.transports.set(id, { time, playing, duration, wraps });
|
|
893
|
+
if (playing) anyPlaying = true;
|
|
894
|
+
this.notify(id);
|
|
895
|
+
}
|
|
896
|
+
this.rafId = anyPlaying ? window.requestAnimationFrame(this.tick) : null;
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
register(meta, options) {
|
|
900
|
+
const existing = this.timelines.get(meta.id);
|
|
901
|
+
if (existing && existing.name !== meta.name) {
|
|
902
|
+
console.warn(
|
|
903
|
+
`[dialkit] Timeline id "${meta.id}" is already registered by "${existing.name}"; "${meta.name}" will share and overwrite that transport.`
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
this.registrationCounts.set(meta.id, (this.registrationCounts.get(meta.id) ?? 0) + 1);
|
|
907
|
+
this.applyMeta(meta, options.autoplay);
|
|
908
|
+
}
|
|
909
|
+
update(meta) {
|
|
910
|
+
if (!this.timelines.has(meta.id)) return;
|
|
911
|
+
this.applyMeta(meta, false);
|
|
912
|
+
}
|
|
913
|
+
unregister(id) {
|
|
914
|
+
const nextCount = (this.registrationCounts.get(id) ?? 1) - 1;
|
|
915
|
+
if (nextCount > 0) {
|
|
916
|
+
this.registrationCounts.set(id, nextCount);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
this.registrationCounts.delete(id);
|
|
920
|
+
this.timelines.delete(id);
|
|
921
|
+
this.transports.delete(id);
|
|
922
|
+
if (this.listeners.get(id)?.size === 0) this.listeners.delete(id);
|
|
923
|
+
this.listCache = null;
|
|
924
|
+
this.notifyGlobal();
|
|
925
|
+
}
|
|
926
|
+
play(id) {
|
|
927
|
+
const transport = this.transports.get(id);
|
|
928
|
+
if (!transport || transport.duration <= 0 || transport.playing) return;
|
|
929
|
+
const restart = transport.time >= transport.duration;
|
|
930
|
+
this.transports.set(id, {
|
|
931
|
+
...transport,
|
|
932
|
+
time: restart ? 0 : transport.time,
|
|
933
|
+
wraps: restart ? 0 : transport.wraps,
|
|
934
|
+
playing: true
|
|
935
|
+
});
|
|
936
|
+
this.notify(id);
|
|
937
|
+
this.ensureLoop();
|
|
938
|
+
}
|
|
939
|
+
pause(id) {
|
|
940
|
+
const transport = this.transports.get(id);
|
|
941
|
+
if (!transport || !transport.playing) return;
|
|
942
|
+
this.transports.set(id, { ...transport, playing: false });
|
|
943
|
+
this.notify(id);
|
|
944
|
+
}
|
|
945
|
+
replay(id) {
|
|
946
|
+
const transport = this.transports.get(id);
|
|
947
|
+
if (!transport || transport.duration <= 0) return;
|
|
948
|
+
this.transports.set(id, { ...transport, time: 0, wraps: 0, playing: true });
|
|
949
|
+
this.notify(id);
|
|
950
|
+
this.ensureLoop();
|
|
951
|
+
}
|
|
952
|
+
seek(id, time) {
|
|
953
|
+
const transport = this.transports.get(id);
|
|
954
|
+
if (!transport || !Number.isFinite(time)) return;
|
|
955
|
+
const clamped = Math.min(transport.duration, Math.max(0, time));
|
|
956
|
+
this.transports.set(id, { ...transport, time: clamped, wraps: 0 });
|
|
957
|
+
this.notify(id);
|
|
958
|
+
}
|
|
959
|
+
getTransport(id) {
|
|
960
|
+
return this.transports.get(id) ?? EMPTY_TRANSPORT;
|
|
961
|
+
}
|
|
962
|
+
getTimeline(id) {
|
|
963
|
+
return this.timelines.get(id);
|
|
964
|
+
}
|
|
965
|
+
getTimelines() {
|
|
966
|
+
if (!this.listCache) {
|
|
967
|
+
this.listCache = Array.from(this.timelines.values());
|
|
968
|
+
}
|
|
969
|
+
return this.listCache;
|
|
970
|
+
}
|
|
971
|
+
subscribe(id, listener) {
|
|
972
|
+
if (!this.listeners.has(id)) {
|
|
973
|
+
this.listeners.set(id, /* @__PURE__ */ new Set());
|
|
974
|
+
}
|
|
975
|
+
this.listeners.get(id).add(listener);
|
|
976
|
+
return () => {
|
|
977
|
+
const listeners = this.listeners.get(id);
|
|
978
|
+
listeners?.delete(listener);
|
|
979
|
+
if (listeners?.size === 0 && !this.timelines.has(id)) {
|
|
980
|
+
this.listeners.delete(id);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
subscribeGlobal(listener) {
|
|
985
|
+
this.globalListeners.add(listener);
|
|
986
|
+
return () => {
|
|
987
|
+
this.globalListeners.delete(listener);
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
applyMeta(meta, autoplay) {
|
|
991
|
+
const duration = Number.isFinite(meta.duration) ? Math.max(0, meta.duration) : 0;
|
|
992
|
+
const loopStart = Number.isFinite(meta.loopStart) ? Math.min(duration, Math.max(0, meta.loopStart)) : 0;
|
|
993
|
+
const safeMeta = { ...meta, duration, loopStart };
|
|
994
|
+
this.timelines.set(meta.id, safeMeta);
|
|
995
|
+
const existing = this.transports.get(meta.id);
|
|
996
|
+
if (existing) {
|
|
997
|
+
this.transports.set(meta.id, {
|
|
998
|
+
time: Math.min(existing.time, duration),
|
|
999
|
+
playing: duration > 0 && existing.playing,
|
|
1000
|
+
duration,
|
|
1001
|
+
wraps: existing.wraps
|
|
1002
|
+
});
|
|
1003
|
+
} else {
|
|
1004
|
+
const playing = duration > 0 && autoplay;
|
|
1005
|
+
this.transports.set(meta.id, { time: 0, playing, duration, wraps: 0 });
|
|
1006
|
+
if (playing) this.ensureLoop();
|
|
1007
|
+
}
|
|
1008
|
+
this.listCache = null;
|
|
1009
|
+
this.notify(meta.id);
|
|
1010
|
+
this.notifyGlobal();
|
|
1011
|
+
}
|
|
1012
|
+
ensureLoop() {
|
|
1013
|
+
if (this.rafId !== null || typeof window === "undefined") return;
|
|
1014
|
+
this.lastTick = performance.now();
|
|
1015
|
+
this.rafId = window.requestAnimationFrame(this.tick);
|
|
1016
|
+
}
|
|
1017
|
+
notify(id) {
|
|
1018
|
+
this.listeners.get(id)?.forEach((fn) => fn());
|
|
1019
|
+
}
|
|
1020
|
+
notifyGlobal() {
|
|
1021
|
+
this.globalListeners.forEach((fn) => fn());
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
var TimelineStore = /* @__PURE__ */ new TimelineStoreClass();
|
|
1025
|
+
|
|
1026
|
+
// src/env.ts
|
|
1027
|
+
var isDevDefault = typeof process !== "undefined" && process?.env?.NODE_ENV ? process.env.NODE_ENV !== "production" : typeof import.meta !== "undefined" && import.meta.env?.MODE ? import.meta.env.MODE !== "production" : true;
|
|
1028
|
+
|
|
785
1029
|
// src/components/Folder.tsx
|
|
786
|
-
import { useState, useRef as
|
|
1030
|
+
import { useState, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
787
1031
|
import { motion, AnimatePresence } from "motion/react";
|
|
788
1032
|
|
|
789
1033
|
// src/icons.ts
|
|
790
1034
|
var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
|
|
791
1035
|
var ICON_CHECK = "M5 12.75L10 19L19 5";
|
|
1036
|
+
var ICON_PAUSE = [
|
|
1037
|
+
"M6.75 3C5.23122 3 4 4.23122 4 5.75V18.25C4 19.7688 5.23122 21 6.75 21H7.25C8.76878 21 10 19.7688 10 18.25V5.75C10 4.23122 8.76878 3 7.25 3H6.75Z",
|
|
1038
|
+
"M16.75 3C15.2312 3 14 4.23122 14 5.75V18.25C14 19.7688 15.2312 21 16.75 21H17.25C18.7688 21 20 19.7688 20 18.25V5.75C20 4.23122 18.7688 3 17.25 3H16.75Z"
|
|
1039
|
+
];
|
|
1040
|
+
var ICON_PLAY = "M9.24394 2.36758C7.41419 1.18362 5 2.49701 5 4.67639V19.3238C5 21.5032 7.41419 22.8166 9.24394 21.6326L20.5624 14.3089C22.2371 13.2253 22.2372 10.775 20.5624 9.69129L9.24394 2.36758Z";
|
|
1041
|
+
var ICON_TIMELINE = [
|
|
1042
|
+
"M18.868 10C20.8517 10.0003 22.2886 11.8914 21.7577 13.8027L20.369 18.8027C20.0083 20.1012 18.826 20.9999 17.4784 21H6.51941C5.17179 21 3.98948 20.1012 3.62878 18.8027L2.24011 13.8027C1.7092 11.8913 3.14603 10.0003 5.12976 10H18.868Z",
|
|
1043
|
+
"M18.9989 6.5C19.5511 6.50007 19.9989 6.94776 19.9989 7.5C19.9989 8.05224 19.5511 8.49993 18.9989 8.5H4.9989C4.44661 8.5 3.9989 8.05228 3.9989 7.5C3.9989 6.94772 4.44661 6.5 4.9989 6.5H18.9989Z",
|
|
1044
|
+
"M16.9989 3C17.5511 3.00007 17.9989 3.44776 17.9989 4C17.9989 4.55224 17.5511 4.99993 16.9989 5H6.9989C6.44661 5 5.9989 4.55228 5.9989 4C5.9989 3.44772 6.44661 3 6.9989 3H16.9989Z"
|
|
1045
|
+
];
|
|
792
1046
|
var ICON_CLIPBOARD = {
|
|
793
1047
|
board: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z",
|
|
794
1048
|
sparkle: "M19.2405 16.1852L18.5436 14.3733C18.4571 14.1484 18.241 14 18 14C17.759 14 17.5429 14.1484 17.4564 14.3733L16.7595 16.1852C16.658 16.4493 16.4493 16.658 16.1852 16.7595L14.3733 17.4564C14.1484 17.5429 14 17.759 14 18C14 18.241 14.1484 18.4571 14.3733 18.5436L16.1852 19.2405C16.4493 19.342 16.658 19.5507 16.7595 19.8148L17.4564 21.6267C17.5429 21.8516 17.759 22 18 22C18.241 22 18.4571 21.8516 18.5436 21.6267L19.2405 19.8148C19.342 19.5507 19.5507 19.342 19.8148 19.2405L21.6267 18.5436C21.8516 18.4571 22 18.241 22 18C22 17.759 21.8516 17.5429 21.6267 17.4564L19.8148 16.7595C19.5507 16.658 19.342 16.4493 19.2405 16.1852Z",
|
|
@@ -822,16 +1076,16 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
822
1076
|
function Folder({ title, children, defaultOpen = true, isRoot = false, inline = false, onOpenChange, toolbar, panelHeightOffset = 10 }) {
|
|
823
1077
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
824
1078
|
const [isCollapsed, setIsCollapsed] = useState(!defaultOpen);
|
|
825
|
-
const contentRef =
|
|
1079
|
+
const contentRef = useRef3(null);
|
|
826
1080
|
const [contentHeight, setContentHeight] = useState(void 0);
|
|
827
1081
|
const [windowHeight, setWindowHeight] = useState(typeof window !== "undefined" ? window.innerHeight : 800);
|
|
828
|
-
|
|
1082
|
+
useEffect3(() => {
|
|
829
1083
|
if (!isRoot) return;
|
|
830
1084
|
const onResize = () => setWindowHeight(window.innerHeight);
|
|
831
1085
|
window.addEventListener("resize", onResize);
|
|
832
1086
|
return () => window.removeEventListener("resize", onResize);
|
|
833
1087
|
}, [isRoot]);
|
|
834
|
-
|
|
1088
|
+
useEffect3(() => {
|
|
835
1089
|
const el = contentRef.current;
|
|
836
1090
|
if (!el) return;
|
|
837
1091
|
const ro = new ResizeObserver(() => {
|
|
@@ -932,11 +1186,43 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, inline =
|
|
|
932
1186
|
}
|
|
933
1187
|
|
|
934
1188
|
// src/components/Panel.tsx
|
|
935
|
-
import { useCallback as
|
|
1189
|
+
import { useCallback as useCallback10, useState as useState9, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
936
1190
|
import { motion as motion5, AnimatePresence as AnimatePresence4 } from "motion/react";
|
|
937
1191
|
|
|
1192
|
+
// src/copy-instruction.ts
|
|
1193
|
+
function buildCopyInstruction(hookName, panelName, values) {
|
|
1194
|
+
const jsonStr = JSON.stringify(values, null, 2);
|
|
1195
|
+
if (hookName === "useDialTimeline" || hookName === "createDialTimeline") {
|
|
1196
|
+
return `Update the ${hookName} configuration for "${panelName}" with these values:
|
|
1197
|
+
|
|
1198
|
+
\`\`\`json
|
|
1199
|
+
${jsonStr}
|
|
1200
|
+
\`\`\`
|
|
1201
|
+
|
|
1202
|
+
Apply these values as the new defaults in the ${hookName} call. Keep the existing \`clip.current\` bindings while this timeline is being authored; do not convert the animation or remove DialKit yet.
|
|
1203
|
+
|
|
1204
|
+
Add this comment immediately above the ${hookName} call as a production handoff note:
|
|
1205
|
+
|
|
1206
|
+
\`\`\`tsx
|
|
1207
|
+
// TODO(production): DialKit's clip.current values are the scrubbable authoring preview.
|
|
1208
|
+
// Replace them with equivalent real Motion animations using the tuned timeline
|
|
1209
|
+
// timings and transitions, then remove ${hookName} and <DialTimeline />.
|
|
1210
|
+
\`\`\``;
|
|
1211
|
+
}
|
|
1212
|
+
return `Update the ${hookName} configuration for "${panelName}" with these values:
|
|
1213
|
+
|
|
1214
|
+
\`\`\`json
|
|
1215
|
+
${jsonStr}
|
|
1216
|
+
\`\`\`
|
|
1217
|
+
|
|
1218
|
+
Apply these values as the new defaults in the ${hookName} call.`;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// src/components/ControlRenderer.tsx
|
|
1222
|
+
import { useContext } from "react";
|
|
1223
|
+
|
|
938
1224
|
// src/components/ShortcutListener.tsx
|
|
939
|
-
import { createContext, useEffect as
|
|
1225
|
+
import { createContext, useEffect as useEffect4, useRef as useRef4, useState as useState2, useCallback as useCallback3 } from "react";
|
|
940
1226
|
|
|
941
1227
|
// src/shortcut-utils.ts
|
|
942
1228
|
function decimalsForStep(step) {
|
|
@@ -1028,11 +1314,11 @@ import { jsx as jsx2 } from "react/jsx-runtime";
|
|
|
1028
1314
|
var ShortcutContext = createContext({ activePanelId: null, activePath: null });
|
|
1029
1315
|
function ShortcutListener({ children }) {
|
|
1030
1316
|
const [activeShortcut, setActiveShortcut] = useState2({ activePanelId: null, activePath: null });
|
|
1031
|
-
const activeKeysRef =
|
|
1032
|
-
const isDraggingRef =
|
|
1033
|
-
const lastMouseXRef =
|
|
1034
|
-
const dragAccumulatorRef =
|
|
1035
|
-
const resolveActiveTarget =
|
|
1317
|
+
const activeKeysRef = useRef4(/* @__PURE__ */ new Set());
|
|
1318
|
+
const isDraggingRef = useRef4(false);
|
|
1319
|
+
const lastMouseXRef = useRef4(null);
|
|
1320
|
+
const dragAccumulatorRef = useRef4(0);
|
|
1321
|
+
const resolveActiveTarget = useCallback3((interaction) => {
|
|
1036
1322
|
for (const key of activeKeysRef.current) {
|
|
1037
1323
|
const panels = DialStore.getPanels();
|
|
1038
1324
|
for (const panel of panels) {
|
|
@@ -1049,7 +1335,7 @@ function ShortcutListener({ children }) {
|
|
|
1049
1335
|
}
|
|
1050
1336
|
return null;
|
|
1051
1337
|
}, []);
|
|
1052
|
-
|
|
1338
|
+
useEffect4(() => {
|
|
1053
1339
|
const handleKeyDown = (e) => {
|
|
1054
1340
|
if (isInputFocused()) return;
|
|
1055
1341
|
const key = e.key.toLowerCase();
|
|
@@ -1211,7 +1497,7 @@ function ShortcutListener({ children }) {
|
|
|
1211
1497
|
}
|
|
1212
1498
|
|
|
1213
1499
|
// src/components/Slider.tsx
|
|
1214
|
-
import { useRef as
|
|
1500
|
+
import { useRef as useRef5, useState as useState3, useCallback as useCallback4, useEffect as useEffect5 } from "react";
|
|
1215
1501
|
import { motion as motion2, useMotionValue, useTransform, animate } from "motion/react";
|
|
1216
1502
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1217
1503
|
var CLICK_THRESHOLD = 3;
|
|
@@ -1229,11 +1515,11 @@ function Slider({
|
|
|
1229
1515
|
shortcut,
|
|
1230
1516
|
shortcutActive
|
|
1231
1517
|
}) {
|
|
1232
|
-
const wrapperRef =
|
|
1233
|
-
const trackRef =
|
|
1234
|
-
const inputRef =
|
|
1235
|
-
const labelRef =
|
|
1236
|
-
const valueSpanRef =
|
|
1518
|
+
const wrapperRef = useRef5(null);
|
|
1519
|
+
const trackRef = useRef5(null);
|
|
1520
|
+
const inputRef = useRef5(null);
|
|
1521
|
+
const labelRef = useRef5(null);
|
|
1522
|
+
const valueSpanRef = useRef5(null);
|
|
1237
1523
|
const [isInteracting, setIsInteracting] = useState3(false);
|
|
1238
1524
|
const [isDragging, setIsDragging] = useState3(false);
|
|
1239
1525
|
const [isHovered, setIsHovered] = useState3(false);
|
|
@@ -1241,12 +1527,12 @@ function Slider({
|
|
|
1241
1527
|
const [isValueEditable, setIsValueEditable] = useState3(false);
|
|
1242
1528
|
const [showInput, setShowInput] = useState3(false);
|
|
1243
1529
|
const [inputValue, setInputValue] = useState3("");
|
|
1244
|
-
const hoverTimeoutRef =
|
|
1245
|
-
const pointerDownPos =
|
|
1246
|
-
const isClickRef =
|
|
1247
|
-
const animRef =
|
|
1248
|
-
const wrapperRectRef =
|
|
1249
|
-
const scaleRef =
|
|
1530
|
+
const hoverTimeoutRef = useRef5(null);
|
|
1531
|
+
const pointerDownPos = useRef5(null);
|
|
1532
|
+
const isClickRef = useRef5(true);
|
|
1533
|
+
const animRef = useRef5(null);
|
|
1534
|
+
const wrapperRectRef = useRef5(null);
|
|
1535
|
+
const scaleRef = useRef5(1);
|
|
1250
1536
|
const percentage = (value - min) / (max - min) * 100;
|
|
1251
1537
|
const isActive = isInteracting || isHovered;
|
|
1252
1538
|
const fillPercent = useMotionValue(percentage);
|
|
@@ -1264,12 +1550,12 @@ function Slider({
|
|
|
1264
1550
|
rubberStretchPx,
|
|
1265
1551
|
(stretch) => stretch < 0 ? stretch : 0
|
|
1266
1552
|
);
|
|
1267
|
-
|
|
1553
|
+
useEffect5(() => {
|
|
1268
1554
|
if (!isInteracting && !animRef.current) {
|
|
1269
1555
|
fillPercent.jump(percentage);
|
|
1270
1556
|
}
|
|
1271
1557
|
}, [percentage, isInteracting, fillPercent]);
|
|
1272
|
-
const positionToValue =
|
|
1558
|
+
const positionToValue = useCallback4(
|
|
1273
1559
|
(clientX) => {
|
|
1274
1560
|
const rect = wrapperRectRef.current;
|
|
1275
1561
|
if (!rect) return value;
|
|
@@ -1282,11 +1568,11 @@ function Slider({
|
|
|
1282
1568
|
},
|
|
1283
1569
|
[min, max, value]
|
|
1284
1570
|
);
|
|
1285
|
-
const percentFromValue =
|
|
1571
|
+
const percentFromValue = useCallback4(
|
|
1286
1572
|
(v) => (v - min) / (max - min) * 100,
|
|
1287
1573
|
[min, max]
|
|
1288
1574
|
);
|
|
1289
|
-
const computeRubberStretch =
|
|
1575
|
+
const computeRubberStretch = useCallback4(
|
|
1290
1576
|
(clientX, sign) => {
|
|
1291
1577
|
const rect = wrapperRectRef.current;
|
|
1292
1578
|
if (!rect) return 0;
|
|
@@ -1296,7 +1582,7 @@ function Slider({
|
|
|
1296
1582
|
},
|
|
1297
1583
|
[]
|
|
1298
1584
|
);
|
|
1299
|
-
const handlePointerDown =
|
|
1585
|
+
const handlePointerDown = useCallback4(
|
|
1300
1586
|
(e) => {
|
|
1301
1587
|
if (showInput) return;
|
|
1302
1588
|
e.preventDefault();
|
|
@@ -1312,7 +1598,7 @@ function Slider({
|
|
|
1312
1598
|
},
|
|
1313
1599
|
[showInput]
|
|
1314
1600
|
);
|
|
1315
|
-
const handlePointerMove =
|
|
1601
|
+
const handlePointerMove = useCallback4(
|
|
1316
1602
|
(e) => {
|
|
1317
1603
|
if (!isInteracting || !pointerDownPos.current) return;
|
|
1318
1604
|
const dx = e.clientX - pointerDownPos.current.x;
|
|
@@ -1353,7 +1639,7 @@ function Slider({
|
|
|
1353
1639
|
computeRubberStretch
|
|
1354
1640
|
]
|
|
1355
1641
|
);
|
|
1356
|
-
const handlePointerUp =
|
|
1642
|
+
const handlePointerUp = useCallback4(
|
|
1357
1643
|
(e) => {
|
|
1358
1644
|
if (!isInteracting) return;
|
|
1359
1645
|
if (isClickRef.current) {
|
|
@@ -1397,7 +1683,7 @@ function Slider({
|
|
|
1397
1683
|
rubberStretchPx
|
|
1398
1684
|
]
|
|
1399
1685
|
);
|
|
1400
|
-
|
|
1686
|
+
useEffect5(() => {
|
|
1401
1687
|
if (isValueHovered && !showInput && !isValueEditable) {
|
|
1402
1688
|
hoverTimeoutRef.current = setTimeout(() => {
|
|
1403
1689
|
setIsValueEditable(true);
|
|
@@ -1415,7 +1701,7 @@ function Slider({
|
|
|
1415
1701
|
}
|
|
1416
1702
|
};
|
|
1417
1703
|
}, [isValueHovered, showInput, isValueEditable]);
|
|
1418
|
-
|
|
1704
|
+
useEffect5(() => {
|
|
1419
1705
|
if (showInput && inputRef.current) {
|
|
1420
1706
|
inputRef.current.focus();
|
|
1421
1707
|
inputRef.current.select();
|
|
@@ -1570,17 +1856,17 @@ function Slider({
|
|
|
1570
1856
|
}
|
|
1571
1857
|
|
|
1572
1858
|
// src/components/SegmentedControl.tsx
|
|
1573
|
-
import { useRef as
|
|
1859
|
+
import { useRef as useRef6, useState as useState4, useLayoutEffect, useCallback as useCallback5 } from "react";
|
|
1574
1860
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1575
1861
|
function SegmentedControl({
|
|
1576
1862
|
options,
|
|
1577
1863
|
value,
|
|
1578
1864
|
onChange
|
|
1579
1865
|
}) {
|
|
1580
|
-
const containerRef =
|
|
1581
|
-
const hasAnimated =
|
|
1866
|
+
const containerRef = useRef6(null);
|
|
1867
|
+
const hasAnimated = useRef6(false);
|
|
1582
1868
|
const [pillStyle, setPillStyle] = useState4(null);
|
|
1583
|
-
const measure =
|
|
1869
|
+
const measure = useCallback5(() => {
|
|
1584
1870
|
const container = containerRef.current;
|
|
1585
1871
|
if (!container) return;
|
|
1586
1872
|
const activeButton = container.querySelector('[data-active="true"]');
|
|
@@ -1645,47 +1931,118 @@ function Toggle({ label, checked, onChange, shortcut, shortcutActive }) {
|
|
|
1645
1931
|
] });
|
|
1646
1932
|
}
|
|
1647
1933
|
|
|
1648
|
-
// src/
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1934
|
+
// src/transition-math.ts
|
|
1935
|
+
function round2(value) {
|
|
1936
|
+
return Math.round(value * 100) / 100;
|
|
1937
|
+
}
|
|
1938
|
+
function clamp(value, min, max) {
|
|
1939
|
+
return Math.min(max, Math.max(min, value));
|
|
1940
|
+
}
|
|
1941
|
+
function isTransitionConfig(value) {
|
|
1942
|
+
return isSpringConfigValue(value) || isEasingConfigValue(value);
|
|
1943
|
+
}
|
|
1944
|
+
function isPhysicsSpring(transition) {
|
|
1945
|
+
return transition.type === "spring" && (transition.stiffness !== void 0 || transition.damping !== void 0 || transition.mass !== void 0);
|
|
1946
|
+
}
|
|
1947
|
+
function springParams(spring) {
|
|
1948
|
+
if (isPhysicsSpring(spring)) {
|
|
1949
|
+
return { stiffness: spring.stiffness ?? 200, damping: spring.damping ?? 25, mass: spring.mass ?? 1 };
|
|
1950
|
+
}
|
|
1951
|
+
const visualDuration = Math.max(0.05, spring.visualDuration ?? 0.3);
|
|
1952
|
+
const bounce = spring.bounce ?? 0.3;
|
|
1953
|
+
const root = 2 * Math.PI / (visualDuration * 1.2);
|
|
1954
|
+
const stiffness = root * root;
|
|
1955
|
+
const damping = 2 * Math.min(1, Math.max(0.05, 1 - bounce)) * Math.sqrt(stiffness);
|
|
1956
|
+
return { stiffness, damping, mass: 1 };
|
|
1957
|
+
}
|
|
1958
|
+
function springProgress(t, { stiffness, damping, mass }) {
|
|
1959
|
+
if (t <= 0) return 0;
|
|
1960
|
+
const w0 = Math.sqrt(stiffness / mass);
|
|
1961
|
+
const zeta = damping / (2 * Math.sqrt(stiffness * mass));
|
|
1962
|
+
if (zeta < 0.9999) {
|
|
1963
|
+
const wd2 = w0 * Math.sqrt(1 - zeta * zeta);
|
|
1964
|
+
return 1 - Math.exp(-zeta * w0 * t) * (Math.cos(wd2 * t) + zeta * w0 / wd2 * Math.sin(wd2 * t));
|
|
1965
|
+
}
|
|
1966
|
+
if (zeta < 1.0001) {
|
|
1967
|
+
return 1 - Math.exp(-w0 * t) * (1 + w0 * t);
|
|
1968
|
+
}
|
|
1969
|
+
const wd = w0 * Math.sqrt(zeta * zeta - 1);
|
|
1970
|
+
const r1 = -zeta * w0 + wd;
|
|
1971
|
+
const r2 = -zeta * w0 - wd;
|
|
1972
|
+
return 1 + (r2 * Math.exp(r1 * t) - r1 * Math.exp(r2 * t)) / (r1 - r2);
|
|
1973
|
+
}
|
|
1974
|
+
function springSettleDuration(params) {
|
|
1975
|
+
const w0 = Math.sqrt(params.stiffness / params.mass);
|
|
1976
|
+
const zeta = params.damping / (2 * Math.sqrt(params.stiffness * params.mass));
|
|
1977
|
+
const decay = zeta >= 1 ? zeta * w0 - w0 * Math.sqrt(Math.max(0, zeta * zeta - 1)) : zeta * w0;
|
|
1978
|
+
const duration = Math.log(200) / Math.max(decay, 1e-6);
|
|
1979
|
+
return round2(clamp(duration, 0.05, 10));
|
|
1980
|
+
}
|
|
1981
|
+
function cubicBezierProgress(p, [x1, y1, x2, y2]) {
|
|
1982
|
+
if (p <= 0) return 0;
|
|
1983
|
+
if (p >= 1) return 1;
|
|
1984
|
+
const sampleX = (t2) => bezierAxis(t2, x1, x2);
|
|
1985
|
+
const sampleY = (t2) => bezierAxis(t2, y1, y2);
|
|
1986
|
+
let t = p;
|
|
1987
|
+
for (let i = 0; i < 8; i++) {
|
|
1988
|
+
const x = sampleX(t) - p;
|
|
1989
|
+
if (Math.abs(x) < 1e-5) return sampleY(t);
|
|
1990
|
+
const dx = bezierAxisDerivative(t, x1, x2);
|
|
1991
|
+
if (Math.abs(dx) < 1e-6) break;
|
|
1992
|
+
t -= x / dx;
|
|
1993
|
+
}
|
|
1994
|
+
let lo = 0;
|
|
1995
|
+
let hi = 1;
|
|
1996
|
+
t = p;
|
|
1997
|
+
while (hi - lo > 1e-5) {
|
|
1998
|
+
if (sampleX(t) < p) lo = t;
|
|
1999
|
+
else hi = t;
|
|
2000
|
+
t = (lo + hi) / 2;
|
|
2001
|
+
}
|
|
2002
|
+
return sampleY(t);
|
|
2003
|
+
}
|
|
2004
|
+
function bezierAxis(t, a1, a2) {
|
|
2005
|
+
return (1 - 3 * a2 + 3 * a1) * t * t * t + (3 * a2 - 6 * a1) * t * t + 3 * a1 * t;
|
|
2006
|
+
}
|
|
2007
|
+
function bezierAxisDerivative(t, a1, a2) {
|
|
2008
|
+
return 3 * (1 - 3 * a2 + 3 * a1) * t * t + 2 * (3 * a2 - 6 * a1) * t + 3 * a1;
|
|
2009
|
+
}
|
|
2010
|
+
function resolveClipTransition(raw, clipDuration) {
|
|
2011
|
+
const safeDuration = Math.max(0.05, clipDuration);
|
|
2012
|
+
if (raw.type === "easing") {
|
|
2013
|
+
return {
|
|
2014
|
+
transition: { ...raw, duration: safeDuration },
|
|
2015
|
+
duration: safeDuration,
|
|
2016
|
+
isPhysics: false
|
|
2017
|
+
};
|
|
1665
2018
|
}
|
|
1666
|
-
|
|
2019
|
+
if (isPhysicsSpring(raw)) {
|
|
2020
|
+
return {
|
|
2021
|
+
transition: raw,
|
|
2022
|
+
duration: springSettleDuration(springParams(raw)),
|
|
2023
|
+
isPhysics: true
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
return {
|
|
2027
|
+
transition: { type: "spring", bounce: raw.bounce ?? 0.2, visualDuration: safeDuration },
|
|
2028
|
+
duration: safeDuration,
|
|
2029
|
+
isPhysics: false
|
|
2030
|
+
};
|
|
1667
2031
|
}
|
|
2032
|
+
|
|
2033
|
+
// src/components/SpringVisualization.tsx
|
|
2034
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1668
2035
|
function SpringVisualization({ spring, isSimpleMode }) {
|
|
1669
2036
|
const width = 256;
|
|
1670
2037
|
const height = 140;
|
|
1671
|
-
|
|
1672
|
-
let damping;
|
|
1673
|
-
let mass;
|
|
1674
|
-
if (isSimpleMode) {
|
|
1675
|
-
const visualDuration = spring.visualDuration ?? 0.3;
|
|
1676
|
-
const bounce = spring.bounce ?? 0.2;
|
|
1677
|
-
mass = 1;
|
|
1678
|
-
stiffness = 2 * Math.PI / visualDuration;
|
|
1679
|
-
stiffness = Math.pow(stiffness, 2);
|
|
1680
|
-
const dampingRatio = 1 - bounce;
|
|
1681
|
-
damping = 2 * dampingRatio * Math.sqrt(stiffness * mass);
|
|
1682
|
-
} else {
|
|
1683
|
-
stiffness = spring.stiffness ?? 400;
|
|
1684
|
-
damping = spring.damping ?? 17;
|
|
1685
|
-
mass = spring.mass ?? 1;
|
|
1686
|
-
}
|
|
2038
|
+
const params = isSimpleMode ? springParams({ type: "spring", visualDuration: spring.visualDuration ?? 0.3, bounce: spring.bounce ?? 0.2 }) : springParams({ type: "spring", stiffness: spring.stiffness ?? 400, damping: spring.damping ?? 17, mass: spring.mass ?? 1 });
|
|
1687
2039
|
const duration = 2;
|
|
1688
|
-
const
|
|
2040
|
+
const steps = 100;
|
|
2041
|
+
const points = [];
|
|
2042
|
+
for (let i = 0; i <= steps; i++) {
|
|
2043
|
+
const time = i / steps * duration;
|
|
2044
|
+
points.push([time, springProgress(time, params)]);
|
|
2045
|
+
}
|
|
1689
2046
|
const values = points.map(([, value]) => value);
|
|
1690
2047
|
const minValue = Math.min(...values);
|
|
1691
2048
|
const maxValue = Math.max(...values);
|
|
@@ -1734,20 +2091,20 @@ function SpringVisualization({ spring, isSimpleMode }) {
|
|
|
1734
2091
|
}
|
|
1735
2092
|
|
|
1736
2093
|
// src/components/SpringControl.tsx
|
|
1737
|
-
import { useCallback as
|
|
2094
|
+
import { useCallback as useCallback6, useRef as useRef7, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
1738
2095
|
import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1739
2096
|
function SpringControl({ panelId, path, label, spring, onChange }) {
|
|
1740
|
-
const subscribe =
|
|
2097
|
+
const subscribe = useCallback6(
|
|
1741
2098
|
(callback) => DialStore.subscribe(panelId, callback),
|
|
1742
2099
|
[panelId]
|
|
1743
2100
|
);
|
|
1744
|
-
const getSnapshot =
|
|
2101
|
+
const getSnapshot = useCallback6(
|
|
1745
2102
|
() => DialStore.getSpringMode(panelId, path),
|
|
1746
2103
|
[panelId, path]
|
|
1747
2104
|
);
|
|
1748
2105
|
const mode = useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
|
|
1749
2106
|
const isSimpleMode = mode === "simple";
|
|
1750
|
-
const cache =
|
|
2107
|
+
const cache = useRef7({
|
|
1751
2108
|
simple: spring.visualDuration !== void 0 ? spring : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1752
2109
|
advanced: spring.stiffness !== void 0 ? spring : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
|
|
1753
2110
|
});
|
|
@@ -1894,21 +2251,29 @@ function EasingVisualization({ easing }) {
|
|
|
1894
2251
|
}
|
|
1895
2252
|
|
|
1896
2253
|
// src/components/TransitionControl.tsx
|
|
1897
|
-
import { useCallback as
|
|
2254
|
+
import { useCallback as useCallback7, useRef as useRef8, useState as useState5, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
1898
2255
|
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1899
|
-
function TransitionControl({
|
|
1900
|
-
|
|
2256
|
+
function TransitionControl({
|
|
2257
|
+
panelId,
|
|
2258
|
+
path,
|
|
2259
|
+
label,
|
|
2260
|
+
value,
|
|
2261
|
+
onChange,
|
|
2262
|
+
hideDuration = false,
|
|
2263
|
+
durationControl
|
|
2264
|
+
}) {
|
|
2265
|
+
const subscribe = useCallback7(
|
|
1901
2266
|
(callback) => DialStore.subscribe(panelId, callback),
|
|
1902
2267
|
[panelId]
|
|
1903
2268
|
);
|
|
1904
|
-
const getSnapshot =
|
|
2269
|
+
const getSnapshot = useCallback7(
|
|
1905
2270
|
() => DialStore.getTransitionMode(panelId, path),
|
|
1906
2271
|
[panelId, path]
|
|
1907
2272
|
);
|
|
1908
2273
|
const mode = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
1909
2274
|
const isEasing = mode === "easing";
|
|
1910
2275
|
const isSimpleSpring = mode === "simple";
|
|
1911
|
-
const cache =
|
|
2276
|
+
const cache = useRef8({
|
|
1912
2277
|
easing: value.type === "easing" ? value : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] },
|
|
1913
2278
|
simple: value.type === "spring" && value.visualDuration !== void 0 ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1914
2279
|
advanced: value.type === "spring" && value.stiffness !== void 0 ? value : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
|
|
@@ -1946,6 +2311,21 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
|
|
|
1946
2311
|
newEase[index] = val;
|
|
1947
2312
|
onChange({ ...easing, ease: newEase });
|
|
1948
2313
|
};
|
|
2314
|
+
const durationSlider = !hideDuration && (isEasing || isSimpleSpring) ? /* @__PURE__ */ jsx9(
|
|
2315
|
+
Slider,
|
|
2316
|
+
{
|
|
2317
|
+
label: "Duration",
|
|
2318
|
+
value: durationControl?.value ?? (isEasing ? easing.duration : spring.visualDuration ?? 0.3),
|
|
2319
|
+
onChange: durationControl?.onChange ?? ((next) => {
|
|
2320
|
+
if (isEasing) onChange({ ...easing, duration: next });
|
|
2321
|
+
else handleSpringUpdate("visualDuration", next);
|
|
2322
|
+
}),
|
|
2323
|
+
min: durationControl?.min ?? 0.1,
|
|
2324
|
+
max: durationControl?.max ?? (isEasing ? 2 : 1),
|
|
2325
|
+
step: durationControl?.step ?? 0.05,
|
|
2326
|
+
unit: "s"
|
|
2327
|
+
}
|
|
2328
|
+
) : null;
|
|
1949
2329
|
return /* @__PURE__ */ jsx9(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
1950
2330
|
isEasing ? /* @__PURE__ */ jsx9(EasingVisualization, { easing }) : /* @__PURE__ */ jsx9(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
|
|
1951
2331
|
/* @__PURE__ */ jsxs8("div", { className: "dialkit-labeled-control", children: [
|
|
@@ -1968,16 +2348,13 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
|
|
|
1968
2348
|
/* @__PURE__ */ jsx9(Slider, { label: "y1", value: easing.ease[1], onChange: (v) => updateEase(1, v), min: -1, max: 2, step: 0.01 }),
|
|
1969
2349
|
/* @__PURE__ */ jsx9(Slider, { label: "x2", value: easing.ease[2], onChange: (v) => updateEase(2, v), min: 0, max: 1, step: 0.01 }),
|
|
1970
2350
|
/* @__PURE__ */ jsx9(Slider, { label: "y2", value: easing.ease[3], onChange: (v) => updateEase(3, v), min: -1, max: 2, step: 0.01 }),
|
|
1971
|
-
/* @__PURE__ */ jsx9(Slider, { label: "Duration", value: easing.duration, onChange: (v) => onChange({ ...easing, duration: v }), min: 0.1, max: 2, step: 0.05, unit: "s" }),
|
|
1972
2351
|
/* @__PURE__ */ jsx9(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
|
|
1973
|
-
] }) : isSimpleSpring ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1974
|
-
/* @__PURE__ */ jsx9(Slider, { label: "Duration", value: spring.visualDuration ?? 0.3, onChange: (v) => handleSpringUpdate("visualDuration", v), min: 0.1, max: 1, step: 0.05, unit: "s" }),
|
|
1975
|
-
/* @__PURE__ */ jsx9(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 })
|
|
1976
|
-
] }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
2352
|
+
] }) : isSimpleSpring ? /* @__PURE__ */ jsx9(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1977
2353
|
/* @__PURE__ */ jsx9(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
|
|
1978
2354
|
/* @__PURE__ */ jsx9(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
|
|
1979
2355
|
/* @__PURE__ */ jsx9(Slider, { label: "Mass", value: spring.mass ?? 1, onChange: (v) => handleSpringUpdate("mass", v), min: 0.1, max: 10, step: 0.1 })
|
|
1980
|
-
] })
|
|
2356
|
+
] }),
|
|
2357
|
+
durationSlider
|
|
1981
2358
|
] }) });
|
|
1982
2359
|
}
|
|
1983
2360
|
function formatEase(ease) {
|
|
@@ -2044,7 +2421,7 @@ function TextControl({ label, value, onChange, placeholder }) {
|
|
|
2044
2421
|
}
|
|
2045
2422
|
|
|
2046
2423
|
// src/components/SelectControl.tsx
|
|
2047
|
-
import { useState as useState6, useRef as
|
|
2424
|
+
import { useState as useState6, useRef as useRef9, useEffect as useEffect6, useCallback as useCallback8 } from "react";
|
|
2048
2425
|
import { createPortal } from "react-dom";
|
|
2049
2426
|
import { motion as motion3, AnimatePresence as AnimatePresence2 } from "motion/react";
|
|
2050
2427
|
|
|
@@ -2078,26 +2455,26 @@ function normalizeOptions(options) {
|
|
|
2078
2455
|
}
|
|
2079
2456
|
function SelectControl({ label, value, options, onChange }) {
|
|
2080
2457
|
const [isOpen, setIsOpen] = useState6(false);
|
|
2081
|
-
const triggerRef =
|
|
2082
|
-
const dropdownRef =
|
|
2458
|
+
const triggerRef = useRef9(null);
|
|
2459
|
+
const dropdownRef = useRef9(null);
|
|
2083
2460
|
const [portalTarget, setPortalTarget] = useState6(null);
|
|
2084
2461
|
const [pos, setPos] = useState6(null);
|
|
2085
2462
|
const normalized = normalizeOptions(options);
|
|
2086
2463
|
const selectedOption = normalized.find((o) => o.value === value);
|
|
2087
|
-
const updatePos =
|
|
2464
|
+
const updatePos = useCallback8(() => {
|
|
2088
2465
|
const el = triggerRef.current;
|
|
2089
2466
|
if (!el || !portalTarget) return;
|
|
2090
2467
|
const dropdownHeight = 8 + normalized.length * 36;
|
|
2091
2468
|
setPos(getDropdownPosition(el, portalTarget, { dropdownHeight }));
|
|
2092
2469
|
}, [normalized.length, portalTarget]);
|
|
2093
|
-
|
|
2470
|
+
useEffect6(() => {
|
|
2094
2471
|
setPortalTarget(getDialKitPortalRoot(triggerRef.current) ?? document.body);
|
|
2095
2472
|
}, []);
|
|
2096
|
-
|
|
2473
|
+
useEffect6(() => {
|
|
2097
2474
|
if (!isOpen) return;
|
|
2098
2475
|
updatePos();
|
|
2099
2476
|
}, [isOpen, updatePos]);
|
|
2100
|
-
|
|
2477
|
+
useEffect6(() => {
|
|
2101
2478
|
if (!isOpen) return;
|
|
2102
2479
|
const handleClick = (e) => {
|
|
2103
2480
|
const target = e.target;
|
|
@@ -2177,14 +2554,14 @@ function SelectControl({ label, value, options, onChange }) {
|
|
|
2177
2554
|
}
|
|
2178
2555
|
|
|
2179
2556
|
// src/components/ColorControl.tsx
|
|
2180
|
-
import { useState as useState7, useRef as
|
|
2557
|
+
import { useState as useState7, useRef as useRef10, useEffect as useEffect7 } from "react";
|
|
2181
2558
|
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2182
2559
|
var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
2183
2560
|
function ColorControl({ label, value, onChange }) {
|
|
2184
2561
|
const [isEditing, setIsEditing] = useState7(false);
|
|
2185
2562
|
const [editValue, setEditValue] = useState7(value);
|
|
2186
|
-
const colorInputRef =
|
|
2187
|
-
|
|
2563
|
+
const colorInputRef = useRef10(null);
|
|
2564
|
+
useEffect7(() => {
|
|
2188
2565
|
if (!isEditing) {
|
|
2189
2566
|
setEditValue(value);
|
|
2190
2567
|
}
|
|
@@ -2254,266 +2631,105 @@ function expandShorthandHex(hex) {
|
|
|
2254
2631
|
return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
|
|
2255
2632
|
}
|
|
2256
2633
|
|
|
2257
|
-
// src/components/
|
|
2258
|
-
import {
|
|
2259
|
-
|
|
2260
|
-
import { motion as motion4, AnimatePresence as AnimatePresence3 } from "motion/react";
|
|
2261
|
-
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2262
|
-
function PresetManager({ panelId, presets, activePresetId, onAdd }) {
|
|
2263
|
-
const [isOpen, setIsOpen] = useState8(false);
|
|
2264
|
-
const triggerRef = useRef10(null);
|
|
2265
|
-
const dropdownRef = useRef10(null);
|
|
2266
|
-
const [pos, setPos] = useState8({ top: 0, left: 0, width: 0 });
|
|
2267
|
-
const hasPresets = presets.length > 0;
|
|
2268
|
-
const activePreset = presets.find((p) => p.id === activePresetId);
|
|
2269
|
-
const open = useCallback8(() => {
|
|
2270
|
-
if (!hasPresets) return;
|
|
2271
|
-
const rect = triggerRef.current?.getBoundingClientRect();
|
|
2272
|
-
if (rect) {
|
|
2273
|
-
setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
|
|
2274
|
-
}
|
|
2275
|
-
setIsOpen(true);
|
|
2276
|
-
}, [hasPresets]);
|
|
2277
|
-
const close = useCallback8(() => setIsOpen(false), []);
|
|
2278
|
-
const toggle = useCallback8(() => {
|
|
2279
|
-
if (isOpen) close();
|
|
2280
|
-
else open();
|
|
2281
|
-
}, [isOpen, open, close]);
|
|
2282
|
-
useEffect7(() => {
|
|
2283
|
-
if (!isOpen) return;
|
|
2284
|
-
const handler = (e) => {
|
|
2285
|
-
const target = e.target;
|
|
2286
|
-
if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
|
|
2287
|
-
close();
|
|
2288
|
-
};
|
|
2289
|
-
document.addEventListener("mousedown", handler);
|
|
2290
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
2291
|
-
}, [isOpen, close]);
|
|
2292
|
-
const handleSelect = (presetId) => {
|
|
2293
|
-
if (presetId) {
|
|
2294
|
-
DialStore.loadPreset(panelId, presetId);
|
|
2295
|
-
} else {
|
|
2296
|
-
DialStore.clearActivePreset(panelId);
|
|
2297
|
-
}
|
|
2298
|
-
close();
|
|
2299
|
-
};
|
|
2300
|
-
const handleDelete = (e, presetId) => {
|
|
2301
|
-
e.stopPropagation();
|
|
2302
|
-
DialStore.deletePreset(panelId, presetId);
|
|
2303
|
-
};
|
|
2304
|
-
return /* @__PURE__ */ jsxs12("div", { className: "dialkit-preset-manager", children: [
|
|
2305
|
-
/* @__PURE__ */ jsxs12(
|
|
2306
|
-
"button",
|
|
2307
|
-
{
|
|
2308
|
-
ref: triggerRef,
|
|
2309
|
-
className: "dialkit-preset-trigger",
|
|
2310
|
-
onClick: toggle,
|
|
2311
|
-
"data-open": String(isOpen),
|
|
2312
|
-
"data-has-preset": String(!!activePreset),
|
|
2313
|
-
"data-disabled": String(!hasPresets),
|
|
2314
|
-
children: [
|
|
2315
|
-
/* @__PURE__ */ jsx13("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
|
|
2316
|
-
/* @__PURE__ */ jsx13(
|
|
2317
|
-
motion4.svg,
|
|
2318
|
-
{
|
|
2319
|
-
className: "dialkit-select-chevron",
|
|
2320
|
-
viewBox: "0 0 24 24",
|
|
2321
|
-
fill: "none",
|
|
2322
|
-
stroke: "currentColor",
|
|
2323
|
-
strokeWidth: "2.5",
|
|
2324
|
-
strokeLinecap: "round",
|
|
2325
|
-
strokeLinejoin: "round",
|
|
2326
|
-
animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
|
|
2327
|
-
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
2328
|
-
children: /* @__PURE__ */ jsx13("path", { d: ICON_CHEVRON })
|
|
2329
|
-
}
|
|
2330
|
-
)
|
|
2331
|
-
]
|
|
2332
|
-
}
|
|
2333
|
-
),
|
|
2334
|
-
createPortal2(
|
|
2335
|
-
/* @__PURE__ */ jsx13(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs12(
|
|
2336
|
-
motion4.div,
|
|
2337
|
-
{
|
|
2338
|
-
ref: dropdownRef,
|
|
2339
|
-
className: "dialkit-root dialkit-preset-dropdown",
|
|
2340
|
-
style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.width },
|
|
2341
|
-
initial: { opacity: 0, y: 4, scale: 0.97 },
|
|
2342
|
-
animate: { opacity: 1, y: 0, scale: 1 },
|
|
2343
|
-
exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
|
|
2344
|
-
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
2345
|
-
children: [
|
|
2346
|
-
/* @__PURE__ */ jsx13(
|
|
2347
|
-
"div",
|
|
2348
|
-
{
|
|
2349
|
-
className: "dialkit-preset-item",
|
|
2350
|
-
"data-active": String(!activePresetId),
|
|
2351
|
-
onClick: () => handleSelect(null),
|
|
2352
|
-
children: /* @__PURE__ */ jsx13("span", { className: "dialkit-preset-name", children: "Version 1" })
|
|
2353
|
-
}
|
|
2354
|
-
),
|
|
2355
|
-
presets.map((preset) => /* @__PURE__ */ jsxs12(
|
|
2356
|
-
"div",
|
|
2357
|
-
{
|
|
2358
|
-
className: "dialkit-preset-item",
|
|
2359
|
-
"data-active": String(preset.id === activePresetId),
|
|
2360
|
-
onClick: () => handleSelect(preset.id),
|
|
2361
|
-
children: [
|
|
2362
|
-
/* @__PURE__ */ jsx13("span", { className: "dialkit-preset-name", children: preset.name }),
|
|
2363
|
-
/* @__PURE__ */ jsx13(
|
|
2364
|
-
"button",
|
|
2365
|
-
{
|
|
2366
|
-
className: "dialkit-preset-delete",
|
|
2367
|
-
onClick: (e) => handleDelete(e, preset.id),
|
|
2368
|
-
title: "Delete preset",
|
|
2369
|
-
children: /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_TRASH.map((d, i) => /* @__PURE__ */ jsx13("path", { d }, i)) })
|
|
2370
|
-
}
|
|
2371
|
-
)
|
|
2372
|
-
]
|
|
2373
|
-
},
|
|
2374
|
-
preset.id
|
|
2375
|
-
))
|
|
2376
|
-
]
|
|
2377
|
-
}
|
|
2378
|
-
) }),
|
|
2379
|
-
document.body
|
|
2380
|
-
)
|
|
2381
|
-
] });
|
|
2382
|
-
}
|
|
2383
|
-
|
|
2384
|
-
// src/components/Panel.tsx
|
|
2385
|
-
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2386
|
-
function Panel({ panel, defaultOpen = true, inline = false, onOpenChange, variant = "root" }) {
|
|
2387
|
-
const [copied, setCopied] = useState9(false);
|
|
2388
|
-
const [isPanelOpen, setIsPanelOpen] = useState9(defaultOpen);
|
|
2634
|
+
// src/components/ControlRenderer.tsx
|
|
2635
|
+
import { Fragment as Fragment3, jsx as jsx13 } from "react/jsx-runtime";
|
|
2636
|
+
function ControlRenderer({ panelId, controls, values, transitionDuration }) {
|
|
2389
2637
|
const shortcutCtx = useContext(ShortcutContext);
|
|
2390
|
-
const hasShortcuts = Object.keys(panel.shortcuts).length > 0;
|
|
2391
|
-
const subscribe = useCallback9(
|
|
2392
|
-
(callback) => DialStore.subscribe(panel.id, callback),
|
|
2393
|
-
[panel.id]
|
|
2394
|
-
);
|
|
2395
|
-
const getSnapshot = useCallback9(
|
|
2396
|
-
() => DialStore.getValues(panel.id),
|
|
2397
|
-
[panel.id]
|
|
2398
|
-
);
|
|
2399
|
-
const values = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
|
|
2400
|
-
const presets = DialStore.getPresets(panel.id);
|
|
2401
|
-
const activePresetId = DialStore.getActivePresetId(panel.id);
|
|
2402
|
-
const handleAddPreset = () => {
|
|
2403
|
-
const nextNum = presets.length + 2;
|
|
2404
|
-
DialStore.savePreset(panel.id, `Version ${nextNum}`);
|
|
2405
|
-
};
|
|
2406
|
-
const handleCopy = () => {
|
|
2407
|
-
const jsonStr = JSON.stringify(values, null, 2);
|
|
2408
|
-
const instruction = `Update the useDialKit configuration for "${panel.name}" with these values:
|
|
2409
|
-
|
|
2410
|
-
\`\`\`json
|
|
2411
|
-
${jsonStr}
|
|
2412
|
-
\`\`\`
|
|
2413
|
-
|
|
2414
|
-
Apply these values as the new defaults in the useDialKit call.`;
|
|
2415
|
-
navigator.clipboard.writeText(instruction);
|
|
2416
|
-
setCopied(true);
|
|
2417
|
-
setTimeout(() => setCopied(false), 1500);
|
|
2418
|
-
};
|
|
2419
|
-
const handleOpenChange = useCallback9((open) => {
|
|
2420
|
-
setIsPanelOpen(open);
|
|
2421
|
-
onOpenChange?.(open);
|
|
2422
|
-
}, [onOpenChange]);
|
|
2423
2638
|
const renderControl = (control) => {
|
|
2424
2639
|
const value = values[control.path];
|
|
2425
2640
|
switch (control.type) {
|
|
2426
2641
|
case "slider":
|
|
2427
|
-
return /* @__PURE__ */
|
|
2642
|
+
return /* @__PURE__ */ jsx13(
|
|
2428
2643
|
Slider,
|
|
2429
2644
|
{
|
|
2430
2645
|
label: control.label,
|
|
2431
2646
|
value,
|
|
2432
|
-
onChange: (v) => DialStore.updateValue(
|
|
2647
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v),
|
|
2433
2648
|
min: control.min,
|
|
2434
2649
|
max: control.max,
|
|
2435
2650
|
step: control.step,
|
|
2436
2651
|
shortcut: control.shortcut,
|
|
2437
|
-
shortcutActive: shortcutCtx.activePanelId ===
|
|
2652
|
+
shortcutActive: shortcutCtx.activePanelId === panelId && shortcutCtx.activePath === control.path
|
|
2438
2653
|
},
|
|
2439
2654
|
control.path
|
|
2440
2655
|
);
|
|
2441
2656
|
case "toggle":
|
|
2442
|
-
return /* @__PURE__ */
|
|
2657
|
+
return /* @__PURE__ */ jsx13(
|
|
2443
2658
|
Toggle,
|
|
2444
2659
|
{
|
|
2445
2660
|
label: control.label,
|
|
2446
2661
|
checked: value,
|
|
2447
|
-
onChange: (v) => DialStore.updateValue(
|
|
2662
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v),
|
|
2448
2663
|
shortcut: control.shortcut,
|
|
2449
|
-
shortcutActive: shortcutCtx.activePanelId ===
|
|
2664
|
+
shortcutActive: shortcutCtx.activePanelId === panelId && shortcutCtx.activePath === control.path
|
|
2450
2665
|
},
|
|
2451
2666
|
control.path
|
|
2452
2667
|
);
|
|
2453
2668
|
case "spring":
|
|
2454
|
-
return /* @__PURE__ */
|
|
2669
|
+
return /* @__PURE__ */ jsx13(
|
|
2455
2670
|
SpringControl,
|
|
2456
2671
|
{
|
|
2457
|
-
panelId
|
|
2672
|
+
panelId,
|
|
2458
2673
|
path: control.path,
|
|
2459
2674
|
label: control.label,
|
|
2460
2675
|
spring: value,
|
|
2461
|
-
onChange: (v) => DialStore.updateValue(
|
|
2676
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v)
|
|
2462
2677
|
},
|
|
2463
2678
|
control.path
|
|
2464
2679
|
);
|
|
2465
2680
|
case "transition":
|
|
2466
|
-
return /* @__PURE__ */
|
|
2681
|
+
return /* @__PURE__ */ jsx13(
|
|
2467
2682
|
TransitionControl,
|
|
2468
2683
|
{
|
|
2469
|
-
panelId
|
|
2684
|
+
panelId,
|
|
2470
2685
|
path: control.path,
|
|
2471
2686
|
label: control.label,
|
|
2472
2687
|
value,
|
|
2473
|
-
onChange: (v) => DialStore.updateValue(
|
|
2688
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v),
|
|
2689
|
+
durationControl: transitionDuration
|
|
2474
2690
|
},
|
|
2475
2691
|
control.path
|
|
2476
2692
|
);
|
|
2477
2693
|
case "folder":
|
|
2478
|
-
return /* @__PURE__ */
|
|
2694
|
+
return /* @__PURE__ */ jsx13(Folder, { title: control.label, defaultOpen: control.defaultOpen ?? true, children: control.children?.map(renderControl) }, control.path);
|
|
2479
2695
|
case "text":
|
|
2480
|
-
return /* @__PURE__ */
|
|
2696
|
+
return /* @__PURE__ */ jsx13(
|
|
2481
2697
|
TextControl,
|
|
2482
2698
|
{
|
|
2483
2699
|
label: control.label,
|
|
2484
2700
|
value,
|
|
2485
|
-
onChange: (v) => DialStore.updateValue(
|
|
2701
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v),
|
|
2486
2702
|
placeholder: control.placeholder
|
|
2487
2703
|
},
|
|
2488
2704
|
control.path
|
|
2489
2705
|
);
|
|
2490
2706
|
case "select":
|
|
2491
|
-
return /* @__PURE__ */
|
|
2707
|
+
return /* @__PURE__ */ jsx13(
|
|
2492
2708
|
SelectControl,
|
|
2493
2709
|
{
|
|
2494
2710
|
label: control.label,
|
|
2495
2711
|
value,
|
|
2496
2712
|
options: control.options ?? [],
|
|
2497
|
-
onChange: (v) => DialStore.updateValue(
|
|
2713
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v)
|
|
2498
2714
|
},
|
|
2499
2715
|
control.path
|
|
2500
2716
|
);
|
|
2501
2717
|
case "color":
|
|
2502
|
-
return /* @__PURE__ */
|
|
2718
|
+
return /* @__PURE__ */ jsx13(
|
|
2503
2719
|
ColorControl,
|
|
2504
2720
|
{
|
|
2505
2721
|
label: control.label,
|
|
2506
2722
|
value,
|
|
2507
|
-
onChange: (v) => DialStore.updateValue(
|
|
2723
|
+
onChange: (v) => DialStore.updateValue(panelId, control.path, v)
|
|
2508
2724
|
},
|
|
2509
2725
|
control.path
|
|
2510
2726
|
);
|
|
2511
2727
|
case "action":
|
|
2512
|
-
return /* @__PURE__ */
|
|
2728
|
+
return /* @__PURE__ */ jsx13(
|
|
2513
2729
|
"button",
|
|
2514
2730
|
{
|
|
2515
2731
|
className: "dialkit-button",
|
|
2516
|
-
onClick: () => DialStore.triggerAction(
|
|
2732
|
+
onClick: () => DialStore.triggerAction(panelId, control.path),
|
|
2517
2733
|
children: control.label
|
|
2518
2734
|
},
|
|
2519
2735
|
control.path
|
|
@@ -2522,12 +2738,170 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2522
2738
|
return null;
|
|
2523
2739
|
}
|
|
2524
2740
|
};
|
|
2525
|
-
|
|
2526
|
-
|
|
2741
|
+
return /* @__PURE__ */ jsx13(Fragment3, { children: controls.map(renderControl) });
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/components/PresetManager.tsx
|
|
2745
|
+
import { useState as useState8, useRef as useRef11, useEffect as useEffect8, useCallback as useCallback9 } from "react";
|
|
2746
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
2747
|
+
import { motion as motion4, AnimatePresence as AnimatePresence3 } from "motion/react";
|
|
2748
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2749
|
+
function PresetManager({ panelId, presets, activePresetId, onAdd }) {
|
|
2750
|
+
const [isOpen, setIsOpen] = useState8(false);
|
|
2751
|
+
const triggerRef = useRef11(null);
|
|
2752
|
+
const dropdownRef = useRef11(null);
|
|
2753
|
+
const [pos, setPos] = useState8({ top: 0, left: 0, width: 0 });
|
|
2754
|
+
const hasPresets = presets.length > 0;
|
|
2755
|
+
const activePreset = presets.find((p) => p.id === activePresetId);
|
|
2756
|
+
const open = useCallback9(() => {
|
|
2757
|
+
if (!hasPresets) return;
|
|
2758
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
2759
|
+
if (rect) {
|
|
2760
|
+
setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
|
|
2761
|
+
}
|
|
2762
|
+
setIsOpen(true);
|
|
2763
|
+
}, [hasPresets]);
|
|
2764
|
+
const close = useCallback9(() => setIsOpen(false), []);
|
|
2765
|
+
const toggle = useCallback9(() => {
|
|
2766
|
+
if (isOpen) close();
|
|
2767
|
+
else open();
|
|
2768
|
+
}, [isOpen, open, close]);
|
|
2769
|
+
useEffect8(() => {
|
|
2770
|
+
if (!isOpen) return;
|
|
2771
|
+
const handler = (e) => {
|
|
2772
|
+
const target = e.target;
|
|
2773
|
+
if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
|
|
2774
|
+
close();
|
|
2775
|
+
};
|
|
2776
|
+
document.addEventListener("mousedown", handler);
|
|
2777
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
2778
|
+
}, [isOpen, close]);
|
|
2779
|
+
const handleSelect = (presetId) => {
|
|
2780
|
+
if (presetId) {
|
|
2781
|
+
DialStore.loadPreset(panelId, presetId);
|
|
2782
|
+
} else {
|
|
2783
|
+
DialStore.clearActivePreset(panelId);
|
|
2784
|
+
}
|
|
2785
|
+
close();
|
|
2786
|
+
};
|
|
2787
|
+
const handleDelete = (e, presetId) => {
|
|
2788
|
+
e.stopPropagation();
|
|
2789
|
+
DialStore.deletePreset(panelId, presetId);
|
|
2790
|
+
};
|
|
2791
|
+
return /* @__PURE__ */ jsxs12("div", { className: "dialkit-preset-manager", children: [
|
|
2792
|
+
/* @__PURE__ */ jsxs12(
|
|
2793
|
+
"button",
|
|
2794
|
+
{
|
|
2795
|
+
ref: triggerRef,
|
|
2796
|
+
className: "dialkit-preset-trigger",
|
|
2797
|
+
onClick: toggle,
|
|
2798
|
+
"data-open": String(isOpen),
|
|
2799
|
+
"data-has-preset": String(!!activePreset),
|
|
2800
|
+
"data-disabled": String(!hasPresets),
|
|
2801
|
+
children: [
|
|
2802
|
+
/* @__PURE__ */ jsx14("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
|
|
2803
|
+
/* @__PURE__ */ jsx14(
|
|
2804
|
+
motion4.svg,
|
|
2805
|
+
{
|
|
2806
|
+
className: "dialkit-select-chevron",
|
|
2807
|
+
viewBox: "0 0 24 24",
|
|
2808
|
+
fill: "none",
|
|
2809
|
+
stroke: "currentColor",
|
|
2810
|
+
strokeWidth: "2.5",
|
|
2811
|
+
strokeLinecap: "round",
|
|
2812
|
+
strokeLinejoin: "round",
|
|
2813
|
+
animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
|
|
2814
|
+
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
2815
|
+
children: /* @__PURE__ */ jsx14("path", { d: ICON_CHEVRON })
|
|
2816
|
+
}
|
|
2817
|
+
)
|
|
2818
|
+
]
|
|
2819
|
+
}
|
|
2820
|
+
),
|
|
2821
|
+
createPortal2(
|
|
2822
|
+
/* @__PURE__ */ jsx14(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs12(
|
|
2823
|
+
motion4.div,
|
|
2824
|
+
{
|
|
2825
|
+
ref: dropdownRef,
|
|
2826
|
+
className: "dialkit-root dialkit-preset-dropdown",
|
|
2827
|
+
style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.width },
|
|
2828
|
+
initial: { opacity: 0, y: 4, scale: 0.97 },
|
|
2829
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
2830
|
+
exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
|
|
2831
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
2832
|
+
children: [
|
|
2833
|
+
/* @__PURE__ */ jsx14(
|
|
2834
|
+
"div",
|
|
2835
|
+
{
|
|
2836
|
+
className: "dialkit-preset-item",
|
|
2837
|
+
"data-active": String(!activePresetId),
|
|
2838
|
+
onClick: () => handleSelect(null),
|
|
2839
|
+
children: /* @__PURE__ */ jsx14("span", { className: "dialkit-preset-name", children: "Version 1" })
|
|
2840
|
+
}
|
|
2841
|
+
),
|
|
2842
|
+
presets.map((preset) => /* @__PURE__ */ jsxs12(
|
|
2843
|
+
"div",
|
|
2844
|
+
{
|
|
2845
|
+
className: "dialkit-preset-item",
|
|
2846
|
+
"data-active": String(preset.id === activePresetId),
|
|
2847
|
+
onClick: () => handleSelect(preset.id),
|
|
2848
|
+
children: [
|
|
2849
|
+
/* @__PURE__ */ jsx14("span", { className: "dialkit-preset-name", children: preset.name }),
|
|
2850
|
+
/* @__PURE__ */ jsx14(
|
|
2851
|
+
"button",
|
|
2852
|
+
{
|
|
2853
|
+
className: "dialkit-preset-delete",
|
|
2854
|
+
onClick: (e) => handleDelete(e, preset.id),
|
|
2855
|
+
title: "Delete preset",
|
|
2856
|
+
children: /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_TRASH.map((d, i) => /* @__PURE__ */ jsx14("path", { d }, i)) })
|
|
2857
|
+
}
|
|
2858
|
+
)
|
|
2859
|
+
]
|
|
2860
|
+
},
|
|
2861
|
+
preset.id
|
|
2862
|
+
))
|
|
2863
|
+
]
|
|
2864
|
+
}
|
|
2865
|
+
) }),
|
|
2866
|
+
document.body
|
|
2867
|
+
)
|
|
2868
|
+
] });
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// src/components/Panel.tsx
|
|
2872
|
+
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2873
|
+
function Panel({ panel, defaultOpen = true, inline = false, onOpenChange, variant = "root", toolbarExtra }) {
|
|
2874
|
+
const [copied, setCopied] = useState9(false);
|
|
2875
|
+
const [isPanelOpen, setIsPanelOpen] = useState9(defaultOpen);
|
|
2876
|
+
const hasShortcuts = Object.keys(panel.shortcuts).length > 0;
|
|
2877
|
+
const subscribe = useCallback10(
|
|
2878
|
+
(callback) => DialStore.subscribe(panel.id, callback),
|
|
2879
|
+
[panel.id]
|
|
2880
|
+
);
|
|
2881
|
+
const getSnapshot = useCallback10(
|
|
2882
|
+
() => DialStore.getValues(panel.id),
|
|
2883
|
+
[panel.id]
|
|
2884
|
+
);
|
|
2885
|
+
const values = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
|
|
2886
|
+
const presets = DialStore.getPresets(panel.id);
|
|
2887
|
+
const activePresetId = DialStore.getActivePresetId(panel.id);
|
|
2888
|
+
const handleAddPreset = () => {
|
|
2889
|
+
const nextNum = presets.length + 2;
|
|
2890
|
+
DialStore.savePreset(panel.id, `Version ${nextNum}`);
|
|
2891
|
+
};
|
|
2892
|
+
const handleCopy = () => {
|
|
2893
|
+
navigator.clipboard.writeText(buildCopyInstruction("useDialKit", panel.name, values));
|
|
2894
|
+
setCopied(true);
|
|
2895
|
+
setTimeout(() => setCopied(false), 1500);
|
|
2527
2896
|
};
|
|
2897
|
+
const handleOpenChange = useCallback10((open) => {
|
|
2898
|
+
setIsPanelOpen(open);
|
|
2899
|
+
onOpenChange?.(open);
|
|
2900
|
+
}, [onOpenChange]);
|
|
2901
|
+
const renderControls = () => /* @__PURE__ */ jsx15(ControlRenderer, { panelId: panel.id, controls: panel.controls, values });
|
|
2528
2902
|
const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
|
|
2529
|
-
const toolbar = /* @__PURE__ */ jsxs13(
|
|
2530
|
-
/* @__PURE__ */
|
|
2903
|
+
const toolbar = /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
2904
|
+
/* @__PURE__ */ jsx15(
|
|
2531
2905
|
motion5.button,
|
|
2532
2906
|
{
|
|
2533
2907
|
className: "dialkit-toolbar-add",
|
|
@@ -2535,10 +2909,10 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2535
2909
|
title: "Add preset",
|
|
2536
2910
|
whileTap: { scale: 0.9 },
|
|
2537
2911
|
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
2538
|
-
children: /* @__PURE__ */
|
|
2912
|
+
children: /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_ADD_PRESET.map((d, i) => /* @__PURE__ */ jsx15("path", { d }, i)) })
|
|
2539
2913
|
}
|
|
2540
2914
|
),
|
|
2541
|
-
/* @__PURE__ */
|
|
2915
|
+
/* @__PURE__ */ jsx15(
|
|
2542
2916
|
PresetManager,
|
|
2543
2917
|
{
|
|
2544
2918
|
panelId: panel.id,
|
|
@@ -2547,7 +2921,7 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2547
2921
|
onAdd: handleAddPreset
|
|
2548
2922
|
}
|
|
2549
2923
|
),
|
|
2550
|
-
/* @__PURE__ */
|
|
2924
|
+
/* @__PURE__ */ jsx15(
|
|
2551
2925
|
motion5.button,
|
|
2552
2926
|
{
|
|
2553
2927
|
className: "dialkit-toolbar-add",
|
|
@@ -2555,7 +2929,7 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2555
2929
|
title: "Copy parameters",
|
|
2556
2930
|
whileTap: { scale: 0.9 },
|
|
2557
2931
|
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
2558
|
-
children: /* @__PURE__ */
|
|
2932
|
+
children: /* @__PURE__ */ jsx15("span", { style: { position: "relative", width: 16, height: 16 }, children: /* @__PURE__ */ jsx15(AnimatePresence4, { initial: false, mode: "wait", children: copied ? /* @__PURE__ */ jsx15(
|
|
2559
2933
|
motion5.svg,
|
|
2560
2934
|
{
|
|
2561
2935
|
viewBox: "0 0 24 24",
|
|
@@ -2569,7 +2943,7 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2569
2943
|
animate: { scale: 1, opacity: 1 },
|
|
2570
2944
|
exit: { scale: 0.8, opacity: 0 },
|
|
2571
2945
|
transition: { duration: 0.08 },
|
|
2572
|
-
children: /* @__PURE__ */
|
|
2946
|
+
children: /* @__PURE__ */ jsx15("path", { d: ICON_CHECK })
|
|
2573
2947
|
},
|
|
2574
2948
|
"check"
|
|
2575
2949
|
) : /* @__PURE__ */ jsxs13(
|
|
@@ -2583,23 +2957,116 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2583
2957
|
exit: { scale: 0.8, opacity: 0 },
|
|
2584
2958
|
transition: { duration: 0.08 },
|
|
2585
2959
|
children: [
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
/* @__PURE__ */
|
|
2588
|
-
/* @__PURE__ */
|
|
2960
|
+
/* @__PURE__ */ jsx15("path", { d: ICON_CLIPBOARD.board, stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
|
|
2961
|
+
/* @__PURE__ */ jsx15("path", { d: ICON_CLIPBOARD.sparkle, fill: "currentColor" }),
|
|
2962
|
+
/* @__PURE__ */ jsx15("path", { d: ICON_CLIPBOARD.body, stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2589
2963
|
]
|
|
2590
2964
|
},
|
|
2591
2965
|
"clipboard"
|
|
2592
2966
|
) }) })
|
|
2593
2967
|
}
|
|
2594
|
-
)
|
|
2968
|
+
),
|
|
2969
|
+
toolbarExtra
|
|
2595
2970
|
] });
|
|
2596
2971
|
if (variant === "section") {
|
|
2597
2972
|
return /* @__PURE__ */ jsxs13(Folder, { title: panel.name, defaultOpen, onOpenChange: handleOpenChange, children: [
|
|
2598
|
-
/* @__PURE__ */
|
|
2973
|
+
/* @__PURE__ */ jsx15("div", { className: "dialkit-panel-section-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar }),
|
|
2599
2974
|
renderControls()
|
|
2600
2975
|
] });
|
|
2601
2976
|
}
|
|
2602
|
-
return /* @__PURE__ */
|
|
2977
|
+
return /* @__PURE__ */ jsx15("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx15(Folder, { title: panel.name, defaultOpen, isRoot: true, inline, onOpenChange: handleOpenChange, toolbar, children: renderControls() }) });
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
// src/components/Timeline/TimelineToggleButton.tsx
|
|
2981
|
+
import { useCallback as useCallback11, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
2982
|
+
import { motion as motion6 } from "motion/react";
|
|
2983
|
+
|
|
2984
|
+
// src/store/TimelineUiStore.ts
|
|
2985
|
+
var TimelineUiStoreClass = class {
|
|
2986
|
+
constructor() {
|
|
2987
|
+
this.visible = true;
|
|
2988
|
+
this.initialized = false;
|
|
2989
|
+
this.controllers = /* @__PURE__ */ new Map();
|
|
2990
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
2991
|
+
}
|
|
2992
|
+
getVisible() {
|
|
2993
|
+
for (const controller of this.controllers.values()) {
|
|
2994
|
+
if (controller.visible !== void 0) return controller.visible;
|
|
2995
|
+
}
|
|
2996
|
+
return this.visible;
|
|
2997
|
+
}
|
|
2998
|
+
registerController(id, controller) {
|
|
2999
|
+
const previous = this.getVisible();
|
|
3000
|
+
if (!this.initialized) {
|
|
3001
|
+
this.visible = controller.defaultVisible;
|
|
3002
|
+
this.initialized = true;
|
|
3003
|
+
}
|
|
3004
|
+
this.controllers.set(id, controller);
|
|
3005
|
+
if (previous !== this.getVisible()) this.notify();
|
|
3006
|
+
return () => {
|
|
3007
|
+
const before = this.getVisible();
|
|
3008
|
+
this.controllers.delete(id);
|
|
3009
|
+
if (this.controllers.size === 0) this.initialized = false;
|
|
3010
|
+
if (before !== this.getVisible()) this.notify();
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
updateController(id, controller) {
|
|
3014
|
+
if (!this.controllers.has(id)) return;
|
|
3015
|
+
const previous = this.getVisible();
|
|
3016
|
+
this.controllers.set(id, controller);
|
|
3017
|
+
if (previous !== this.getVisible()) this.notify();
|
|
3018
|
+
}
|
|
3019
|
+
requestVisible(visible) {
|
|
3020
|
+
const current = this.getVisible();
|
|
3021
|
+
if (current === visible) return;
|
|
3022
|
+
const controlled = Array.from(this.controllers.values()).filter(
|
|
3023
|
+
(controller) => controller.visible !== void 0
|
|
3024
|
+
);
|
|
3025
|
+
if (controlled.length > 0) {
|
|
3026
|
+
controlled.forEach((controller) => controller.onVisibilityChange?.(visible));
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
this.visible = visible;
|
|
3030
|
+
this.controllers.forEach((controller) => controller.onVisibilityChange?.(visible));
|
|
3031
|
+
this.notify();
|
|
3032
|
+
}
|
|
3033
|
+
toggle() {
|
|
3034
|
+
this.requestVisible(!this.getVisible());
|
|
3035
|
+
}
|
|
3036
|
+
subscribe(listener) {
|
|
3037
|
+
this.listeners.add(listener);
|
|
3038
|
+
return () => this.listeners.delete(listener);
|
|
3039
|
+
}
|
|
3040
|
+
notify() {
|
|
3041
|
+
this.listeners.forEach((listener) => listener());
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
var TimelineUiStore = /* @__PURE__ */ new TimelineUiStoreClass();
|
|
3045
|
+
|
|
3046
|
+
// src/components/Timeline/TimelineToggleButton.tsx
|
|
3047
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
3048
|
+
function TimelineToggleButton() {
|
|
3049
|
+
const subscribe = useCallback11(
|
|
3050
|
+
(listener) => TimelineUiStore.subscribe(listener),
|
|
3051
|
+
[]
|
|
3052
|
+
);
|
|
3053
|
+
const getVisible = useCallback11(() => TimelineUiStore.getVisible(), []);
|
|
3054
|
+
const visible = useSyncExternalStore5(subscribe, getVisible, getVisible);
|
|
3055
|
+
const label = visible ? "Hide timeline" : "Show timeline";
|
|
3056
|
+
return /* @__PURE__ */ jsx16(
|
|
3057
|
+
motion6.button,
|
|
3058
|
+
{
|
|
3059
|
+
className: "dialkit-toolbar-add dialkit-timeline-toolbar-toggle",
|
|
3060
|
+
"data-active": visible || void 0,
|
|
3061
|
+
"aria-pressed": visible,
|
|
3062
|
+
"aria-label": label,
|
|
3063
|
+
title: label,
|
|
3064
|
+
onClick: () => TimelineUiStore.toggle(),
|
|
3065
|
+
whileTap: { scale: 0.9 },
|
|
3066
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
3067
|
+
children: /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: ICON_TIMELINE.map((d, i) => /* @__PURE__ */ jsx16("path", { d, fill: "currentColor" }, i)) })
|
|
3068
|
+
}
|
|
3069
|
+
);
|
|
2603
3070
|
}
|
|
2604
3071
|
|
|
2605
3072
|
// src/panel-drag.ts
|
|
@@ -2667,32 +3134,39 @@ function blockPanelDragClick(handle) {
|
|
|
2667
3134
|
}
|
|
2668
3135
|
|
|
2669
3136
|
// src/components/DialRoot.tsx
|
|
2670
|
-
import { jsx as
|
|
2671
|
-
var isDevDefault = typeof process !== "undefined" && process?.env?.NODE_ENV ? process.env.NODE_ENV !== "production" : typeof import.meta !== "undefined" && import.meta.env?.MODE ? import.meta.env.MODE !== "production" : true;
|
|
3137
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2672
3138
|
function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover", theme = "system", productionEnabled = isDevDefault, onOpenChange }) {
|
|
2673
3139
|
if (!productionEnabled) return null;
|
|
2674
3140
|
const [panels, setPanels] = useState10([]);
|
|
3141
|
+
const [timelineCount, setTimelineCount] = useState10(0);
|
|
2675
3142
|
const [mounted, setMounted] = useState10(false);
|
|
2676
3143
|
const inline = mode === "inline";
|
|
2677
|
-
const panelRef =
|
|
3144
|
+
const panelRef = useRef12(null);
|
|
2678
3145
|
const [dragOffset, setDragOffset] = useState10(null);
|
|
2679
3146
|
const [activePosition, setActivePosition] = useState10(position);
|
|
2680
|
-
const lastDragOffset =
|
|
2681
|
-
const draggingRef =
|
|
2682
|
-
const dragStartRef =
|
|
2683
|
-
const didDragRef =
|
|
2684
|
-
const dragTargetRef =
|
|
2685
|
-
const panelOpenStatesRef =
|
|
2686
|
-
const rootOpenRef =
|
|
2687
|
-
|
|
3147
|
+
const lastDragOffset = useRef12(null);
|
|
3148
|
+
const draggingRef = useRef12(false);
|
|
3149
|
+
const dragStartRef = useRef12(null);
|
|
3150
|
+
const didDragRef = useRef12(false);
|
|
3151
|
+
const dragTargetRef = useRef12(null);
|
|
3152
|
+
const panelOpenStatesRef = useRef12(/* @__PURE__ */ new Map());
|
|
3153
|
+
const rootOpenRef = useRef12(null);
|
|
3154
|
+
useEffect9(() => {
|
|
2688
3155
|
setMounted(true);
|
|
2689
|
-
setPanels(DialStore.getPanels());
|
|
2690
|
-
|
|
2691
|
-
|
|
3156
|
+
setPanels(DialStore.getPanels("panel"));
|
|
3157
|
+
setTimelineCount(TimelineStore.getTimelines().length);
|
|
3158
|
+
const unsubscribePanels = DialStore.subscribeGlobal(() => {
|
|
3159
|
+
setPanels(DialStore.getPanels("panel"));
|
|
3160
|
+
});
|
|
3161
|
+
const unsubscribeTimelines = TimelineStore.subscribeGlobal(() => {
|
|
3162
|
+
setTimelineCount(TimelineStore.getTimelines().length);
|
|
2692
3163
|
});
|
|
2693
|
-
return
|
|
3164
|
+
return () => {
|
|
3165
|
+
unsubscribePanels();
|
|
3166
|
+
unsubscribeTimelines();
|
|
3167
|
+
};
|
|
2694
3168
|
}, []);
|
|
2695
|
-
|
|
3169
|
+
useEffect9(() => {
|
|
2696
3170
|
const fallbackOpen = inline || defaultOpen;
|
|
2697
3171
|
const nextStates = /* @__PURE__ */ new Map();
|
|
2698
3172
|
for (const panel of panels) {
|
|
@@ -2701,7 +3175,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2701
3175
|
panelOpenStatesRef.current = nextStates;
|
|
2702
3176
|
rootOpenRef.current = Array.from(nextStates.values()).some(Boolean);
|
|
2703
3177
|
}, [defaultOpen, inline, panels]);
|
|
2704
|
-
|
|
3178
|
+
useEffect9(() => {
|
|
2705
3179
|
if (!panelRef.current || inline) return;
|
|
2706
3180
|
const observer = new MutationObserver(() => {
|
|
2707
3181
|
const inners = panelRef.current?.querySelectorAll(".dialkit-panel-inner");
|
|
@@ -2729,7 +3203,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2729
3203
|
observer.observe(panelRef.current, { subtree: true, attributes: true, attributeFilter: ["data-collapsed"] });
|
|
2730
3204
|
return () => observer.disconnect();
|
|
2731
3205
|
}, [inline, dragOffset, position]);
|
|
2732
|
-
const handlePointerDown =
|
|
3206
|
+
const handlePointerDown = useCallback12((e) => {
|
|
2733
3207
|
const panel = panelRef.current;
|
|
2734
3208
|
const handle = getPanelDragHandle(e.target, panel);
|
|
2735
3209
|
if (!panel || !handle) return;
|
|
@@ -2739,13 +3213,13 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2739
3213
|
draggingRef.current = true;
|
|
2740
3214
|
handle.setPointerCapture(e.pointerId);
|
|
2741
3215
|
}, []);
|
|
2742
|
-
const handlePointerMove =
|
|
3216
|
+
const handlePointerMove = useCallback12((e) => {
|
|
2743
3217
|
if (!draggingRef.current || !dragStartRef.current) return;
|
|
2744
3218
|
if (!didDragRef.current && !hasPanelDragMoved(dragStartRef.current, e.clientX, e.clientY)) return;
|
|
2745
3219
|
didDragRef.current = true;
|
|
2746
3220
|
setDragOffset(getPanelDragOffset(dragStartRef.current, e.clientX, e.clientY));
|
|
2747
3221
|
}, []);
|
|
2748
|
-
const handlePointerUp =
|
|
3222
|
+
const handlePointerUp = useCallback12((e) => {
|
|
2749
3223
|
if (!draggingRef.current) return;
|
|
2750
3224
|
draggingRef.current = false;
|
|
2751
3225
|
dragStartRef.current = null;
|
|
@@ -2761,7 +3235,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2761
3235
|
}
|
|
2762
3236
|
dragTargetRef.current = null;
|
|
2763
3237
|
}, []);
|
|
2764
|
-
const handlePanelOpenChange =
|
|
3238
|
+
const handlePanelOpenChange = useCallback12((panelId, open) => {
|
|
2765
3239
|
panelOpenStatesRef.current.set(panelId, open);
|
|
2766
3240
|
const fallbackOpen = inline || defaultOpen;
|
|
2767
3241
|
const nextRootOpen = panels.some((panel) => panelOpenStatesRef.current.get(panel.id) ?? fallbackOpen);
|
|
@@ -2769,7 +3243,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2769
3243
|
rootOpenRef.current = nextRootOpen;
|
|
2770
3244
|
onOpenChange?.(nextRootOpen);
|
|
2771
3245
|
}, [defaultOpen, inline, onOpenChange, panels]);
|
|
2772
|
-
const handleRootOpenChange =
|
|
3246
|
+
const handleRootOpenChange = useCallback12((open) => {
|
|
2773
3247
|
if (rootOpenRef.current === open) return;
|
|
2774
3248
|
rootOpenRef.current = open;
|
|
2775
3249
|
onOpenChange?.(open);
|
|
@@ -2777,7 +3251,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2777
3251
|
if (!mounted || typeof window === "undefined") {
|
|
2778
3252
|
return null;
|
|
2779
3253
|
}
|
|
2780
|
-
if (panels.length === 0) {
|
|
3254
|
+
if (panels.length === 0 && timelineCount === 0) {
|
|
2781
3255
|
return null;
|
|
2782
3256
|
}
|
|
2783
3257
|
const dragStyle = dragOffset ? {
|
|
@@ -2788,7 +3262,8 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2788
3262
|
} : void 0;
|
|
2789
3263
|
const originX = getPanelOriginX(activePosition, dragOffset);
|
|
2790
3264
|
const hasMultiplePanels = panels.length > 1;
|
|
2791
|
-
const
|
|
3265
|
+
const timelineToggle = timelineCount > 0 ? /* @__PURE__ */ jsx17(TimelineToggleButton, {}) : null;
|
|
3266
|
+
const content = /* @__PURE__ */ jsx17(ShortcutListener, { children: /* @__PURE__ */ jsx17("div", { className: "dialkit-root", "data-mode": mode, "data-theme": theme, children: /* @__PURE__ */ jsx17(
|
|
2792
3267
|
"div",
|
|
2793
3268
|
{
|
|
2794
3269
|
ref: panelRef,
|
|
@@ -2802,7 +3277,7 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2802
3277
|
onPointerMove: !inline ? handlePointerMove : void 0,
|
|
2803
3278
|
onPointerUp: !inline ? handlePointerUp : void 0,
|
|
2804
3279
|
onPointerCancel: !inline ? handlePointerUp : void 0,
|
|
2805
|
-
children:
|
|
3280
|
+
children: panels.length === 0 ? /* @__PURE__ */ jsx17("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx17(
|
|
2806
3281
|
Folder,
|
|
2807
3282
|
{
|
|
2808
3283
|
title: "DialKit",
|
|
@@ -2810,23 +3285,37 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2810
3285
|
isRoot: true,
|
|
2811
3286
|
inline,
|
|
2812
3287
|
onOpenChange: handleRootOpenChange,
|
|
3288
|
+
toolbar: timelineToggle,
|
|
2813
3289
|
panelHeightOffset: 2,
|
|
2814
|
-
children:
|
|
2815
|
-
Panel,
|
|
2816
|
-
{
|
|
2817
|
-
panel,
|
|
2818
|
-
defaultOpen: true,
|
|
2819
|
-
variant: "section"
|
|
2820
|
-
},
|
|
2821
|
-
panel.id
|
|
2822
|
-
))
|
|
3290
|
+
children: /* @__PURE__ */ jsx17("div", { className: "dialkit-timeline-toolkit-only", children: "Timeline" })
|
|
2823
3291
|
}
|
|
2824
|
-
) }) :
|
|
3292
|
+
) }) : hasMultiplePanels ? /* @__PURE__ */ jsx17("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx17(
|
|
3293
|
+
Folder,
|
|
3294
|
+
{
|
|
3295
|
+
title: "DialKit",
|
|
3296
|
+
defaultOpen: inline || defaultOpen,
|
|
3297
|
+
isRoot: true,
|
|
3298
|
+
inline,
|
|
3299
|
+
onOpenChange: handleRootOpenChange,
|
|
3300
|
+
toolbar: timelineToggle,
|
|
3301
|
+
panelHeightOffset: 2,
|
|
3302
|
+
children: panels.map((panel) => /* @__PURE__ */ jsx17(
|
|
3303
|
+
Panel,
|
|
3304
|
+
{
|
|
3305
|
+
panel,
|
|
3306
|
+
defaultOpen: true,
|
|
3307
|
+
variant: "section"
|
|
3308
|
+
},
|
|
3309
|
+
panel.id
|
|
3310
|
+
))
|
|
3311
|
+
}
|
|
3312
|
+
) }) : panels.map((panel) => /* @__PURE__ */ jsx17(
|
|
2825
3313
|
Panel,
|
|
2826
3314
|
{
|
|
2827
3315
|
panel,
|
|
2828
3316
|
defaultOpen: inline || defaultOpen,
|
|
2829
3317
|
inline,
|
|
3318
|
+
toolbarExtra: timelineToggle,
|
|
2830
3319
|
onOpenChange: (open) => handlePanelOpenChange(panel.id, open)
|
|
2831
3320
|
},
|
|
2832
3321
|
panel.id
|
|
@@ -2839,10 +3328,2149 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
|
|
|
2839
3328
|
return createPortal3(content, document.body);
|
|
2840
3329
|
}
|
|
2841
3330
|
|
|
3331
|
+
// src/hooks/useDialTimeline.ts
|
|
3332
|
+
import { useCallback as useCallback13, useEffect as useEffect10, useMemo as useMemo2, useRef as useRef13, useSyncExternalStore as useSyncExternalStore6 } from "react";
|
|
3333
|
+
|
|
3334
|
+
// src/timeline-core.ts
|
|
3335
|
+
var CLIP_VALUE_STEP = 0.01;
|
|
3336
|
+
var TIMELINE_MIN_CLIP_DURATION = 0.05;
|
|
3337
|
+
var DEFAULT_STEP_DURATION = 0.3;
|
|
3338
|
+
var DEFAULT_CLIP_TRANSITION = { type: "spring", bounce: 0.2 };
|
|
3339
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["time", "playing", "duration", "play", "pause", "replay", "seek"]);
|
|
3340
|
+
function isClipConfig(value) {
|
|
3341
|
+
return isPlainObject(value) && Number.isFinite(value.at);
|
|
3342
|
+
}
|
|
3343
|
+
function isGroupConfig(value) {
|
|
3344
|
+
if (!isPlainObject(value) || "at" in value) return false;
|
|
3345
|
+
const entries = Object.values(value);
|
|
3346
|
+
return entries.length > 0 && entries.some(isClipConfig);
|
|
3347
|
+
}
|
|
3348
|
+
function isPlainObject(value) {
|
|
3349
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3350
|
+
}
|
|
3351
|
+
function nonNegativeFinite(value, fallback = 0) {
|
|
3352
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : fallback;
|
|
3353
|
+
}
|
|
3354
|
+
function animatedDuration(value, fallback = DEFAULT_STEP_DURATION) {
|
|
3355
|
+
return Math.max(TIMELINE_MIN_CLIP_DURATION, nonNegativeFinite(value, fallback));
|
|
3356
|
+
}
|
|
3357
|
+
function transitionDefaultDuration(transition) {
|
|
3358
|
+
if (transition.type === "easing") return animatedDuration(transition.duration);
|
|
3359
|
+
if (isPhysicsSpring(transition)) {
|
|
3360
|
+
return animatedDuration(springSettleDuration(springParams(transition)));
|
|
3361
|
+
}
|
|
3362
|
+
if (transition.visualDuration !== void 0) return animatedDuration(transition.visualDuration);
|
|
3363
|
+
return animatedDuration(springSettleDuration(springParams(transition)));
|
|
3364
|
+
}
|
|
3365
|
+
function defaultStepDuration(step, inheritedTransition) {
|
|
3366
|
+
const curve = step.transition ?? inheritedTransition;
|
|
3367
|
+
if (curve && isPhysicsSpring(curve)) return transitionDefaultDuration(curve);
|
|
3368
|
+
if (step.duration !== void 0) return animatedDuration(step.duration);
|
|
3369
|
+
if (step.transition) return transitionDefaultDuration(step.transition);
|
|
3370
|
+
return DEFAULT_STEP_DURATION;
|
|
3371
|
+
}
|
|
3372
|
+
function defaultTrackDuration(track, inheritedTransition) {
|
|
3373
|
+
const curve = track.transition ?? inheritedTransition;
|
|
3374
|
+
if (track.steps?.length) {
|
|
3375
|
+
return track.steps.reduce((sum, step) => sum + defaultStepDuration(step, curve), 0);
|
|
3376
|
+
}
|
|
3377
|
+
if (curve && isPhysicsSpring(curve)) return transitionDefaultDuration(curve);
|
|
3378
|
+
if (track.duration !== void 0) return animatedDuration(track.duration);
|
|
3379
|
+
if (track.transition) return transitionDefaultDuration(track.transition);
|
|
3380
|
+
return DEFAULT_STEP_DURATION;
|
|
3381
|
+
}
|
|
3382
|
+
function defaultClipDuration(clip) {
|
|
3383
|
+
const defaultCurve = isTransitionConfig(clip.transition) ? clip.transition : DEFAULT_CLIP_TRANSITION;
|
|
3384
|
+
if (clip.props) {
|
|
3385
|
+
return Object.values(clip.props).reduce(
|
|
3386
|
+
(max, track) => Math.max(
|
|
3387
|
+
max,
|
|
3388
|
+
nonNegativeFinite(track.delay) + defaultTrackDuration(track, defaultCurve)
|
|
3389
|
+
),
|
|
3390
|
+
0
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3393
|
+
if (clip.steps?.length) {
|
|
3394
|
+
return clip.steps.reduce((sum, step) => sum + defaultStepDuration(step, defaultCurve), 0);
|
|
3395
|
+
}
|
|
3396
|
+
const animating = Boolean(clip.transition || clip.from || clip.to);
|
|
3397
|
+
if (!animating) return nonNegativeFinite(clip.duration);
|
|
3398
|
+
if (isPhysicsSpring(defaultCurve)) return transitionDefaultDuration(defaultCurve);
|
|
3399
|
+
if (clip.duration !== void 0) return animatedDuration(clip.duration);
|
|
3400
|
+
if (isTransitionConfig(clip.transition)) return transitionDefaultDuration(clip.transition);
|
|
3401
|
+
return clip.from || clip.to ? transitionDefaultDuration(DEFAULT_CLIP_TRANSITION) : 0;
|
|
3402
|
+
}
|
|
3403
|
+
function normalizeLoopMode(value) {
|
|
3404
|
+
if (value === true || value === "mirror" || value === "repeat") return "repeat";
|
|
3405
|
+
return "off";
|
|
3406
|
+
}
|
|
3407
|
+
function normalizeStoredTransition(transition, clipDuration) {
|
|
3408
|
+
if (transition.type === "easing") {
|
|
3409
|
+
return { ...transition, duration: clipDuration };
|
|
3410
|
+
}
|
|
3411
|
+
if (isPhysicsSpring(transition)) {
|
|
3412
|
+
return transition;
|
|
3413
|
+
}
|
|
3414
|
+
return { type: "spring", bounce: transition.bounce ?? 0.2 };
|
|
3415
|
+
}
|
|
3416
|
+
function collectClipEntries(config) {
|
|
3417
|
+
const entries = [];
|
|
3418
|
+
for (const [key, value] of Object.entries(config)) {
|
|
3419
|
+
if (key === "duration") continue;
|
|
3420
|
+
if (RESERVED_KEYS.has(key)) {
|
|
3421
|
+
console.warn(`[dialkit] Timeline key "${key}" collides with a reserved key and was skipped.`);
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3424
|
+
if (isClipConfig(value)) {
|
|
3425
|
+
entries.push({ path: key, childKey: key, clip: value });
|
|
3426
|
+
} else if (isGroupConfig(value)) {
|
|
3427
|
+
for (const [childKey, childClip] of Object.entries(value)) {
|
|
3428
|
+
if (isClipConfig(childClip)) {
|
|
3429
|
+
entries.push({ path: `${key}.${childKey}`, childKey, group: key, clip: childClip });
|
|
3430
|
+
} else {
|
|
3431
|
+
console.warn(
|
|
3432
|
+
`[dialkit] Timeline clip "${key}.${childKey}" is missing a numeric "at" and was skipped.`
|
|
3433
|
+
);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
} else {
|
|
3437
|
+
console.warn(
|
|
3438
|
+
`[dialkit] Timeline entry "${key}" is neither a clip (needs a numeric "at") nor a group of clips and was skipped.`
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
return entries;
|
|
3443
|
+
}
|
|
3444
|
+
function definedValues(values) {
|
|
3445
|
+
if (!values) return void 0;
|
|
3446
|
+
const result = {};
|
|
3447
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3448
|
+
if (value !== void 0) result[key] = value;
|
|
3449
|
+
}
|
|
3450
|
+
return result;
|
|
3451
|
+
}
|
|
3452
|
+
function setDialPath(dialConfig, path, value) {
|
|
3453
|
+
const segments = path.split(".");
|
|
3454
|
+
let node = dialConfig;
|
|
3455
|
+
for (const segment of segments.slice(0, -1)) {
|
|
3456
|
+
node = node[segment] ?? (node[segment] = {});
|
|
3457
|
+
}
|
|
3458
|
+
node[segments[segments.length - 1]] = value;
|
|
3459
|
+
}
|
|
3460
|
+
function parseTimelineConfig(config) {
|
|
3461
|
+
const entries = collectClipEntries(config);
|
|
3462
|
+
let maxEnd = 0;
|
|
3463
|
+
for (const { clip } of entries) {
|
|
3464
|
+
maxEnd = Math.max(maxEnd, nonNegativeFinite(clip.at) + defaultClipDuration(clip));
|
|
3465
|
+
}
|
|
3466
|
+
const duration = typeof config.duration === "number" && Number.isFinite(config.duration) && config.duration > 0 ? config.duration : maxEnd > 0 ? Math.ceil(maxEnd * 100 - 1e-4) / 100 : 1;
|
|
3467
|
+
const dialConfig = {};
|
|
3468
|
+
const clips = [];
|
|
3469
|
+
entries.forEach(({ path, childKey, group, clip }, index) => {
|
|
3470
|
+
const raw = clip;
|
|
3471
|
+
if (raw.props && (raw.steps?.length || raw.from || raw.to)) {
|
|
3472
|
+
console.warn(
|
|
3473
|
+
`[dialkit] Timeline clip "${path}": "props" is mutually exclusive with from/to/steps \u2014 using "props".`
|
|
3474
|
+
);
|
|
3475
|
+
} else if (raw.steps?.length && raw.to) {
|
|
3476
|
+
console.warn(
|
|
3477
|
+
`[dialkit] Timeline clip "${path}": "to" is ignored when "steps" is present \u2014 each leg's "to" defines its targets.`
|
|
3478
|
+
);
|
|
3479
|
+
}
|
|
3480
|
+
const hasSteps = Boolean(clip.steps?.length) && !clip.props;
|
|
3481
|
+
const hasProps = Boolean(clip.props);
|
|
3482
|
+
const single = isTransitionConfig(clip.transition) ? clip.transition : void 0;
|
|
3483
|
+
const total = defaultClipDuration(clip);
|
|
3484
|
+
const defaultCurve = single ?? DEFAULT_CLIP_TRANSITION;
|
|
3485
|
+
const clipAt = nonNegativeFinite(clip.at);
|
|
3486
|
+
const clipDial = {
|
|
3487
|
+
at: [clipAt, 0, duration, CLIP_VALUE_STEP]
|
|
3488
|
+
};
|
|
3489
|
+
if (!hasSteps && !hasProps) {
|
|
3490
|
+
clipDial.duration = [total, 0, duration, CLIP_VALUE_STEP];
|
|
3491
|
+
}
|
|
3492
|
+
if (!hasSteps && !hasProps && (clip.transition || clip.from || clip.to)) {
|
|
3493
|
+
clipDial.transition = normalizeStoredTransition(defaultCurve, total);
|
|
3494
|
+
}
|
|
3495
|
+
let tracks;
|
|
3496
|
+
if (clip.props) {
|
|
3497
|
+
tracks = [];
|
|
3498
|
+
for (const [prop, track] of Object.entries(clip.props)) {
|
|
3499
|
+
if (TRACK_RESERVED.has(prop) || /^step\d+$/.test(prop)) {
|
|
3500
|
+
console.warn(`[dialkit] Timeline property "${prop}" collides with a clip field and was skipped.`);
|
|
3501
|
+
continue;
|
|
3502
|
+
}
|
|
3503
|
+
const trackDuration = defaultTrackDuration(track, defaultCurve);
|
|
3504
|
+
const trackCurve = track.transition ?? defaultCurve;
|
|
3505
|
+
const hasTrackSteps = Boolean(track.steps?.length);
|
|
3506
|
+
const trackDial = {
|
|
3507
|
+
delay: [nonNegativeFinite(track.delay), 0, duration, CLIP_VALUE_STEP]
|
|
3508
|
+
};
|
|
3509
|
+
if (!hasTrackSteps) {
|
|
3510
|
+
trackDial.duration = [trackDuration, 0, duration, CLIP_VALUE_STEP];
|
|
3511
|
+
trackDial.transition = normalizeStoredTransition(trackCurve, trackDuration);
|
|
3512
|
+
}
|
|
3513
|
+
const fromValue = track.from ?? (hasTrackSteps ? void 0 : track.to);
|
|
3514
|
+
if (hasTrackSteps && fromValue === void 0) {
|
|
3515
|
+
console.warn(
|
|
3516
|
+
`[dialkit] Timeline clip "${path}": track "${prop}" has steps but no "from" \u2014 declare its starting value.`
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
if (fromValue !== void 0) {
|
|
3520
|
+
trackDial.from = scalarDial(prop, fromValue, hasTrackSteps ? track.steps[0]?.to : track.to);
|
|
3521
|
+
}
|
|
3522
|
+
if (!hasTrackSteps && track.to !== void 0) {
|
|
3523
|
+
trackDial.to = scalarDial(prop, track.to, fromValue);
|
|
3524
|
+
}
|
|
3525
|
+
let trackStepKeys;
|
|
3526
|
+
if (hasTrackSteps) {
|
|
3527
|
+
trackStepKeys = [];
|
|
3528
|
+
let previous = fromValue;
|
|
3529
|
+
track.steps.forEach((step, stepIndex) => {
|
|
3530
|
+
const stepKey = `step${stepIndex + 1}`;
|
|
3531
|
+
trackStepKeys.push(stepKey);
|
|
3532
|
+
const stepDuration = defaultStepDuration(step, trackCurve);
|
|
3533
|
+
const stepDial = {
|
|
3534
|
+
duration: [stepDuration, 0, duration, CLIP_VALUE_STEP],
|
|
3535
|
+
transition: normalizeStoredTransition(step.transition ?? trackCurve, stepDuration)
|
|
3536
|
+
};
|
|
3537
|
+
if (step.to !== void 0) {
|
|
3538
|
+
stepDial.to = scalarDial(prop, step.to, previous);
|
|
3539
|
+
previous = step.to;
|
|
3540
|
+
}
|
|
3541
|
+
trackDial[stepKey] = stepDial;
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
clipDial[prop] = trackDial;
|
|
3545
|
+
tracks.push({ prop, stepKeys: trackStepKeys });
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
if (clip.from && !hasProps) {
|
|
3549
|
+
clipDial.from = withFromToRanges(
|
|
3550
|
+
clip.from,
|
|
3551
|
+
hasSteps ? definedValues(clip.steps[0]?.to) : clip.to
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
if (!hasSteps && !hasProps && clip.to) {
|
|
3555
|
+
clipDial.to = withFromToRanges(clip.to, clip.from);
|
|
3556
|
+
}
|
|
3557
|
+
let stepKeys;
|
|
3558
|
+
if (hasSteps) {
|
|
3559
|
+
stepKeys = [];
|
|
3560
|
+
let running = clip.from;
|
|
3561
|
+
clip.steps.forEach((step, stepIndex) => {
|
|
3562
|
+
const stepKey = `step${stepIndex + 1}`;
|
|
3563
|
+
stepKeys.push(stepKey);
|
|
3564
|
+
const stepDuration = defaultStepDuration(step, defaultCurve);
|
|
3565
|
+
const stepDial = {
|
|
3566
|
+
duration: [stepDuration, 0, duration, CLIP_VALUE_STEP],
|
|
3567
|
+
transition: normalizeStoredTransition(step.transition ?? defaultCurve, stepDuration)
|
|
3568
|
+
};
|
|
3569
|
+
const stepTo = definedValues(step.to);
|
|
3570
|
+
if (stepTo) {
|
|
3571
|
+
for (const prop of Object.keys(stepTo)) {
|
|
3572
|
+
if (!running || !(prop in running)) {
|
|
3573
|
+
console.warn(
|
|
3574
|
+
`[dialkit] Timeline clip "${path}": property "${prop}" first animates in step ${stepIndex + 1} with no starting value \u2014 declare it in "from".`
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
stepDial.to = withFromToRanges(stepTo, running);
|
|
3579
|
+
}
|
|
3580
|
+
clipDial[stepKey] = stepDial;
|
|
3581
|
+
running = { ...running ?? {}, ...stepTo ?? {} };
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
setDialPath(dialConfig, path, clipDial);
|
|
3585
|
+
clips.push({
|
|
3586
|
+
key: path,
|
|
3587
|
+
label: formatLabel(childKey),
|
|
3588
|
+
color: TIMELINE_CLIP_COLORS[index % TIMELINE_CLIP_COLORS.length],
|
|
3589
|
+
loop: normalizeLoopMode(clip.loop),
|
|
3590
|
+
group,
|
|
3591
|
+
stepKeys,
|
|
3592
|
+
tracks
|
|
3593
|
+
});
|
|
3594
|
+
});
|
|
3595
|
+
return { duration, dialConfig, clips };
|
|
3596
|
+
}
|
|
3597
|
+
var TRACK_RESERVED = /* @__PURE__ */ new Set(["at", "duration", "loop", "from", "to", "transition", "delay"]);
|
|
3598
|
+
function scalarDial(prop, value, counterpart) {
|
|
3599
|
+
const record = withFromToRanges(
|
|
3600
|
+
{ [prop]: value },
|
|
3601
|
+
counterpart === void 0 ? void 0 : { [prop]: counterpart }
|
|
3602
|
+
);
|
|
3603
|
+
return record[prop];
|
|
3604
|
+
}
|
|
3605
|
+
var FROM_TO_RANGE_PRESETS = [
|
|
3606
|
+
[/^(x|y|z|tx|ty|offsetx|offsety|translatex|translatey)$/i, { min: -100, max: 100, step: 1 }],
|
|
3607
|
+
[/rotat|angle|skew/i, { min: -180, max: 180, step: 1 }],
|
|
3608
|
+
[/^scale/i, { min: 0, max: 2, step: 0.01 }],
|
|
3609
|
+
[/opacity|alpha/i, { min: 0, max: 1, step: 0.01 }],
|
|
3610
|
+
[/blur|radius|spread/i, { min: 0, max: 100, step: 1 }]
|
|
3611
|
+
];
|
|
3612
|
+
function inferFromToRange(key, value, counterpart) {
|
|
3613
|
+
const lo = Math.min(value, counterpart ?? value);
|
|
3614
|
+
const hi = Math.max(value, counterpart ?? value);
|
|
3615
|
+
const preset = FROM_TO_RANGE_PRESETS.find(([pattern]) => pattern.test(key))?.[1];
|
|
3616
|
+
if (preset) {
|
|
3617
|
+
return [value, Math.min(preset.min, lo), Math.max(preset.max, hi), preset.step];
|
|
3618
|
+
}
|
|
3619
|
+
if (lo >= 0 && hi <= 1) {
|
|
3620
|
+
return [value, 0, 1, 0.01];
|
|
3621
|
+
}
|
|
3622
|
+
const extent = Math.max(Math.abs(lo), Math.abs(hi), 1);
|
|
3623
|
+
const min = lo < 0 ? -extent * 2 : 0;
|
|
3624
|
+
const max = Math.max(extent * 2, hi);
|
|
3625
|
+
return [value, min, max, inferStep(min, max)];
|
|
3626
|
+
}
|
|
3627
|
+
function withFromToRanges(config, counterpart) {
|
|
3628
|
+
const result = {};
|
|
3629
|
+
for (const [key, value] of Object.entries(config)) {
|
|
3630
|
+
const other = counterpart?.[key];
|
|
3631
|
+
if (typeof value === "number") {
|
|
3632
|
+
result[key] = inferFromToRange(key, value, typeof other === "number" ? other : void 0);
|
|
3633
|
+
} else if (isPlainObject(value) && !("type" in value)) {
|
|
3634
|
+
result[key] = withFromToRanges(
|
|
3635
|
+
value,
|
|
3636
|
+
isPlainObject(other) && !("type" in other) ? other : void 0
|
|
3637
|
+
);
|
|
3638
|
+
} else {
|
|
3639
|
+
result[key] = value;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
return result;
|
|
3643
|
+
}
|
|
3644
|
+
function curveStatic(transition, duration) {
|
|
3645
|
+
if (!transition) return { duration };
|
|
3646
|
+
if (transition.type === "easing") return { duration, ease: transition.ease };
|
|
3647
|
+
const spring = springParams(transition);
|
|
3648
|
+
return { duration, spring, settle: springSettleDuration(spring) };
|
|
3649
|
+
}
|
|
3650
|
+
function sampleCurve(curve, elapsed) {
|
|
3651
|
+
if (elapsed <= 0) return 0;
|
|
3652
|
+
if (curve.spring) {
|
|
3653
|
+
if (curve.settle !== void 0 && elapsed >= curve.settle) return 1;
|
|
3654
|
+
return springProgress(elapsed, curve.spring);
|
|
3655
|
+
}
|
|
3656
|
+
if (curve.ease) {
|
|
3657
|
+
return cubicBezierProgress(clamp(curve.duration > 0 ? elapsed / curve.duration : 1, 0, 1), curve.ease);
|
|
3658
|
+
}
|
|
3659
|
+
return curve.duration > 0 ? Math.min(1, elapsed / curve.duration) : 1;
|
|
3660
|
+
}
|
|
3661
|
+
function resolvedAtPath(resolved, path) {
|
|
3662
|
+
let node = resolved;
|
|
3663
|
+
for (const segment of path.split(".")) {
|
|
3664
|
+
node = isPlainObject(node) ? node[segment] : void 0;
|
|
3665
|
+
}
|
|
3666
|
+
return isPlainObject(node) ? node : {};
|
|
3667
|
+
}
|
|
3668
|
+
function computeStaticClips(parsed, flatValues) {
|
|
3669
|
+
const resolved = resolveDialValues(parsed.dialConfig, flatValues);
|
|
3670
|
+
return parsed.clips.map(
|
|
3671
|
+
(clip) => buildClipStatic(resolvedAtPath(resolved, clip.key), clip, parsed.duration)
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
function computeStaticTimeline(parsed, flatValues) {
|
|
3675
|
+
let clips = computeStaticClips(parsed, flatValues);
|
|
3676
|
+
const maxEnd = clips.reduce(
|
|
3677
|
+
(end, clip) => Math.max(end, clip.at + clip.duration),
|
|
3678
|
+
parsed.duration
|
|
3679
|
+
);
|
|
3680
|
+
const duration = maxEnd > parsed.duration ? Math.ceil(maxEnd * 100 - 1e-4) / 100 : parsed.duration;
|
|
3681
|
+
if (duration !== parsed.duration) {
|
|
3682
|
+
clips = clips.map(
|
|
3683
|
+
(clip) => clip.loop === "repeat" ? { ...clip, end: duration } : clip
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
return { duration, clips };
|
|
3687
|
+
}
|
|
3688
|
+
function computeClipStaticFromValues(values, clip, timelineDuration) {
|
|
3689
|
+
return buildClipStatic(unflattenClipValues(values, clip.key), clip, timelineDuration);
|
|
3690
|
+
}
|
|
3691
|
+
function buildClipStatic(clipResolved, clip, timelineDuration) {
|
|
3692
|
+
{
|
|
3693
|
+
const at = typeof clipResolved.at === "number" ? clipResolved.at : 0;
|
|
3694
|
+
const from = isPlainObject(clipResolved.from) ? clipResolved.from : void 0;
|
|
3695
|
+
const single = isTransitionConfig(clipResolved.transition) ? clipResolved.transition : void 0;
|
|
3696
|
+
const staticClip = {
|
|
3697
|
+
key: clip.key,
|
|
3698
|
+
childKey: clip.group ? clip.key.slice(clip.group.length + 1) : clip.key,
|
|
3699
|
+
group: clip.group,
|
|
3700
|
+
at,
|
|
3701
|
+
duration: 0,
|
|
3702
|
+
loop: "off",
|
|
3703
|
+
end: 0,
|
|
3704
|
+
isPhysics: false,
|
|
3705
|
+
from,
|
|
3706
|
+
tracks: [],
|
|
3707
|
+
explicitSteps: Boolean(clip.stepKeys?.length)
|
|
3708
|
+
};
|
|
3709
|
+
if (clip.tracks?.length) {
|
|
3710
|
+
const tracks = clip.tracks.map(({ prop, stepKeys }) => {
|
|
3711
|
+
const trackResolved = isPlainObject(clipResolved[prop]) ? clipResolved[prop] : {};
|
|
3712
|
+
const delay = typeof trackResolved.delay === "number" ? trackResolved.delay : 0;
|
|
3713
|
+
const fromValue = trackResolved.from;
|
|
3714
|
+
let steps;
|
|
3715
|
+
let trackDuration = 0;
|
|
3716
|
+
if (stepKeys?.length) {
|
|
3717
|
+
let running = fromValue;
|
|
3718
|
+
steps = stepKeys.map((stepKey) => {
|
|
3719
|
+
const stepResolved = isPlainObject(trackResolved[stepKey]) ? trackResolved[stepKey] : {};
|
|
3720
|
+
const storedDuration = typeof stepResolved.duration === "number" ? stepResolved.duration : 0;
|
|
3721
|
+
const raw = isTransitionConfig(stepResolved.transition) ? stepResolved.transition : void 0;
|
|
3722
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
3723
|
+
const toValue = stepResolved.to;
|
|
3724
|
+
const step = {
|
|
3725
|
+
key: stepKey,
|
|
3726
|
+
offset: trackDuration,
|
|
3727
|
+
duration: effective.duration,
|
|
3728
|
+
isPhysics: effective.isPhysics,
|
|
3729
|
+
start: running === void 0 ? {} : { [prop]: running },
|
|
3730
|
+
to: toValue === void 0 ? {} : { [prop]: toValue },
|
|
3731
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
3732
|
+
};
|
|
3733
|
+
if (toValue !== void 0) running = toValue;
|
|
3734
|
+
trackDuration += effective.duration;
|
|
3735
|
+
return step;
|
|
3736
|
+
});
|
|
3737
|
+
} else {
|
|
3738
|
+
const storedDuration = typeof trackResolved.duration === "number" ? trackResolved.duration : 0;
|
|
3739
|
+
const raw = isTransitionConfig(trackResolved.transition) ? trackResolved.transition : void 0;
|
|
3740
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
3741
|
+
const toValue = trackResolved.to;
|
|
3742
|
+
trackDuration = effective.duration;
|
|
3743
|
+
steps = [
|
|
3744
|
+
{
|
|
3745
|
+
key: null,
|
|
3746
|
+
offset: 0,
|
|
3747
|
+
duration: effective.duration,
|
|
3748
|
+
isPhysics: effective.isPhysics,
|
|
3749
|
+
start: fromValue === void 0 ? {} : { [prop]: fromValue },
|
|
3750
|
+
to: toValue === void 0 ? {} : { [prop]: toValue },
|
|
3751
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
3752
|
+
}
|
|
3753
|
+
];
|
|
3754
|
+
}
|
|
3755
|
+
return { prop, delay, duration: trackDuration, steps };
|
|
3756
|
+
});
|
|
3757
|
+
staticClip.tracks = tracks;
|
|
3758
|
+
staticClip.props = tracks.map((track) => track.prop);
|
|
3759
|
+
staticClip.duration = tracks.reduce((max, track) => Math.max(max, track.delay + track.duration), 0);
|
|
3760
|
+
staticClip.from = Object.fromEntries(
|
|
3761
|
+
tracks.map((track) => [track.prop, track.steps[0].start[track.prop]])
|
|
3762
|
+
);
|
|
3763
|
+
staticClip.to = Object.fromEntries(
|
|
3764
|
+
tracks.map((track) => {
|
|
3765
|
+
const last = track.steps[track.steps.length - 1];
|
|
3766
|
+
return [track.prop, last.to[track.prop] ?? last.start[track.prop]];
|
|
3767
|
+
})
|
|
3768
|
+
);
|
|
3769
|
+
staticClip.loop = staticClip.duration > 0 ? clip.loop : "off";
|
|
3770
|
+
staticClip.end = staticClip.loop === "off" ? staticClip.at + staticClip.duration : timelineDuration;
|
|
3771
|
+
return staticClip;
|
|
3772
|
+
}
|
|
3773
|
+
if (clip.stepKeys?.length) {
|
|
3774
|
+
let running = { ...from ?? {} };
|
|
3775
|
+
let offset = 0;
|
|
3776
|
+
const steps = clip.stepKeys.map((stepKey) => {
|
|
3777
|
+
const stepResolved = isPlainObject(clipResolved[stepKey]) ? clipResolved[stepKey] : {};
|
|
3778
|
+
const storedDuration = typeof stepResolved.duration === "number" ? stepResolved.duration : 0;
|
|
3779
|
+
const raw = isTransitionConfig(stepResolved.transition) ? stepResolved.transition : void 0;
|
|
3780
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
3781
|
+
const to = isPlainObject(stepResolved.to) ? stepResolved.to : {};
|
|
3782
|
+
const step = {
|
|
3783
|
+
key: stepKey,
|
|
3784
|
+
offset,
|
|
3785
|
+
duration: effective.duration,
|
|
3786
|
+
isPhysics: effective.isPhysics,
|
|
3787
|
+
start: running,
|
|
3788
|
+
to,
|
|
3789
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
3790
|
+
};
|
|
3791
|
+
running = { ...running, ...to };
|
|
3792
|
+
offset += effective.duration;
|
|
3793
|
+
return step;
|
|
3794
|
+
});
|
|
3795
|
+
staticClip.tracks = [{ delay: 0, duration: offset, steps }];
|
|
3796
|
+
staticClip.duration = offset;
|
|
3797
|
+
staticClip.to = running;
|
|
3798
|
+
} else {
|
|
3799
|
+
const storedDuration = typeof clipResolved.duration === "number" ? clipResolved.duration : 0;
|
|
3800
|
+
const to = isPlainObject(clipResolved.to) ? clipResolved.to : void 0;
|
|
3801
|
+
if (single) {
|
|
3802
|
+
const effective = resolveClipTransition(single, storedDuration);
|
|
3803
|
+
staticClip.duration = effective.duration;
|
|
3804
|
+
staticClip.isPhysics = effective.isPhysics;
|
|
3805
|
+
staticClip.transition = effective.transition;
|
|
3806
|
+
staticClip.css = transitionToCss(effective.transition);
|
|
3807
|
+
staticClip.to = to;
|
|
3808
|
+
if (from && to) {
|
|
3809
|
+
staticClip.tracks = [
|
|
3810
|
+
{
|
|
3811
|
+
delay: 0,
|
|
3812
|
+
duration: effective.duration,
|
|
3813
|
+
steps: [
|
|
3814
|
+
{
|
|
3815
|
+
key: null,
|
|
3816
|
+
offset: 0,
|
|
3817
|
+
duration: effective.duration,
|
|
3818
|
+
isPhysics: effective.isPhysics,
|
|
3819
|
+
start: from,
|
|
3820
|
+
to,
|
|
3821
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
3822
|
+
}
|
|
3823
|
+
]
|
|
3824
|
+
}
|
|
3825
|
+
];
|
|
3826
|
+
}
|
|
3827
|
+
} else {
|
|
3828
|
+
staticClip.duration = storedDuration;
|
|
3829
|
+
staticClip.to = to;
|
|
3830
|
+
if (from && to) {
|
|
3831
|
+
const base = resolveClipTransition(DEFAULT_CLIP_TRANSITION, storedDuration);
|
|
3832
|
+
staticClip.duration = base.duration;
|
|
3833
|
+
staticClip.tracks = [
|
|
3834
|
+
{
|
|
3835
|
+
delay: 0,
|
|
3836
|
+
duration: base.duration,
|
|
3837
|
+
steps: [
|
|
3838
|
+
{
|
|
3839
|
+
key: null,
|
|
3840
|
+
offset: 0,
|
|
3841
|
+
duration: base.duration,
|
|
3842
|
+
isPhysics: false,
|
|
3843
|
+
start: from,
|
|
3844
|
+
to,
|
|
3845
|
+
curve: curveStatic(base.transition, base.duration)
|
|
3846
|
+
}
|
|
3847
|
+
]
|
|
3848
|
+
}
|
|
3849
|
+
];
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
if (staticClip.tracks.length) {
|
|
3854
|
+
const props = new Set(Object.keys(from ?? {}));
|
|
3855
|
+
for (const track of staticClip.tracks) {
|
|
3856
|
+
for (const step of track.steps) {
|
|
3857
|
+
for (const prop of Object.keys(step.to)) props.add(prop);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
staticClip.props = Array.from(props);
|
|
3861
|
+
}
|
|
3862
|
+
staticClip.loop = staticClip.duration > 0 ? clip.loop : "off";
|
|
3863
|
+
staticClip.end = staticClip.loop === "off" ? staticClip.at + staticClip.duration : timelineDuration;
|
|
3864
|
+
return staticClip;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
function stepAtPosition(steps, pos) {
|
|
3868
|
+
for (const step of steps) {
|
|
3869
|
+
if (pos < step.offset + step.duration) return step;
|
|
3870
|
+
}
|
|
3871
|
+
return steps[steps.length - 1];
|
|
3872
|
+
}
|
|
3873
|
+
function evalPropAtPos(steps, prop, pos) {
|
|
3874
|
+
const step = stepAtPosition(steps, pos);
|
|
3875
|
+
const within = Math.max(0, pos - step.offset);
|
|
3876
|
+
if (prop in step.to) {
|
|
3877
|
+
const eased = sampleCurve(step.curve, within);
|
|
3878
|
+
return interpolateResolved(step.start[prop], step.to[prop], eased);
|
|
3879
|
+
}
|
|
3880
|
+
return step.start[prop];
|
|
3881
|
+
}
|
|
3882
|
+
function computeClipState(clip, time, cycleTime = time) {
|
|
3883
|
+
const total = clip.duration;
|
|
3884
|
+
const looping = clip.loop === "repeat" && total > 0;
|
|
3885
|
+
const started = time >= clip.at || looping && cycleTime > time;
|
|
3886
|
+
const done = time >= clip.end;
|
|
3887
|
+
const elapsed = time - clip.at;
|
|
3888
|
+
const phaseElapsed = looping ? cycleTime - clip.at : elapsed;
|
|
3889
|
+
const fold = (e) => looping ? e % total : e;
|
|
3890
|
+
const basePos = started ? fold(Math.max(0, phaseElapsed)) : 0;
|
|
3891
|
+
const progress = total > 0 ? clamp(basePos / total, 0, 1) : started ? 1 : 0;
|
|
3892
|
+
let current;
|
|
3893
|
+
let stepIndex = 0;
|
|
3894
|
+
if (clip.tracks.length && clip.props?.length) {
|
|
3895
|
+
current = {};
|
|
3896
|
+
for (const track of clip.tracks) {
|
|
3897
|
+
const props = track.prop !== void 0 ? [track.prop] : clip.props;
|
|
3898
|
+
for (const prop of props) {
|
|
3899
|
+
const startValue = track.steps[0]?.start[prop];
|
|
3900
|
+
if (!started) {
|
|
3901
|
+
if (startValue !== void 0) current[prop] = startValue;
|
|
3902
|
+
continue;
|
|
3903
|
+
}
|
|
3904
|
+
const phase = phaseElapsed - track.delay;
|
|
3905
|
+
if (phase <= 0) {
|
|
3906
|
+
if (startValue !== void 0) current[prop] = startValue;
|
|
3907
|
+
continue;
|
|
3908
|
+
}
|
|
3909
|
+
const pos = looping && track.duration > 0 ? phase % track.duration : phase;
|
|
3910
|
+
const value = evalPropAtPos(track.steps, prop, pos);
|
|
3911
|
+
if (value !== void 0) current[prop] = value;
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
const shared = clip.tracks[0];
|
|
3915
|
+
if (started && clip.explicitSteps && shared.prop === void 0) {
|
|
3916
|
+
stepIndex = shared.steps.indexOf(stepAtPosition(shared.steps, basePos));
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
return {
|
|
3920
|
+
at: clip.at,
|
|
3921
|
+
duration: clip.duration,
|
|
3922
|
+
loop: clip.loop,
|
|
3923
|
+
started,
|
|
3924
|
+
active: started && !done,
|
|
3925
|
+
done,
|
|
3926
|
+
progress,
|
|
3927
|
+
step: clip.explicitSteps ? stepIndex : void 0,
|
|
3928
|
+
from: clip.from,
|
|
3929
|
+
to: clip.to,
|
|
3930
|
+
animate: started ? clip.to : clip.from,
|
|
3931
|
+
transition: clip.transition,
|
|
3932
|
+
css: clip.css,
|
|
3933
|
+
current
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
function interpolateResolved(from, to, p) {
|
|
3937
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
3938
|
+
return from + (to - from) * p;
|
|
3939
|
+
}
|
|
3940
|
+
if (typeof from === "string" && typeof to === "string") {
|
|
3941
|
+
const mixed = mixHexColors(from, to, p);
|
|
3942
|
+
if (mixed) return mixed;
|
|
3943
|
+
}
|
|
3944
|
+
if (isPlainObject(from) && isPlainObject(to)) {
|
|
3945
|
+
const result = {};
|
|
3946
|
+
for (const key of Object.keys(from)) {
|
|
3947
|
+
result[key] = key in to ? interpolateResolved(from[key], to[key], p) : from[key];
|
|
3948
|
+
}
|
|
3949
|
+
for (const key of Object.keys(to)) {
|
|
3950
|
+
if (!(key in from)) result[key] = to[key];
|
|
3951
|
+
}
|
|
3952
|
+
return result;
|
|
3953
|
+
}
|
|
3954
|
+
return p < 0.5 ? from : to;
|
|
3955
|
+
}
|
|
3956
|
+
function parseHex(hex) {
|
|
3957
|
+
if (!isHexColor(hex)) return null;
|
|
3958
|
+
let h = hex.slice(1);
|
|
3959
|
+
if (h.length === 3) h = h.split("").map((c) => c + c).join("");
|
|
3960
|
+
return [
|
|
3961
|
+
parseInt(h.slice(0, 2), 16),
|
|
3962
|
+
parseInt(h.slice(2, 4), 16),
|
|
3963
|
+
parseInt(h.slice(4, 6), 16),
|
|
3964
|
+
h.length === 8 ? parseInt(h.slice(6, 8), 16) : 255
|
|
3965
|
+
];
|
|
3966
|
+
}
|
|
3967
|
+
function mixHexColors(a, b, p) {
|
|
3968
|
+
const ca = parseHex(a);
|
|
3969
|
+
const cb = parseHex(b);
|
|
3970
|
+
if (!ca || !cb) return null;
|
|
3971
|
+
const t = clamp(p, 0, 1);
|
|
3972
|
+
const mixed = ca.map((v, i) => Math.round(v + (cb[i] - v) * t));
|
|
3973
|
+
const hex = (n) => n.toString(16).padStart(2, "0");
|
|
3974
|
+
const rgb = `#${hex(mixed[0])}${hex(mixed[1])}${hex(mixed[2])}`;
|
|
3975
|
+
return mixed[3] === 255 ? rgb : `${rgb}${hex(mixed[3])}`;
|
|
3976
|
+
}
|
|
3977
|
+
function transitionToCss(transition) {
|
|
3978
|
+
if (!transition) return void 0;
|
|
3979
|
+
if (transition.type === "easing") {
|
|
3980
|
+
return {
|
|
3981
|
+
transitionDuration: `${round2(transition.duration)}s`,
|
|
3982
|
+
transitionTimingFunction: `cubic-bezier(${transition.ease.map((v) => round2(v)).join(", ")})`
|
|
3983
|
+
};
|
|
3984
|
+
}
|
|
3985
|
+
const params = springParams(transition);
|
|
3986
|
+
const dampingRatio = params.damping / (2 * Math.sqrt(params.stiffness * params.mass));
|
|
3987
|
+
const duration = transition.visualDuration ?? springSettleDuration(params);
|
|
3988
|
+
const bounce = transition.bounce ?? Math.max(0, round2(1 - dampingRatio));
|
|
3989
|
+
return {
|
|
3990
|
+
transitionDuration: `${round2(duration)}s`,
|
|
3991
|
+
transitionTimingFunction: bounce > 0.05 ? `cubic-bezier(0.34, ${round2(1.2 + bounce)}, 0.64, 1)` : "cubic-bezier(0.25, 0.6, 0.35, 1)"
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
function timelinePopoverDisplayValues(values, clipKey, stepKeys, stepKey) {
|
|
3995
|
+
const display = { ...values };
|
|
3996
|
+
const swap = (path, duration) => {
|
|
3997
|
+
const raw = display[path];
|
|
3998
|
+
if (isTransitionConfig(raw)) display[path] = resolveClipTransition(raw, duration).transition;
|
|
3999
|
+
};
|
|
4000
|
+
if (stepKey) {
|
|
4001
|
+
swap(`${clipKey}.${stepKey}.transition`, numberValue(values[`${clipKey}.${stepKey}.duration`]));
|
|
4002
|
+
return display;
|
|
4003
|
+
}
|
|
4004
|
+
const cycle = stepKeys?.length ? stepKeys.reduce((sum, sk) => sum + numberValue(values[`${clipKey}.${sk}.duration`]), 0) : numberValue(values[`${clipKey}.duration`]);
|
|
4005
|
+
swap(`${clipKey}.transition`, cycle);
|
|
4006
|
+
return display;
|
|
4007
|
+
}
|
|
4008
|
+
function numberValue(value) {
|
|
4009
|
+
return typeof value === "number" ? value : 0;
|
|
4010
|
+
}
|
|
4011
|
+
function unflattenClipValues(values, clipKey) {
|
|
4012
|
+
const prefix = `${clipKey}.`;
|
|
4013
|
+
const result = {};
|
|
4014
|
+
const entries = Object.entries(values).filter(([path]) => path.startsWith(prefix)).map(([path, value]) => ({ segments: path.slice(prefix.length).split("."), value })).sort((a, b) => a.segments.length - b.segments.length);
|
|
4015
|
+
for (const { segments, value } of entries) {
|
|
4016
|
+
let node = result;
|
|
4017
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
4018
|
+
const existing = node[segments[i]];
|
|
4019
|
+
node = isPlainObject(existing) ? existing : node[segments[i]] = {};
|
|
4020
|
+
}
|
|
4021
|
+
node[segments[segments.length - 1]] = cloneTimelineValue(value);
|
|
4022
|
+
}
|
|
4023
|
+
return result;
|
|
4024
|
+
}
|
|
4025
|
+
function cloneTimelineValue(value) {
|
|
4026
|
+
if (Array.isArray(value)) return value.map(cloneTimelineValue);
|
|
4027
|
+
if (!isPlainObject(value)) return value;
|
|
4028
|
+
return Object.fromEntries(
|
|
4029
|
+
Object.entries(value).map(([key, nested]) => [key, cloneTimelineValue(nested)])
|
|
4030
|
+
);
|
|
4031
|
+
}
|
|
4032
|
+
function clampTrackDelay(delay, at, trackDuration, timelineDuration) {
|
|
4033
|
+
return clamp(round2(delay), 0, Math.max(0, round2(timelineDuration - at - trackDuration)));
|
|
4034
|
+
}
|
|
4035
|
+
function clampClipMove(at, duration, timelineDuration) {
|
|
4036
|
+
return clamp(round2(at), 0, Math.max(0, timelineDuration - duration));
|
|
4037
|
+
}
|
|
4038
|
+
function clampClipResizeEnd(duration, at, timelineDuration) {
|
|
4039
|
+
return clamp(round2(duration), TIMELINE_MIN_CLIP_DURATION, timelineDuration - at);
|
|
4040
|
+
}
|
|
4041
|
+
function clampClipResizeStart(newAt, at, duration) {
|
|
4042
|
+
const clampedAt = clamp(round2(newAt), 0, at + duration - TIMELINE_MIN_CLIP_DURATION);
|
|
4043
|
+
return { at: clampedAt, duration: round2(at + duration - clampedAt) };
|
|
4044
|
+
}
|
|
4045
|
+
function clampStepResize(duration, at, otherStepsTotal, timelineDuration) {
|
|
4046
|
+
const max = Math.max(TIMELINE_MIN_CLIP_DURATION, timelineDuration - at - otherStepsTotal);
|
|
4047
|
+
return clamp(round2(duration), TIMELINE_MIN_CLIP_DURATION, max);
|
|
4048
|
+
}
|
|
4049
|
+
function normalizeTimelineValuesForCopy(values, clips) {
|
|
4050
|
+
const normalized = { ...values };
|
|
4051
|
+
for (const path of Object.keys(normalized)) {
|
|
4052
|
+
if (path.endsWith(".__mode")) delete normalized[path];
|
|
4053
|
+
}
|
|
4054
|
+
const normalizeTransitionAt = (transitionPath, durationPath) => {
|
|
4055
|
+
const raw = normalized[transitionPath];
|
|
4056
|
+
if (!isTransitionConfig(raw)) return;
|
|
4057
|
+
if (isPhysicsSpring(raw)) {
|
|
4058
|
+
normalized[durationPath] = transitionDefaultDuration(raw);
|
|
4059
|
+
}
|
|
4060
|
+
normalized[transitionPath] = normalizeStoredTransition(
|
|
4061
|
+
raw,
|
|
4062
|
+
numberValue(normalized[durationPath])
|
|
4063
|
+
);
|
|
4064
|
+
};
|
|
4065
|
+
for (const clip of clips) {
|
|
4066
|
+
for (const stepKey of clip.stepKeys ?? []) {
|
|
4067
|
+
normalizeTransitionAt(
|
|
4068
|
+
`${clip.key}.${stepKey}.transition`,
|
|
4069
|
+
`${clip.key}.${stepKey}.duration`
|
|
4070
|
+
);
|
|
4071
|
+
}
|
|
4072
|
+
normalizeTransitionAt(`${clip.key}.transition`, `${clip.key}.duration`);
|
|
4073
|
+
for (const track of clip.tracks ?? []) {
|
|
4074
|
+
const trackKey = `${clip.key}.${track.prop}`;
|
|
4075
|
+
for (const stepKey of track.stepKeys ?? []) {
|
|
4076
|
+
normalizeTransitionAt(
|
|
4077
|
+
`${trackKey}.${stepKey}.transition`,
|
|
4078
|
+
`${trackKey}.${stepKey}.duration`
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
normalizeTransitionAt(`${trackKey}.transition`, `${trackKey}.duration`);
|
|
4082
|
+
if (normalized[`${trackKey}.delay`] === 0) {
|
|
4083
|
+
delete normalized[`${trackKey}.delay`];
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
delete normalized[`${clip.key}.loop`];
|
|
4087
|
+
}
|
|
4088
|
+
return normalized;
|
|
4089
|
+
}
|
|
4090
|
+
function formatClock(time, tenths = false) {
|
|
4091
|
+
const safe = Math.max(0, time);
|
|
4092
|
+
const minutes = Math.floor(safe / 60);
|
|
4093
|
+
const seconds = safe - minutes * 60;
|
|
4094
|
+
const secondsText = tenths ? seconds.toFixed(1).padStart(4, "0") : String(Math.floor(seconds)).padStart(2, "0");
|
|
4095
|
+
return `${String(minutes).padStart(2, "0")}:${secondsText}`;
|
|
4096
|
+
}
|
|
4097
|
+
function formatSeconds(value) {
|
|
4098
|
+
return `${round2(value)}s`;
|
|
4099
|
+
}
|
|
4100
|
+
function formatStepLabel(stepKey) {
|
|
4101
|
+
const match = /^step(\d+)$/.exec(stepKey);
|
|
4102
|
+
return match ? `Step ${match[1]}` : formatLabel(stepKey);
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
// src/timeline/adapter.ts
|
|
4106
|
+
function resolveTimelineLoop(loop) {
|
|
4107
|
+
if (typeof loop === "object" && loop !== null) {
|
|
4108
|
+
return {
|
|
4109
|
+
enabled: true,
|
|
4110
|
+
start: Number.isFinite(loop.from) ? Math.max(0, loop.from) : 0
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
return { enabled: Boolean(loop), start: 0 };
|
|
4114
|
+
}
|
|
4115
|
+
function buildTimelineMeta(id, name, duration, parsed, loop) {
|
|
4116
|
+
const resolvedLoop = resolveTimelineLoop(loop);
|
|
4117
|
+
return {
|
|
4118
|
+
id,
|
|
4119
|
+
name,
|
|
4120
|
+
duration,
|
|
4121
|
+
loop: resolvedLoop.enabled,
|
|
4122
|
+
loopStart: resolvedLoop.start,
|
|
4123
|
+
clips: parsed.clips
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
function buildTimelineValues(staticClips, transport, timelineDuration, loopStart, actions) {
|
|
4127
|
+
var _a;
|
|
4128
|
+
const result = {
|
|
4129
|
+
time: transport.time,
|
|
4130
|
+
playing: transport.playing,
|
|
4131
|
+
duration: timelineDuration,
|
|
4132
|
+
...actions
|
|
4133
|
+
};
|
|
4134
|
+
const span = loopSpan(transport.duration, loopStart);
|
|
4135
|
+
const cycleTime = (span > 0 ? transport.wraps * span : 0) + transport.time;
|
|
4136
|
+
for (const clip of staticClips) {
|
|
4137
|
+
const state = computeClipState(clip, transport.time, cycleTime);
|
|
4138
|
+
if (clip.group) {
|
|
4139
|
+
const bucket = result[_a = clip.group] ?? (result[_a] = {});
|
|
4140
|
+
bucket[clip.childKey] = state;
|
|
4141
|
+
} else {
|
|
4142
|
+
result[clip.key] = state;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
return result;
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
// src/hooks/useDialTimeline.ts
|
|
4149
|
+
function useDialTimeline(name, config, options) {
|
|
4150
|
+
const serializedConfig = useSerialized(config);
|
|
4151
|
+
const parsed = useMemo2(() => parseTimelineConfig(config), [serializedConfig]);
|
|
4152
|
+
const { panelId, flatValues } = useDialStorePanel(name, parsed.dialConfig, {
|
|
4153
|
+
id: options?.id,
|
|
4154
|
+
persist: options?.persist,
|
|
4155
|
+
kind: "timeline"
|
|
4156
|
+
});
|
|
4157
|
+
const staticTimeline = useMemo2(
|
|
4158
|
+
() => computeStaticTimeline(parsed, flatValues),
|
|
4159
|
+
[parsed, flatValues]
|
|
4160
|
+
);
|
|
4161
|
+
const timelineDuration = staticTimeline.duration;
|
|
4162
|
+
const staticClips = staticTimeline.clips;
|
|
4163
|
+
const parsedRef = useRef13(parsed);
|
|
4164
|
+
parsedRef.current = parsed;
|
|
4165
|
+
const optionsRef = useRef13(options);
|
|
4166
|
+
optionsRef.current = options;
|
|
4167
|
+
const { start: loopStart } = resolveTimelineLoop(options?.loop);
|
|
4168
|
+
const buildMeta = useCallback13(
|
|
4169
|
+
() => buildTimelineMeta(panelId, name, timelineDuration, parsedRef.current, options?.loop),
|
|
4170
|
+
[panelId, name, timelineDuration, options?.loop]
|
|
4171
|
+
);
|
|
4172
|
+
const buildMetaRef = useRef13(buildMeta);
|
|
4173
|
+
buildMetaRef.current = buildMeta;
|
|
4174
|
+
useEffect10(() => {
|
|
4175
|
+
TimelineStore.register(buildMetaRef.current(), { autoplay: optionsRef.current?.autoplay ?? true });
|
|
4176
|
+
return () => TimelineStore.unregister(panelId);
|
|
4177
|
+
}, [panelId, name]);
|
|
4178
|
+
const mountedRef = useRef13(false);
|
|
4179
|
+
useEffect10(() => {
|
|
4180
|
+
if (!mountedRef.current) {
|
|
4181
|
+
mountedRef.current = true;
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
TimelineStore.update(buildMeta());
|
|
4185
|
+
}, [buildMeta, parsed]);
|
|
4186
|
+
const subscribeTransport = useCallback13(
|
|
4187
|
+
(callback) => TimelineStore.subscribe(panelId, callback),
|
|
4188
|
+
[panelId]
|
|
4189
|
+
);
|
|
4190
|
+
const getTransport = useCallback13(() => TimelineStore.getTransport(panelId), [panelId]);
|
|
4191
|
+
const transport = useSyncExternalStore6(subscribeTransport, getTransport, getTransport);
|
|
4192
|
+
const play = useCallback13(() => TimelineStore.play(panelId), [panelId]);
|
|
4193
|
+
const pause = useCallback13(() => TimelineStore.pause(panelId), [panelId]);
|
|
4194
|
+
const replay = useCallback13(() => TimelineStore.replay(panelId), [panelId]);
|
|
4195
|
+
const seek = useCallback13((time) => TimelineStore.seek(panelId, time), [panelId]);
|
|
4196
|
+
return useMemo2(
|
|
4197
|
+
() => buildTimelineValues(staticClips, transport, timelineDuration, loopStart, {
|
|
4198
|
+
play,
|
|
4199
|
+
pause,
|
|
4200
|
+
replay,
|
|
4201
|
+
seek
|
|
4202
|
+
}),
|
|
4203
|
+
[staticClips, transport, timelineDuration, loopStart, play, pause, replay, seek]
|
|
4204
|
+
);
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
// src/components/Timeline/DialTimeline.tsx
|
|
4208
|
+
import { memo, useCallback as useCallback14, useEffect as useEffect11, useLayoutEffect as useLayoutEffect2, useRef as useRef14, useState as useState11, useSyncExternalStore as useSyncExternalStore7 } from "react";
|
|
4209
|
+
import { createPortal as createPortal4 } from "react-dom";
|
|
4210
|
+
import { AnimatePresence as AnimatePresence5, motion as motion7 } from "motion/react";
|
|
4211
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4212
|
+
var DRAG_THRESHOLD_PX = 3;
|
|
4213
|
+
var MAJOR_TICK_TARGET_PX = 140;
|
|
4214
|
+
var MILLISECOND_STEP = 1e-3;
|
|
4215
|
+
var SECOND_TICK_STEPS = [
|
|
4216
|
+
1e-3,
|
|
4217
|
+
2e-3,
|
|
4218
|
+
5e-3,
|
|
4219
|
+
0.01,
|
|
4220
|
+
0.02,
|
|
4221
|
+
0.05,
|
|
4222
|
+
0.1,
|
|
4223
|
+
0.2,
|
|
4224
|
+
0.5,
|
|
4225
|
+
1,
|
|
4226
|
+
2,
|
|
4227
|
+
5,
|
|
4228
|
+
10,
|
|
4229
|
+
15,
|
|
4230
|
+
30,
|
|
4231
|
+
60,
|
|
4232
|
+
120,
|
|
4233
|
+
300,
|
|
4234
|
+
600
|
|
4235
|
+
];
|
|
4236
|
+
var MIN_TIMELINE_MAX_ZOOM = 8;
|
|
4237
|
+
var PLAYHEAD_FLAG_WIDTH = 52;
|
|
4238
|
+
var POPOVER_WIDTH = 280;
|
|
4239
|
+
var ZOOM_DRAG_DISTANCE = 180;
|
|
4240
|
+
var subscribeGlobalTimelines = (callback) => TimelineStore.subscribeGlobal(callback);
|
|
4241
|
+
var getTimelines = () => TimelineStore.getTimelines();
|
|
4242
|
+
var subscribeTimelineVisibility = (callback) => TimelineUiStore.subscribe(callback);
|
|
4243
|
+
var getTimelineVisibility = () => TimelineUiStore.getVisible();
|
|
4244
|
+
var DialTimeline = memo(function DialTimeline2({
|
|
4245
|
+
theme = "system",
|
|
4246
|
+
defaultVisible = true,
|
|
4247
|
+
visible,
|
|
4248
|
+
onVisibilityChange,
|
|
4249
|
+
defaultOpen = true,
|
|
4250
|
+
productionEnabled = isDevDefault
|
|
4251
|
+
}) {
|
|
4252
|
+
if (!productionEnabled) return null;
|
|
4253
|
+
return /* @__PURE__ */ jsx18(
|
|
4254
|
+
DialTimelineDock,
|
|
4255
|
+
{
|
|
4256
|
+
theme,
|
|
4257
|
+
defaultVisible,
|
|
4258
|
+
visible,
|
|
4259
|
+
onVisibilityChange,
|
|
4260
|
+
defaultOpen
|
|
4261
|
+
}
|
|
4262
|
+
);
|
|
4263
|
+
});
|
|
4264
|
+
function DialTimelineDock({
|
|
4265
|
+
theme,
|
|
4266
|
+
defaultVisible,
|
|
4267
|
+
visible,
|
|
4268
|
+
onVisibilityChange,
|
|
4269
|
+
defaultOpen
|
|
4270
|
+
}) {
|
|
4271
|
+
const [mounted, setMounted] = useState11(false);
|
|
4272
|
+
const visibilityControllerId = useRef14(/* @__PURE__ */ Symbol("dialkit-timeline-visibility"));
|
|
4273
|
+
useEffect11(() => TimelineUiStore.registerController(visibilityControllerId.current, {
|
|
4274
|
+
visible,
|
|
4275
|
+
defaultVisible,
|
|
4276
|
+
onVisibilityChange
|
|
4277
|
+
}), []);
|
|
4278
|
+
useEffect11(() => {
|
|
4279
|
+
TimelineUiStore.updateController(visibilityControllerId.current, {
|
|
4280
|
+
visible,
|
|
4281
|
+
defaultVisible,
|
|
4282
|
+
onVisibilityChange
|
|
4283
|
+
});
|
|
4284
|
+
}, [defaultVisible, onVisibilityChange, visible]);
|
|
4285
|
+
useEffect11(() => {
|
|
4286
|
+
setMounted(true);
|
|
4287
|
+
}, []);
|
|
4288
|
+
const timelines = useSyncExternalStore7(subscribeGlobalTimelines, getTimelines, getTimelines);
|
|
4289
|
+
const dockVisible = useSyncExternalStore7(
|
|
4290
|
+
subscribeTimelineVisibility,
|
|
4291
|
+
getTimelineVisibility,
|
|
4292
|
+
getTimelineVisibility
|
|
4293
|
+
);
|
|
4294
|
+
if (!mounted || typeof window === "undefined" || timelines.length === 0) {
|
|
4295
|
+
return null;
|
|
4296
|
+
}
|
|
4297
|
+
return createPortal4(
|
|
4298
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-root dialkit-timeline", "data-theme": theme, hidden: !dockVisible, children: /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-dock", children: timelines.map((timeline) => /* @__PURE__ */ jsx18(
|
|
4299
|
+
TimelineSection,
|
|
4300
|
+
{
|
|
4301
|
+
meta: timeline,
|
|
4302
|
+
defaultOpen,
|
|
4303
|
+
theme,
|
|
4304
|
+
dockVisible
|
|
4305
|
+
},
|
|
4306
|
+
timeline.id
|
|
4307
|
+
)) }) }),
|
|
4308
|
+
document.body
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4311
|
+
function useTransportSubscribe(id) {
|
|
4312
|
+
return useCallback14((callback) => TimelineStore.subscribe(id, callback), [id]);
|
|
4313
|
+
}
|
|
4314
|
+
function PlayPauseButton({ id }) {
|
|
4315
|
+
const subscribe = useTransportSubscribe(id);
|
|
4316
|
+
const getPlaying = useCallback14(() => TimelineStore.getTransport(id).playing, [id]);
|
|
4317
|
+
const playing = useSyncExternalStore7(subscribe, getPlaying, getPlaying);
|
|
4318
|
+
return /* @__PURE__ */ jsx18(
|
|
4319
|
+
motion7.button,
|
|
4320
|
+
{
|
|
4321
|
+
className: "dialkit-toolbar-add",
|
|
4322
|
+
onClick: () => playing ? TimelineStore.pause(id) : TimelineStore.play(id),
|
|
4323
|
+
title: playing ? "Pause" : "Play",
|
|
4324
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
4325
|
+
whileTap: { scale: 0.9 },
|
|
4326
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
4327
|
+
children: /* @__PURE__ */ jsx18("span", { style: { position: "relative", width: 16, height: 16 }, children: /* @__PURE__ */ jsx18(AnimatePresence5, { initial: false, mode: "wait", children: playing ? /* @__PURE__ */ jsx18(
|
|
4328
|
+
motion7.svg,
|
|
4329
|
+
{
|
|
4330
|
+
viewBox: "0 0 24 24",
|
|
4331
|
+
fill: "none",
|
|
4332
|
+
"aria-hidden": "true",
|
|
4333
|
+
style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
|
|
4334
|
+
initial: { scale: 0.8, opacity: 0 },
|
|
4335
|
+
animate: { scale: 1, opacity: 1 },
|
|
4336
|
+
exit: { scale: 0.8, opacity: 0 },
|
|
4337
|
+
transition: { duration: 0.08 },
|
|
4338
|
+
children: ICON_PAUSE.map((d, i) => /* @__PURE__ */ jsx18("path", { d, fill: "currentColor" }, i))
|
|
4339
|
+
},
|
|
4340
|
+
"pause"
|
|
4341
|
+
) : /* @__PURE__ */ jsx18(
|
|
4342
|
+
motion7.svg,
|
|
4343
|
+
{
|
|
4344
|
+
viewBox: "0 0 24 24",
|
|
4345
|
+
fill: "none",
|
|
4346
|
+
"aria-hidden": "true",
|
|
4347
|
+
style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
|
|
4348
|
+
initial: { scale: 0.8, opacity: 0 },
|
|
4349
|
+
animate: { scale: 1, opacity: 1 },
|
|
4350
|
+
exit: { scale: 0.8, opacity: 0 },
|
|
4351
|
+
transition: { duration: 0.08 },
|
|
4352
|
+
children: /* @__PURE__ */ jsx18("path", { d: ICON_PLAY, fill: "currentColor" })
|
|
4353
|
+
},
|
|
4354
|
+
"play"
|
|
4355
|
+
) }) })
|
|
4356
|
+
}
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
function TimelinePlayheadFlag({
|
|
4360
|
+
id,
|
|
4361
|
+
duration,
|
|
4362
|
+
pxPerSecond,
|
|
4363
|
+
viewStart,
|
|
4364
|
+
viewEnd,
|
|
4365
|
+
laneWidth,
|
|
4366
|
+
rulerRef,
|
|
4367
|
+
headerClearStart,
|
|
4368
|
+
headerClearEnd,
|
|
4369
|
+
onResetView
|
|
4370
|
+
}) {
|
|
4371
|
+
const subscribe = useTransportSubscribe(id);
|
|
4372
|
+
const getTime = useCallback14(() => TimelineStore.getTransport(id).time, [id]);
|
|
4373
|
+
const time = useSyncExternalStore7(subscribe, getTime, getTime);
|
|
4374
|
+
const scrubRef = useRef14(null);
|
|
4375
|
+
const cleanupScrubRef = useRef14(null);
|
|
4376
|
+
const seekFromClientX = useCallback14((clientX) => {
|
|
4377
|
+
const rect = scrubRef.current?.rect;
|
|
4378
|
+
const scrub = scrubRef.current;
|
|
4379
|
+
const contentWidth = rect?.width ?? 0;
|
|
4380
|
+
if (!rect || !scrub || contentWidth <= 0) return;
|
|
4381
|
+
const nextTime = clamp(
|
|
4382
|
+
scrub.viewStart + (clientX - rect.left) / contentWidth * (scrub.viewEnd - scrub.viewStart),
|
|
4383
|
+
scrub.viewStart,
|
|
4384
|
+
scrub.viewEnd
|
|
4385
|
+
);
|
|
4386
|
+
TimelineStore.seek(id, nextTime);
|
|
4387
|
+
}, [id]);
|
|
4388
|
+
const handlePointerDown = useCallback14((e) => {
|
|
4389
|
+
const rect = rulerRef.current?.getBoundingClientRect();
|
|
4390
|
+
if (!rect) return;
|
|
4391
|
+
e.preventDefault();
|
|
4392
|
+
e.stopPropagation();
|
|
4393
|
+
cleanupScrubRef.current?.();
|
|
4394
|
+
const resetView = e.shiftKey;
|
|
4395
|
+
scrubRef.current = {
|
|
4396
|
+
wasPlaying: TimelineStore.getTransport(id).playing,
|
|
4397
|
+
rect,
|
|
4398
|
+
viewStart: resetView ? 0 : viewStart,
|
|
4399
|
+
viewEnd: resetView ? duration : viewEnd
|
|
4400
|
+
};
|
|
4401
|
+
if (resetView) onResetView();
|
|
4402
|
+
TimelineStore.pause(id);
|
|
4403
|
+
seekFromClientX(e.clientX);
|
|
4404
|
+
const handleWindowPointerMove = (event) => {
|
|
4405
|
+
event.preventDefault();
|
|
4406
|
+
seekFromClientX(event.clientX);
|
|
4407
|
+
};
|
|
4408
|
+
const finishWindowScrub = () => {
|
|
4409
|
+
window.removeEventListener("pointermove", handleWindowPointerMove);
|
|
4410
|
+
window.removeEventListener("pointerup", finishWindowScrub);
|
|
4411
|
+
window.removeEventListener("pointercancel", finishWindowScrub);
|
|
4412
|
+
if (scrubRef.current?.wasPlaying) TimelineStore.play(id);
|
|
4413
|
+
scrubRef.current = null;
|
|
4414
|
+
cleanupScrubRef.current = null;
|
|
4415
|
+
};
|
|
4416
|
+
window.addEventListener("pointermove", handleWindowPointerMove, { passive: false });
|
|
4417
|
+
window.addEventListener("pointerup", finishWindowScrub);
|
|
4418
|
+
window.addEventListener("pointercancel", finishWindowScrub);
|
|
4419
|
+
cleanupScrubRef.current = finishWindowScrub;
|
|
4420
|
+
}, [duration, id, onResetView, rulerRef, seekFromClientX, viewEnd, viewStart]);
|
|
4421
|
+
useEffect11(() => () => cleanupScrubRef.current?.(), []);
|
|
4422
|
+
if (time < viewStart || time > viewEnd || laneWidth <= 0) return null;
|
|
4423
|
+
const x = clamp(
|
|
4424
|
+
(time - viewStart) * pxPerSecond,
|
|
4425
|
+
0,
|
|
4426
|
+
laneWidth
|
|
4427
|
+
);
|
|
4428
|
+
const flagLeft = x - PLAYHEAD_FLAG_WIDTH / 2;
|
|
4429
|
+
const flagRight = flagLeft + PLAYHEAD_FLAG_WIDTH;
|
|
4430
|
+
const placement = flagLeft >= headerClearStart && flagRight <= headerClearEnd ? "raised" : "lowered";
|
|
4431
|
+
return /* @__PURE__ */ jsxs14(
|
|
4432
|
+
"div",
|
|
4433
|
+
{
|
|
4434
|
+
className: "dialkit-timeline-playhead-control",
|
|
4435
|
+
"data-edge": "center",
|
|
4436
|
+
"data-placement": placement,
|
|
4437
|
+
style: { left: `calc(var(--dial-timeline-label-w) + ${x}px)` },
|
|
4438
|
+
onPointerDown: handlePointerDown,
|
|
4439
|
+
role: "slider",
|
|
4440
|
+
"aria-label": "Timeline current time",
|
|
4441
|
+
"aria-valuemin": 0,
|
|
4442
|
+
"aria-valuemax": duration,
|
|
4443
|
+
"aria-valuenow": time,
|
|
4444
|
+
title: "Drag to scrub the timeline",
|
|
4445
|
+
children: [
|
|
4446
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-flag", children: time.toFixed(2) }),
|
|
4447
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-stem" })
|
|
4448
|
+
]
|
|
4449
|
+
}
|
|
4450
|
+
);
|
|
4451
|
+
}
|
|
4452
|
+
function TimelineOverview({
|
|
4453
|
+
id,
|
|
4454
|
+
duration,
|
|
4455
|
+
viewStart,
|
|
4456
|
+
viewEnd,
|
|
4457
|
+
onNavigate
|
|
4458
|
+
}) {
|
|
4459
|
+
const subscribe = useTransportSubscribe(id);
|
|
4460
|
+
const getTime = useCallback14(() => TimelineStore.getTransport(id).time, [id]);
|
|
4461
|
+
const time = useSyncExternalStore7(subscribe, getTime, getTime);
|
|
4462
|
+
const scrubRef = useRef14(null);
|
|
4463
|
+
const seekFromClientX = useCallback14((clientX) => {
|
|
4464
|
+
const rect = scrubRef.current?.rect;
|
|
4465
|
+
if (!rect || rect.width <= 0 || duration <= 0) return;
|
|
4466
|
+
const nextTime = clamp((clientX - rect.left) / rect.width * duration, 0, duration);
|
|
4467
|
+
TimelineStore.seek(id, nextTime);
|
|
4468
|
+
onNavigate(nextTime);
|
|
4469
|
+
}, [duration, id, onNavigate]);
|
|
4470
|
+
const handlePointerDown = useCallback14((e) => {
|
|
4471
|
+
e.preventDefault();
|
|
4472
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
4473
|
+
scrubRef.current = {
|
|
4474
|
+
wasPlaying: TimelineStore.getTransport(id).playing,
|
|
4475
|
+
rect: e.currentTarget.getBoundingClientRect()
|
|
4476
|
+
};
|
|
4477
|
+
TimelineStore.pause(id);
|
|
4478
|
+
seekFromClientX(e.clientX);
|
|
4479
|
+
}, [id, seekFromClientX]);
|
|
4480
|
+
const handlePointerMove = useCallback14((e) => {
|
|
4481
|
+
if (scrubRef.current) seekFromClientX(e.clientX);
|
|
4482
|
+
}, [seekFromClientX]);
|
|
4483
|
+
const finishScrub = useCallback14(() => {
|
|
4484
|
+
if (scrubRef.current?.wasPlaying) TimelineStore.play(id);
|
|
4485
|
+
scrubRef.current = null;
|
|
4486
|
+
}, [id]);
|
|
4487
|
+
const viewportLeft = duration > 0 ? viewStart / duration * 100 : 0;
|
|
4488
|
+
const viewportWidth = duration > 0 ? (viewEnd - viewStart) / duration * 100 : 100;
|
|
4489
|
+
const playheadLeft = duration > 0 ? time / duration * 100 : 0;
|
|
4490
|
+
return /* @__PURE__ */ jsxs14(
|
|
4491
|
+
"div",
|
|
4492
|
+
{
|
|
4493
|
+
className: "dialkit-timeline-overview",
|
|
4494
|
+
onPointerDown: handlePointerDown,
|
|
4495
|
+
onPointerMove: handlePointerMove,
|
|
4496
|
+
onPointerUp: finishScrub,
|
|
4497
|
+
onPointerCancel: finishScrub,
|
|
4498
|
+
onLostPointerCapture: finishScrub,
|
|
4499
|
+
title: "Drag to scrub the full timeline",
|
|
4500
|
+
children: [
|
|
4501
|
+
/* @__PURE__ */ jsx18(
|
|
4502
|
+
"div",
|
|
4503
|
+
{
|
|
4504
|
+
className: "dialkit-timeline-overview-viewport",
|
|
4505
|
+
"data-zoomed": viewportWidth < 99.999 || void 0,
|
|
4506
|
+
style: { left: `${viewportLeft}%`, width: `${viewportWidth}%` }
|
|
4507
|
+
}
|
|
4508
|
+
),
|
|
4509
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-overview-progress", style: { width: `${playheadLeft}%` } }),
|
|
4510
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-overview-playhead", style: { left: `${playheadLeft}%` } })
|
|
4511
|
+
]
|
|
4512
|
+
}
|
|
4513
|
+
);
|
|
4514
|
+
}
|
|
4515
|
+
function clampViewStart(start, duration, visibleDuration) {
|
|
4516
|
+
return clamp(start, 0, Math.max(0, duration - visibleDuration));
|
|
4517
|
+
}
|
|
4518
|
+
function formatRulerSeconds(time, step) {
|
|
4519
|
+
if (step >= 1 && Number.isInteger(time)) return formatClock(time);
|
|
4520
|
+
const decimals = Math.min(3, Math.max(1, Math.ceil(-Math.log10(step))));
|
|
4521
|
+
return `${time.toFixed(decimals)}s`;
|
|
4522
|
+
}
|
|
4523
|
+
var TimelineSection = memo(function TimelineSection2({
|
|
4524
|
+
meta,
|
|
4525
|
+
defaultOpen,
|
|
4526
|
+
theme,
|
|
4527
|
+
dockVisible
|
|
4528
|
+
}) {
|
|
4529
|
+
const [open, setOpen] = useState11(defaultOpen);
|
|
4530
|
+
const [copied, setCopied] = useState11(false);
|
|
4531
|
+
const [popover, setPopover] = useState11(null);
|
|
4532
|
+
const [collapsedGroups, setCollapsedGroups] = useState11(() => /* @__PURE__ */ new Set());
|
|
4533
|
+
const [expandedTracks, setExpandedTracks] = useState11(() => /* @__PURE__ */ new Set());
|
|
4534
|
+
const [zoom, setZoom] = useState11(1);
|
|
4535
|
+
const [viewStart, setViewStart] = useState11(0);
|
|
4536
|
+
const subscribeValues = useCallback14(
|
|
4537
|
+
(callback) => DialStore.subscribe(meta.id, callback),
|
|
4538
|
+
[meta.id]
|
|
4539
|
+
);
|
|
4540
|
+
const getValues = useCallback14(() => DialStore.getValues(meta.id), [meta.id]);
|
|
4541
|
+
const values = useSyncExternalStore7(subscribeValues, getValues, getValues);
|
|
4542
|
+
const presets = DialStore.getPresets(meta.id);
|
|
4543
|
+
const activePresetId = DialStore.getActivePresetId(meta.id);
|
|
4544
|
+
const laneAreaRef = useRef14(null);
|
|
4545
|
+
const titleRef = useRef14(null);
|
|
4546
|
+
const actionsRef = useRef14(null);
|
|
4547
|
+
const [laneWidth, setLaneWidth] = useState11(0);
|
|
4548
|
+
const [flagClearRange, setFlagClearRange] = useState11({ start: 0, end: 0 });
|
|
4549
|
+
useLayoutEffect2(() => {
|
|
4550
|
+
if (!open) return;
|
|
4551
|
+
const ruler = laneAreaRef.current;
|
|
4552
|
+
const title = titleRef.current;
|
|
4553
|
+
const actions = actionsRef.current;
|
|
4554
|
+
if (!ruler || !title || !actions) return;
|
|
4555
|
+
const measure = () => {
|
|
4556
|
+
const rulerRect = ruler.getBoundingClientRect();
|
|
4557
|
+
const titleRect = title.getBoundingClientRect();
|
|
4558
|
+
const actionsRect = actions.getBoundingClientRect();
|
|
4559
|
+
setLaneWidth(rulerRect.width);
|
|
4560
|
+
const start = Math.round(titleRect.right + 10 - rulerRect.left);
|
|
4561
|
+
const end = Math.round(actionsRect.left - 10 - rulerRect.left);
|
|
4562
|
+
setFlagClearRange((current) => current.start === start && current.end === end ? current : { start, end });
|
|
4563
|
+
};
|
|
4564
|
+
measure();
|
|
4565
|
+
const observer = new ResizeObserver(measure);
|
|
4566
|
+
observer.observe(ruler);
|
|
4567
|
+
observer.observe(title);
|
|
4568
|
+
observer.observe(actions);
|
|
4569
|
+
return () => observer.disconnect();
|
|
4570
|
+
}, [open]);
|
|
4571
|
+
const visibleDuration = meta.duration > 0 ? meta.duration / zoom : meta.duration;
|
|
4572
|
+
const safeViewStart = clampViewStart(viewStart, meta.duration, visibleDuration);
|
|
4573
|
+
const viewEnd = safeViewStart + visibleDuration;
|
|
4574
|
+
const pxPerSecond = visibleDuration > 0 && laneWidth > 0 ? laneWidth / visibleDuration : 0;
|
|
4575
|
+
const millisecondReadableZoom = laneWidth > 0 && meta.duration > 0 ? MAJOR_TICK_TARGET_PX * meta.duration / (MILLISECOND_STEP * 10 * laneWidth) : MIN_TIMELINE_MAX_ZOOM;
|
|
4576
|
+
const maxZoom = Math.max(MIN_TIMELINE_MAX_ZOOM, millisecondReadableZoom);
|
|
4577
|
+
useEffect11(() => {
|
|
4578
|
+
setZoom((current) => clamp(current, 1, maxZoom));
|
|
4579
|
+
}, [maxZoom]);
|
|
4580
|
+
useEffect11(() => {
|
|
4581
|
+
setViewStart((current) => clampViewStart(current, meta.duration, meta.duration / zoom));
|
|
4582
|
+
}, [meta.duration, zoom]);
|
|
4583
|
+
useEffect11(() => {
|
|
4584
|
+
if (!dockVisible) setPopover(null);
|
|
4585
|
+
}, [dockVisible]);
|
|
4586
|
+
const centerViewAt = useCallback14((time) => {
|
|
4587
|
+
if (zoom <= 1 || meta.duration <= 0) return;
|
|
4588
|
+
const windowDuration = meta.duration / zoom;
|
|
4589
|
+
setViewStart(clampViewStart(time - windowDuration / 2, meta.duration, windowDuration));
|
|
4590
|
+
}, [meta.duration, zoom]);
|
|
4591
|
+
const resetView = useCallback14(() => {
|
|
4592
|
+
setZoom(1);
|
|
4593
|
+
setViewStart(0);
|
|
4594
|
+
}, []);
|
|
4595
|
+
const zoomDragRef = useRef14(null);
|
|
4596
|
+
const rulerScrubRef = useRef14(null);
|
|
4597
|
+
const seekRulerFromClientX = useCallback14((clientX) => {
|
|
4598
|
+
const scrub = rulerScrubRef.current;
|
|
4599
|
+
const contentWidth = scrub?.rect.width ?? 0;
|
|
4600
|
+
if (!scrub || contentWidth <= 0) return;
|
|
4601
|
+
TimelineStore.seek(
|
|
4602
|
+
meta.id,
|
|
4603
|
+
clamp(
|
|
4604
|
+
scrub.viewStart + (clientX - scrub.rect.left) / contentWidth * scrub.visibleDuration,
|
|
4605
|
+
scrub.viewStart,
|
|
4606
|
+
scrub.viewStart + scrub.visibleDuration
|
|
4607
|
+
)
|
|
4608
|
+
);
|
|
4609
|
+
}, [meta.id]);
|
|
4610
|
+
const handleRulerPointerDown = useCallback14((e) => {
|
|
4611
|
+
e.preventDefault();
|
|
4612
|
+
e.stopPropagation();
|
|
4613
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
4614
|
+
const contentWidth = rect.width;
|
|
4615
|
+
if (contentWidth <= 0) return;
|
|
4616
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
4617
|
+
if (!e.altKey) {
|
|
4618
|
+
const resetView2 = e.shiftKey;
|
|
4619
|
+
rulerScrubRef.current = {
|
|
4620
|
+
wasPlaying: TimelineStore.getTransport(meta.id).playing,
|
|
4621
|
+
rect,
|
|
4622
|
+
viewStart: resetView2 ? 0 : safeViewStart,
|
|
4623
|
+
visibleDuration: resetView2 ? meta.duration : visibleDuration
|
|
4624
|
+
};
|
|
4625
|
+
if (resetView2) {
|
|
4626
|
+
setZoom(1);
|
|
4627
|
+
setViewStart(0);
|
|
4628
|
+
}
|
|
4629
|
+
TimelineStore.pause(meta.id);
|
|
4630
|
+
seekRulerFromClientX(e.clientX);
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
const anchorRatio = clamp((e.clientX - rect.left) / contentWidth, 0, 1);
|
|
4634
|
+
zoomDragRef.current = {
|
|
4635
|
+
pointerX: e.clientX,
|
|
4636
|
+
rect,
|
|
4637
|
+
zoom,
|
|
4638
|
+
viewStart: safeViewStart,
|
|
4639
|
+
anchorRatio,
|
|
4640
|
+
anchorTime: safeViewStart + anchorRatio * visibleDuration,
|
|
4641
|
+
moved: false
|
|
4642
|
+
};
|
|
4643
|
+
}, [meta.duration, meta.id, safeViewStart, seekRulerFromClientX, visibleDuration, zoom]);
|
|
4644
|
+
const handleRulerPointerMove = useCallback14((e) => {
|
|
4645
|
+
if (rulerScrubRef.current) {
|
|
4646
|
+
seekRulerFromClientX(e.clientX);
|
|
4647
|
+
return;
|
|
4648
|
+
}
|
|
4649
|
+
const drag = zoomDragRef.current;
|
|
4650
|
+
if (!drag || meta.duration <= 0) return;
|
|
4651
|
+
const dx = e.clientX - drag.pointerX;
|
|
4652
|
+
if (!drag.moved && Math.abs(dx) <= DRAG_THRESHOLD_PX) return;
|
|
4653
|
+
drag.moved = true;
|
|
4654
|
+
const nextZoom = clamp(drag.zoom * Math.exp(dx / ZOOM_DRAG_DISTANCE), 1, maxZoom);
|
|
4655
|
+
const nextVisibleDuration = meta.duration / nextZoom;
|
|
4656
|
+
const nextStart = clampViewStart(
|
|
4657
|
+
drag.anchorTime - drag.anchorRatio * nextVisibleDuration,
|
|
4658
|
+
meta.duration,
|
|
4659
|
+
nextVisibleDuration
|
|
4660
|
+
);
|
|
4661
|
+
setZoom(nextZoom);
|
|
4662
|
+
setViewStart(nextStart);
|
|
4663
|
+
}, [maxZoom, meta.duration, seekRulerFromClientX]);
|
|
4664
|
+
const handleRulerPointerUp = useCallback14(() => {
|
|
4665
|
+
if (rulerScrubRef.current?.wasPlaying) TimelineStore.play(meta.id);
|
|
4666
|
+
rulerScrubRef.current = null;
|
|
4667
|
+
zoomDragRef.current = null;
|
|
4668
|
+
}, [meta.id]);
|
|
4669
|
+
const handleRulerPointerCancel = useCallback14(() => {
|
|
4670
|
+
if (rulerScrubRef.current?.wasPlaying) TimelineStore.play(meta.id);
|
|
4671
|
+
rulerScrubRef.current = null;
|
|
4672
|
+
zoomDragRef.current = null;
|
|
4673
|
+
}, [meta.id]);
|
|
4674
|
+
const trackScrubRef = useRef14(null);
|
|
4675
|
+
const seekTrackFromClientX = useCallback14((clientX) => {
|
|
4676
|
+
const scrub = trackScrubRef.current;
|
|
4677
|
+
const contentWidth = scrub?.rect.width ?? 0;
|
|
4678
|
+
if (!scrub || contentWidth <= 0) return;
|
|
4679
|
+
const nextTime = clamp(
|
|
4680
|
+
scrub.viewStart + (clientX - scrub.rect.left) / contentWidth * scrub.visibleDuration,
|
|
4681
|
+
scrub.viewStart,
|
|
4682
|
+
scrub.viewStart + scrub.visibleDuration
|
|
4683
|
+
);
|
|
4684
|
+
TimelineStore.seek(meta.id, nextTime);
|
|
4685
|
+
}, [meta.id]);
|
|
4686
|
+
const handleTrackPointerDown = useCallback14((e) => {
|
|
4687
|
+
const target = e.target;
|
|
4688
|
+
if (target.closest(".dialkit-timeline-label, button")) return;
|
|
4689
|
+
if (!e.shiftKey && target.closest(".dialkit-timeline-clip")) return;
|
|
4690
|
+
const rect = laneAreaRef.current?.getBoundingClientRect();
|
|
4691
|
+
if (!rect) return;
|
|
4692
|
+
e.preventDefault();
|
|
4693
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
4694
|
+
const resetView2 = e.shiftKey;
|
|
4695
|
+
trackScrubRef.current = {
|
|
4696
|
+
wasPlaying: TimelineStore.getTransport(meta.id).playing,
|
|
4697
|
+
rect,
|
|
4698
|
+
viewStart: resetView2 ? 0 : safeViewStart,
|
|
4699
|
+
visibleDuration: resetView2 ? meta.duration : visibleDuration
|
|
4700
|
+
};
|
|
4701
|
+
if (resetView2) {
|
|
4702
|
+
setZoom(1);
|
|
4703
|
+
setViewStart(0);
|
|
4704
|
+
}
|
|
4705
|
+
setPopover(null);
|
|
4706
|
+
TimelineStore.pause(meta.id);
|
|
4707
|
+
seekTrackFromClientX(e.clientX);
|
|
4708
|
+
}, [meta.duration, meta.id, safeViewStart, seekTrackFromClientX, visibleDuration]);
|
|
4709
|
+
const handleTrackPointerMove = useCallback14((e) => {
|
|
4710
|
+
if (trackScrubRef.current) seekTrackFromClientX(e.clientX);
|
|
4711
|
+
}, [seekTrackFromClientX]);
|
|
4712
|
+
const finishTrackScrub = useCallback14(() => {
|
|
4713
|
+
if (trackScrubRef.current?.wasPlaying) TimelineStore.play(meta.id);
|
|
4714
|
+
trackScrubRef.current = null;
|
|
4715
|
+
}, [meta.id]);
|
|
4716
|
+
const handleCopy = useCallback14(() => {
|
|
4717
|
+
const normalized = normalizeTimelineValuesForCopy(DialStore.getValues(meta.id), meta.clips);
|
|
4718
|
+
navigator.clipboard.writeText(buildCopyInstruction("useDialTimeline", meta.name, normalized));
|
|
4719
|
+
setCopied(true);
|
|
4720
|
+
setTimeout(() => setCopied(false), 1500);
|
|
4721
|
+
}, [meta.clips, meta.id, meta.name]);
|
|
4722
|
+
const handleAddPreset = useCallback14(() => {
|
|
4723
|
+
DialStore.savePreset(meta.id, `Version ${presets.length + 2}`);
|
|
4724
|
+
}, [meta.id, presets.length]);
|
|
4725
|
+
const closePopover = useCallback14(() => setPopover(null), []);
|
|
4726
|
+
const openClipPopover = useCallback14(
|
|
4727
|
+
(clip, rect, stepKey) => {
|
|
4728
|
+
const targetPath = stepKey ? `${clip.key}.${stepKey}` : clip.key;
|
|
4729
|
+
const exclude = stepKey ? void 0 : clipPopoverExclusions(clip);
|
|
4730
|
+
if (getClipControls(meta.id, targetPath, exclude).length === 0) return;
|
|
4731
|
+
setPopover(
|
|
4732
|
+
(prev) => prev?.clip.key === clip.key && prev?.stepKey === stepKey ? null : {
|
|
4733
|
+
clip,
|
|
4734
|
+
stepKey,
|
|
4735
|
+
anchor: {
|
|
4736
|
+
left: rect.left,
|
|
4737
|
+
top: rect.top,
|
|
4738
|
+
right: rect.right,
|
|
4739
|
+
bottom: rect.bottom,
|
|
4740
|
+
width: rect.width,
|
|
4741
|
+
height: rect.height
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
);
|
|
4745
|
+
},
|
|
4746
|
+
[meta.id]
|
|
4747
|
+
);
|
|
4748
|
+
const toggleTracks = useCallback14((clipKey) => {
|
|
4749
|
+
setExpandedTracks((prev) => {
|
|
4750
|
+
const next = new Set(prev);
|
|
4751
|
+
if (next.has(clipKey)) next.delete(clipKey);
|
|
4752
|
+
else next.add(clipKey);
|
|
4753
|
+
return next;
|
|
4754
|
+
});
|
|
4755
|
+
}, []);
|
|
4756
|
+
const handleBarClick = useCallback14(
|
|
4757
|
+
(clip, rect, stepKey) => {
|
|
4758
|
+
if (!stepKey && clip.tracks?.length) {
|
|
4759
|
+
toggleTracks(clip.key);
|
|
4760
|
+
return;
|
|
4761
|
+
}
|
|
4762
|
+
openClipPopover(clip, rect, stepKey);
|
|
4763
|
+
},
|
|
4764
|
+
[openClipPopover, toggleTracks]
|
|
4765
|
+
);
|
|
4766
|
+
const toggleGroup = useCallback14((group) => {
|
|
4767
|
+
setCollapsedGroups((prev) => {
|
|
4768
|
+
const next = new Set(prev);
|
|
4769
|
+
if (next.has(group)) next.delete(group);
|
|
4770
|
+
else next.add(group);
|
|
4771
|
+
return next;
|
|
4772
|
+
});
|
|
4773
|
+
}, []);
|
|
4774
|
+
const rawStep = pxPerSecond > 0 ? MAJOR_TICK_TARGET_PX / pxPerSecond : 1;
|
|
4775
|
+
const adaptiveMajorStep = SECOND_TICK_STEPS.find((step) => step >= rawStep) ?? SECOND_TICK_STEPS[SECOND_TICK_STEPS.length - 1];
|
|
4776
|
+
const majorStep = zoom < 1.5 && meta.duration >= 1 ? Math.max(1, adaptiveMajorStep) : adaptiveMajorStep;
|
|
4777
|
+
const fineTickStep = majorStep / 10;
|
|
4778
|
+
const majorTicks = [];
|
|
4779
|
+
const mediumTicks = [];
|
|
4780
|
+
const fineTicks = [];
|
|
4781
|
+
const firstMajorTick = Math.ceil((safeViewStart - 1e-6) / majorStep) * majorStep;
|
|
4782
|
+
for (let t = firstMajorTick; t <= viewEnd + 1e-6; t += majorStep) {
|
|
4783
|
+
majorTicks.push(Number(t.toFixed(4)));
|
|
4784
|
+
}
|
|
4785
|
+
const firstFineIndex = Math.ceil((safeViewStart - 1e-6) / fineTickStep);
|
|
4786
|
+
const lastFineIndex = Math.floor((viewEnd + 1e-6) / fineTickStep);
|
|
4787
|
+
for (let index = firstFineIndex; index <= lastFineIndex; index++) {
|
|
4788
|
+
if (index % 10 === 0) continue;
|
|
4789
|
+
const tick = Number((index * fineTickStep).toFixed(6));
|
|
4790
|
+
if (index % 5 === 0) mediumTicks.push(tick);
|
|
4791
|
+
else fineTicks.push(tick);
|
|
4792
|
+
}
|
|
4793
|
+
const rows = [];
|
|
4794
|
+
let lastGroup;
|
|
4795
|
+
for (const clip of meta.clips) {
|
|
4796
|
+
if (clip.group !== lastGroup) {
|
|
4797
|
+
lastGroup = clip.group;
|
|
4798
|
+
if (clip.group) {
|
|
4799
|
+
const group = clip.group;
|
|
4800
|
+
const isCollapsed = collapsedGroups.has(group);
|
|
4801
|
+
rows.push(
|
|
4802
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-row dialkit-timeline-group-row", children: [
|
|
4803
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-label", children: [
|
|
4804
|
+
/* @__PURE__ */ jsx18(
|
|
4805
|
+
"button",
|
|
4806
|
+
{
|
|
4807
|
+
className: "dialkit-timeline-group-toggle",
|
|
4808
|
+
"data-open": !isCollapsed,
|
|
4809
|
+
onClick: () => toggleGroup(group),
|
|
4810
|
+
title: isCollapsed ? "Expand layer" : "Collapse layer",
|
|
4811
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx18("path", { d: ICON_CHEVRON }) })
|
|
4812
|
+
}
|
|
4813
|
+
),
|
|
4814
|
+
/* @__PURE__ */ jsx18("span", { children: formatLabel(group) })
|
|
4815
|
+
] }),
|
|
4816
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-lane" })
|
|
4817
|
+
] }, `group:${group}`)
|
|
4818
|
+
);
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
if (clip.group && collapsedGroups.has(clip.group)) continue;
|
|
4822
|
+
const isProps = Boolean(clip.tracks?.length);
|
|
4823
|
+
const tracksOpen = isProps && expandedTracks.has(clip.key);
|
|
4824
|
+
const stat = computeClipStaticFromValues(values, clip, meta.duration);
|
|
4825
|
+
rows.push(
|
|
4826
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-row", "data-grouped": clip.group ? "" : void 0, children: [
|
|
4827
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-label", children: [
|
|
4828
|
+
isProps ? /* @__PURE__ */ jsx18(
|
|
4829
|
+
"button",
|
|
4830
|
+
{
|
|
4831
|
+
className: "dialkit-timeline-group-toggle",
|
|
4832
|
+
"data-open": tracksOpen,
|
|
4833
|
+
onClick: (e) => {
|
|
4834
|
+
e.stopPropagation();
|
|
4835
|
+
toggleTracks(clip.key);
|
|
4836
|
+
},
|
|
4837
|
+
title: tracksOpen ? "Collapse properties" : "Expand properties",
|
|
4838
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx18("path", { d: ICON_CHEVRON }) })
|
|
4839
|
+
}
|
|
4840
|
+
) : null,
|
|
4841
|
+
clip.label
|
|
4842
|
+
] }),
|
|
4843
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-lane", children: /* @__PURE__ */ jsx18(
|
|
4844
|
+
TimelineClip,
|
|
4845
|
+
{
|
|
4846
|
+
timelineId: meta.id,
|
|
4847
|
+
clip,
|
|
4848
|
+
at: stat.at,
|
|
4849
|
+
duration: stat.duration,
|
|
4850
|
+
loop: stat.loop,
|
|
4851
|
+
steps: clip.stepKeys?.length ? stat.tracks[0]?.steps : void 0,
|
|
4852
|
+
fixedDuration: isProps ? true : stat.isPhysics,
|
|
4853
|
+
composite: isProps,
|
|
4854
|
+
pxPerSecond,
|
|
4855
|
+
viewStart: safeViewStart,
|
|
4856
|
+
timelineDuration: meta.duration,
|
|
4857
|
+
selected: popover?.clip.key === clip.key,
|
|
4858
|
+
selectedStepKey: popover?.clip.key === clip.key ? popover.stepKey : void 0,
|
|
4859
|
+
onClick: handleBarClick,
|
|
4860
|
+
onDrag: closePopover
|
|
4861
|
+
}
|
|
4862
|
+
) })
|
|
4863
|
+
] }, clip.key)
|
|
4864
|
+
);
|
|
4865
|
+
if (tracksOpen) {
|
|
4866
|
+
for (const trackRef of clip.tracks ?? []) {
|
|
4867
|
+
const track = stat.tracks.find((candidate) => candidate.prop === trackRef.prop);
|
|
4868
|
+
if (!track) continue;
|
|
4869
|
+
const trackKey = `${clip.key}.${trackRef.prop}`;
|
|
4870
|
+
const trackMeta = {
|
|
4871
|
+
key: trackKey,
|
|
4872
|
+
label: `${clip.label} \xB7 ${formatLabel(trackRef.prop)}`,
|
|
4873
|
+
color: clip.color,
|
|
4874
|
+
loop: clip.loop,
|
|
4875
|
+
group: clip.group,
|
|
4876
|
+
stepKeys: trackRef.stepKeys
|
|
4877
|
+
};
|
|
4878
|
+
rows.push(
|
|
4879
|
+
/* @__PURE__ */ jsxs14(
|
|
4880
|
+
"div",
|
|
4881
|
+
{
|
|
4882
|
+
className: "dialkit-timeline-row dialkit-timeline-track-row",
|
|
4883
|
+
"data-grouped": clip.group ? "" : void 0,
|
|
4884
|
+
children: [
|
|
4885
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-label", children: formatLabel(trackRef.prop) }),
|
|
4886
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-lane", children: /* @__PURE__ */ jsx18(
|
|
4887
|
+
TimelineClip,
|
|
4888
|
+
{
|
|
4889
|
+
timelineId: meta.id,
|
|
4890
|
+
clip: trackMeta,
|
|
4891
|
+
at: stat.at + track.delay,
|
|
4892
|
+
duration: track.duration,
|
|
4893
|
+
loop: stat.loop,
|
|
4894
|
+
steps: trackRef.stepKeys?.length ? track.steps : void 0,
|
|
4895
|
+
fixedDuration: !trackRef.stepKeys?.length && track.steps[0]?.isPhysics === true,
|
|
4896
|
+
baseAt: stat.at,
|
|
4897
|
+
delayMode: true,
|
|
4898
|
+
pxPerSecond,
|
|
4899
|
+
viewStart: safeViewStart,
|
|
4900
|
+
timelineDuration: meta.duration,
|
|
4901
|
+
selected: popover?.clip.key === trackKey,
|
|
4902
|
+
selectedStepKey: popover?.clip.key === trackKey ? popover.stepKey : void 0,
|
|
4903
|
+
onClick: openClipPopover,
|
|
4904
|
+
onDrag: closePopover
|
|
4905
|
+
}
|
|
4906
|
+
) })
|
|
4907
|
+
]
|
|
4908
|
+
},
|
|
4909
|
+
trackKey
|
|
4910
|
+
)
|
|
4911
|
+
);
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
return /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-section", children: [
|
|
4916
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-header", "data-open": open || void 0, children: [
|
|
4917
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-identity", children: /* @__PURE__ */ jsx18("span", { ref: titleRef, className: "dialkit-timeline-title", children: meta.name }) }),
|
|
4918
|
+
!open && /* @__PURE__ */ jsx18(
|
|
4919
|
+
TimelineOverview,
|
|
4920
|
+
{
|
|
4921
|
+
id: meta.id,
|
|
4922
|
+
duration: meta.duration,
|
|
4923
|
+
viewStart: safeViewStart,
|
|
4924
|
+
viewEnd,
|
|
4925
|
+
onNavigate: centerViewAt
|
|
4926
|
+
}
|
|
4927
|
+
),
|
|
4928
|
+
/* @__PURE__ */ jsxs14("div", { ref: actionsRef, className: "dialkit-timeline-actions", children: [
|
|
4929
|
+
/* @__PURE__ */ jsx18(PlayPauseButton, { id: meta.id }),
|
|
4930
|
+
/* @__PURE__ */ jsx18(
|
|
4931
|
+
motion7.button,
|
|
4932
|
+
{
|
|
4933
|
+
className: "dialkit-toolbar-add",
|
|
4934
|
+
onClick: handleAddPreset,
|
|
4935
|
+
title: "Add timeline version",
|
|
4936
|
+
"aria-label": "Add timeline version",
|
|
4937
|
+
whileTap: { scale: 0.9 },
|
|
4938
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
4939
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: ICON_ADD_PRESET.map((d, i) => /* @__PURE__ */ jsx18("path", { d }, i)) })
|
|
4940
|
+
}
|
|
4941
|
+
),
|
|
4942
|
+
/* @__PURE__ */ jsx18(
|
|
4943
|
+
PresetManager,
|
|
4944
|
+
{
|
|
4945
|
+
panelId: meta.id,
|
|
4946
|
+
presets,
|
|
4947
|
+
activePresetId,
|
|
4948
|
+
onAdd: handleAddPreset
|
|
4949
|
+
}
|
|
4950
|
+
),
|
|
4951
|
+
/* @__PURE__ */ jsx18(
|
|
4952
|
+
motion7.button,
|
|
4953
|
+
{
|
|
4954
|
+
className: "dialkit-toolbar-add",
|
|
4955
|
+
onClick: handleCopy,
|
|
4956
|
+
title: "Copy parameters",
|
|
4957
|
+
"aria-label": copied ? "Copied parameters" : "Copy parameters",
|
|
4958
|
+
whileTap: { scale: 0.9 },
|
|
4959
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
4960
|
+
children: /* @__PURE__ */ jsx18("span", { style: { position: "relative", width: 16, height: 16 }, children: /* @__PURE__ */ jsx18(AnimatePresence5, { initial: false, mode: "wait", children: copied ? /* @__PURE__ */ jsx18(
|
|
4961
|
+
motion7.svg,
|
|
4962
|
+
{
|
|
4963
|
+
viewBox: "0 0 24 24",
|
|
4964
|
+
fill: "none",
|
|
4965
|
+
stroke: "currentColor",
|
|
4966
|
+
strokeWidth: "2",
|
|
4967
|
+
strokeLinecap: "round",
|
|
4968
|
+
strokeLinejoin: "round",
|
|
4969
|
+
"aria-hidden": "true",
|
|
4970
|
+
style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
|
|
4971
|
+
initial: { scale: 0.8, opacity: 0 },
|
|
4972
|
+
animate: { scale: 1, opacity: 1 },
|
|
4973
|
+
exit: { scale: 0.8, opacity: 0 },
|
|
4974
|
+
transition: { duration: 0.08 },
|
|
4975
|
+
children: /* @__PURE__ */ jsx18("path", { d: ICON_CHECK })
|
|
4976
|
+
},
|
|
4977
|
+
"check"
|
|
4978
|
+
) : /* @__PURE__ */ jsxs14(
|
|
4979
|
+
motion7.svg,
|
|
4980
|
+
{
|
|
4981
|
+
viewBox: "0 0 24 24",
|
|
4982
|
+
fill: "none",
|
|
4983
|
+
"aria-hidden": "true",
|
|
4984
|
+
style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
|
|
4985
|
+
initial: { scale: 0.8, opacity: 0 },
|
|
4986
|
+
animate: { scale: 1, opacity: 1 },
|
|
4987
|
+
exit: { scale: 0.8, opacity: 0 },
|
|
4988
|
+
transition: { duration: 0.08 },
|
|
4989
|
+
children: [
|
|
4990
|
+
/* @__PURE__ */ jsx18("path", { d: ICON_CLIPBOARD.board, stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
|
|
4991
|
+
/* @__PURE__ */ jsx18("path", { d: ICON_CLIPBOARD.sparkle, fill: "currentColor" }),
|
|
4992
|
+
/* @__PURE__ */ jsx18("path", { d: ICON_CLIPBOARD.body, stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
4993
|
+
]
|
|
4994
|
+
},
|
|
4995
|
+
"clipboard"
|
|
4996
|
+
) }) })
|
|
4997
|
+
}
|
|
4998
|
+
),
|
|
4999
|
+
/* @__PURE__ */ jsx18(
|
|
5000
|
+
"button",
|
|
5001
|
+
{
|
|
5002
|
+
className: "dialkit-timeline-chevron",
|
|
5003
|
+
"data-open": open,
|
|
5004
|
+
"aria-expanded": open,
|
|
5005
|
+
onClick: () => setOpen(!open),
|
|
5006
|
+
title: open ? "Collapse timeline" : "Expand timeline",
|
|
5007
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx18("path", { d: ICON_CHEVRON }) })
|
|
5008
|
+
}
|
|
5009
|
+
)
|
|
5010
|
+
] })
|
|
5011
|
+
] }),
|
|
5012
|
+
open && /* @__PURE__ */ jsx18(
|
|
5013
|
+
"div",
|
|
5014
|
+
{
|
|
5015
|
+
className: "dialkit-timeline-body",
|
|
5016
|
+
onPointerDown: handleTrackPointerDown,
|
|
5017
|
+
onPointerMove: handleTrackPointerMove,
|
|
5018
|
+
onPointerUp: finishTrackScrub,
|
|
5019
|
+
onPointerCancel: finishTrackScrub,
|
|
5020
|
+
onLostPointerCapture: finishTrackScrub,
|
|
5021
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-grid", children: [
|
|
5022
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-row dialkit-timeline-ruler-row", children: [
|
|
5023
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-label" }),
|
|
5024
|
+
/* @__PURE__ */ jsxs14(
|
|
5025
|
+
"div",
|
|
5026
|
+
{
|
|
5027
|
+
ref: laneAreaRef,
|
|
5028
|
+
className: "dialkit-timeline-ruler",
|
|
5029
|
+
onPointerDown: handleRulerPointerDown,
|
|
5030
|
+
onPointerMove: handleRulerPointerMove,
|
|
5031
|
+
onPointerUp: handleRulerPointerUp,
|
|
5032
|
+
onPointerCancel: handleRulerPointerCancel,
|
|
5033
|
+
onLostPointerCapture: handleRulerPointerCancel,
|
|
5034
|
+
title: "Drag to seek \xB7 Option-drag to zoom \xB7 Shift-drag to reset zoom",
|
|
5035
|
+
children: [
|
|
5036
|
+
fineTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-fine", style: { left: (t - safeViewStart) * pxPerSecond } }, `fine:${t}`)),
|
|
5037
|
+
mediumTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-medium", style: { left: (t - safeViewStart) * pxPerSecond } }, `medium:${t}`)),
|
|
5038
|
+
majorTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick", style: { left: (t - safeViewStart) * pxPerSecond }, children: /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-tick-label", children: formatRulerSeconds(t, majorStep) }) }, t))
|
|
5039
|
+
]
|
|
5040
|
+
}
|
|
5041
|
+
)
|
|
5042
|
+
] }),
|
|
5043
|
+
rows,
|
|
5044
|
+
pxPerSecond > 0 && /* @__PURE__ */ jsx18(
|
|
5045
|
+
TimelinePlayheadFlag,
|
|
5046
|
+
{
|
|
5047
|
+
id: meta.id,
|
|
5048
|
+
duration: meta.duration,
|
|
5049
|
+
pxPerSecond,
|
|
5050
|
+
viewStart: safeViewStart,
|
|
5051
|
+
viewEnd,
|
|
5052
|
+
laneWidth,
|
|
5053
|
+
rulerRef: laneAreaRef,
|
|
5054
|
+
headerClearStart: flagClearRange.start,
|
|
5055
|
+
headerClearEnd: flagClearRange.end,
|
|
5056
|
+
onResetView: resetView
|
|
5057
|
+
}
|
|
5058
|
+
)
|
|
5059
|
+
] })
|
|
5060
|
+
}
|
|
5061
|
+
),
|
|
5062
|
+
popover && /* @__PURE__ */ jsx18(
|
|
5063
|
+
ClipPopover,
|
|
5064
|
+
{
|
|
5065
|
+
panelId: meta.id,
|
|
5066
|
+
popover,
|
|
5067
|
+
values,
|
|
5068
|
+
theme,
|
|
5069
|
+
onClose: closePopover
|
|
5070
|
+
}
|
|
5071
|
+
)
|
|
5072
|
+
] });
|
|
5073
|
+
});
|
|
5074
|
+
function ClipPopover({
|
|
5075
|
+
panelId,
|
|
5076
|
+
popover,
|
|
5077
|
+
values,
|
|
5078
|
+
theme,
|
|
5079
|
+
onClose
|
|
5080
|
+
}) {
|
|
5081
|
+
const ref = useRef14(null);
|
|
5082
|
+
const [naturalHeight, setNaturalHeight] = useState11(0);
|
|
5083
|
+
const [viewport, setViewport] = useState11(() => ({
|
|
5084
|
+
width: window.visualViewport?.width ?? window.innerWidth,
|
|
5085
|
+
height: window.visualViewport?.height ?? window.innerHeight,
|
|
5086
|
+
offsetLeft: window.visualViewport?.offsetLeft ?? 0,
|
|
5087
|
+
offsetTop: window.visualViewport?.offsetTop ?? 0
|
|
5088
|
+
}));
|
|
5089
|
+
useLayoutEffect2(() => {
|
|
5090
|
+
const element = ref.current;
|
|
5091
|
+
if (!element) return;
|
|
5092
|
+
const measure = () => setNaturalHeight(element.scrollHeight + 2);
|
|
5093
|
+
measure();
|
|
5094
|
+
const observer = new ResizeObserver(measure);
|
|
5095
|
+
const body = element.querySelector(".dialkit-timeline-popover-body");
|
|
5096
|
+
observer.observe(body ?? element);
|
|
5097
|
+
return () => observer.disconnect();
|
|
5098
|
+
}, [popover.clip.key, popover.stepKey]);
|
|
5099
|
+
useEffect11(() => {
|
|
5100
|
+
const updateViewport = () => setViewport({
|
|
5101
|
+
width: window.visualViewport?.width ?? window.innerWidth,
|
|
5102
|
+
height: window.visualViewport?.height ?? window.innerHeight,
|
|
5103
|
+
offsetLeft: window.visualViewport?.offsetLeft ?? 0,
|
|
5104
|
+
offsetTop: window.visualViewport?.offsetTop ?? 0
|
|
5105
|
+
});
|
|
5106
|
+
window.addEventListener("resize", updateViewport);
|
|
5107
|
+
window.visualViewport?.addEventListener("resize", updateViewport);
|
|
5108
|
+
window.visualViewport?.addEventListener("scroll", updateViewport);
|
|
5109
|
+
return () => {
|
|
5110
|
+
window.removeEventListener("resize", updateViewport);
|
|
5111
|
+
window.visualViewport?.removeEventListener("resize", updateViewport);
|
|
5112
|
+
window.visualViewport?.removeEventListener("scroll", updateViewport);
|
|
5113
|
+
};
|
|
5114
|
+
}, []);
|
|
5115
|
+
useEffect11(() => {
|
|
5116
|
+
const handlePointerDown = (e) => {
|
|
5117
|
+
const target = e.target;
|
|
5118
|
+
if (ref.current?.contains(target)) return;
|
|
5119
|
+
if (target.closest?.(".dialkit-timeline-clip")) return;
|
|
5120
|
+
if (target.closest?.(".dialkit-timeline-label")) return;
|
|
5121
|
+
onClose();
|
|
5122
|
+
};
|
|
5123
|
+
const handleKeyDown = (e) => {
|
|
5124
|
+
if (e.key === "Escape") onClose();
|
|
5125
|
+
};
|
|
5126
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
5127
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
5128
|
+
return () => {
|
|
5129
|
+
document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
5130
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
5131
|
+
};
|
|
5132
|
+
}, [onClose]);
|
|
5133
|
+
const { clip, stepKey } = popover;
|
|
5134
|
+
let controls;
|
|
5135
|
+
let title;
|
|
5136
|
+
if (stepKey) {
|
|
5137
|
+
controls = getClipControls(panelId, `${clip.key}.${stepKey}`);
|
|
5138
|
+
if (stepKey === clip.stepKeys?.[0]) {
|
|
5139
|
+
const from = getControlAt(panelId, `${clip.key}.from`);
|
|
5140
|
+
if (from) {
|
|
5141
|
+
const toIndex = controls.findIndex((control) => control.path === `${clip.key}.${stepKey}.to`);
|
|
5142
|
+
controls = toIndex >= 0 ? [...controls.slice(0, toIndex), from, ...controls.slice(toIndex)] : [...controls, from];
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
title = `${clip.label} \xB7 ${formatStepLabel(stepKey)}`;
|
|
5146
|
+
} else {
|
|
5147
|
+
controls = getClipControls(panelId, clip.key, clipPopoverExclusions(clip));
|
|
5148
|
+
title = clip.label;
|
|
5149
|
+
}
|
|
5150
|
+
if (controls.length === 0) return null;
|
|
5151
|
+
const targetPath = stepKey ? `${clip.key}.${stepKey}` : clip.key;
|
|
5152
|
+
const durationMeta = getControlAt(panelId, `${targetPath}.duration`);
|
|
5153
|
+
const durationValue = durationMeta ? values[durationMeta.path] : void 0;
|
|
5154
|
+
const transitionDuration = durationMeta?.type === "slider" && typeof durationValue === "number" ? {
|
|
5155
|
+
value: durationValue,
|
|
5156
|
+
onChange: (next) => DialStore.updateValue(panelId, durationMeta.path, next),
|
|
5157
|
+
min: Math.max(TIMELINE_MIN_CLIP_DURATION, durationMeta.min ?? 0),
|
|
5158
|
+
max: durationMeta.max,
|
|
5159
|
+
step: durationMeta.step
|
|
5160
|
+
} : void 0;
|
|
5161
|
+
const displayValues = timelinePopoverDisplayValues(values, clip.key, clip.stepKeys, stepKey);
|
|
5162
|
+
const viewportRight = viewport.offsetLeft + viewport.width;
|
|
5163
|
+
const viewportBottom = viewport.offsetTop + viewport.height;
|
|
5164
|
+
const popoverWidth = Math.min(POPOVER_WIDTH, Math.max(220, viewport.width - 24));
|
|
5165
|
+
const left = clamp(
|
|
5166
|
+
popover.anchor.left + popover.anchor.width / 2 - popoverWidth / 2,
|
|
5167
|
+
viewport.offsetLeft + 12,
|
|
5168
|
+
Math.max(viewport.offsetLeft + 12, viewportRight - popoverWidth - 12)
|
|
5169
|
+
);
|
|
5170
|
+
const spaceAbove = Math.max(0, popover.anchor.top - viewport.offsetTop - 22);
|
|
5171
|
+
const spaceBelow = Math.max(0, viewportBottom - popover.anchor.bottom - 22);
|
|
5172
|
+
const placeAbove = naturalHeight === 0 ? spaceAbove >= spaceBelow : naturalHeight <= spaceAbove || naturalHeight > spaceBelow && spaceAbove >= spaceBelow;
|
|
5173
|
+
const availableHeight = placeAbove ? spaceAbove : spaceBelow;
|
|
5174
|
+
const renderedHeight = Math.min(naturalHeight || availableHeight, availableHeight);
|
|
5175
|
+
const unclampedTop = placeAbove ? popover.anchor.top - 10 - renderedHeight : popover.anchor.bottom + 10;
|
|
5176
|
+
const top = clamp(
|
|
5177
|
+
unclampedTop,
|
|
5178
|
+
viewport.offsetTop + 12,
|
|
5179
|
+
Math.max(viewport.offsetTop + 12, viewportBottom - renderedHeight - 12)
|
|
5180
|
+
);
|
|
5181
|
+
return createPortal4(
|
|
5182
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-root", "data-theme": theme, children: /* @__PURE__ */ jsxs14(
|
|
5183
|
+
"div",
|
|
5184
|
+
{
|
|
5185
|
+
ref,
|
|
5186
|
+
className: "dialkit-timeline-popover",
|
|
5187
|
+
"data-placement": placeAbove ? "above" : "below",
|
|
5188
|
+
style: {
|
|
5189
|
+
left,
|
|
5190
|
+
top,
|
|
5191
|
+
width: popoverWidth,
|
|
5192
|
+
maxHeight: availableHeight,
|
|
5193
|
+
visibility: naturalHeight > 0 ? "visible" : "hidden"
|
|
5194
|
+
},
|
|
5195
|
+
role: "dialog",
|
|
5196
|
+
"aria-label": `Edit ${title}`,
|
|
5197
|
+
children: [
|
|
5198
|
+
/* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-popover-header", children: [
|
|
5199
|
+
/* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-popover-title", children: title }),
|
|
5200
|
+
/* @__PURE__ */ jsx18("button", { className: "dialkit-timeline-popover-close", onClick: onClose, title: "Close editor", "aria-label": "Close editor", children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx18("path", { d: "M6 6L18 18M18 6L6 18" }) }) })
|
|
5201
|
+
] }),
|
|
5202
|
+
/* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-popover-body", children: /* @__PURE__ */ jsx18(
|
|
5203
|
+
ControlRenderer,
|
|
5204
|
+
{
|
|
5205
|
+
panelId,
|
|
5206
|
+
controls,
|
|
5207
|
+
values: displayValues,
|
|
5208
|
+
transitionDuration
|
|
5209
|
+
}
|
|
5210
|
+
) })
|
|
5211
|
+
]
|
|
5212
|
+
}
|
|
5213
|
+
) }),
|
|
5214
|
+
document.body
|
|
5215
|
+
);
|
|
5216
|
+
}
|
|
5217
|
+
function clipPopoverExclusions(clip) {
|
|
5218
|
+
return /* @__PURE__ */ new Set([
|
|
5219
|
+
...clip.stepKeys ?? [],
|
|
5220
|
+
...clip.tracks?.map((track) => track.prop) ?? []
|
|
5221
|
+
]);
|
|
5222
|
+
}
|
|
5223
|
+
function getClipControls(panelId, controlPath, excludeChildren) {
|
|
5224
|
+
const panel = DialStore.getPanel(panelId);
|
|
5225
|
+
const folder = panel ? findControl(panel.controls, controlPath) : null;
|
|
5226
|
+
if (!folder?.children) return [];
|
|
5227
|
+
return folder.children.filter((control) => {
|
|
5228
|
+
const childKey = control.path.slice(controlPath.length + 1);
|
|
5229
|
+
if (childKey === "at" || childKey === "duration") return false;
|
|
5230
|
+
return !excludeChildren?.has(childKey);
|
|
5231
|
+
});
|
|
5232
|
+
}
|
|
5233
|
+
function getControlAt(panelId, path) {
|
|
5234
|
+
const panel = DialStore.getPanel(panelId);
|
|
5235
|
+
return panel ? findControl(panel.controls, path) : null;
|
|
5236
|
+
}
|
|
5237
|
+
function TimelineClip({
|
|
5238
|
+
timelineId,
|
|
5239
|
+
clip,
|
|
5240
|
+
at,
|
|
5241
|
+
duration,
|
|
5242
|
+
loop,
|
|
5243
|
+
steps,
|
|
5244
|
+
fixedDuration,
|
|
5245
|
+
composite = false,
|
|
5246
|
+
baseAt = 0,
|
|
5247
|
+
delayMode = false,
|
|
5248
|
+
pxPerSecond,
|
|
5249
|
+
viewStart,
|
|
5250
|
+
timelineDuration,
|
|
5251
|
+
selected,
|
|
5252
|
+
selectedStepKey,
|
|
5253
|
+
onClick,
|
|
5254
|
+
onDrag
|
|
5255
|
+
}) {
|
|
5256
|
+
const dragRef = useRef14(null);
|
|
5257
|
+
const [dragging, setDragging] = useState11(false);
|
|
5258
|
+
const isSteps = Boolean(steps?.length);
|
|
5259
|
+
const handlePointerDown = useCallback14(
|
|
5260
|
+
(e) => {
|
|
5261
|
+
if (e.shiftKey) return;
|
|
5262
|
+
e.stopPropagation();
|
|
5263
|
+
const target = e.target;
|
|
5264
|
+
let mode = "move";
|
|
5265
|
+
let boundaryIndex;
|
|
5266
|
+
const boundary = target.dataset?.boundary;
|
|
5267
|
+
if (boundary !== void 0) {
|
|
5268
|
+
mode = "boundary";
|
|
5269
|
+
boundaryIndex = Number(boundary);
|
|
5270
|
+
} else if (!fixedDuration) {
|
|
5271
|
+
const edge = target.dataset?.edge;
|
|
5272
|
+
if (edge) mode = edge;
|
|
5273
|
+
}
|
|
5274
|
+
dragRef.current = {
|
|
5275
|
+
mode,
|
|
5276
|
+
boundaryIndex,
|
|
5277
|
+
pointerX: e.clientX,
|
|
5278
|
+
at,
|
|
5279
|
+
duration,
|
|
5280
|
+
stepDurations: steps?.map((step) => step.duration),
|
|
5281
|
+
clickEl: target.closest?.("[data-step]") ?? null,
|
|
5282
|
+
moved: false
|
|
5283
|
+
};
|
|
5284
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
5285
|
+
},
|
|
5286
|
+
[at, duration, fixedDuration, steps]
|
|
5287
|
+
);
|
|
5288
|
+
const handlePointerMove = useCallback14(
|
|
5289
|
+
(e) => {
|
|
5290
|
+
const drag = dragRef.current;
|
|
5291
|
+
if (!drag || pxPerSecond <= 0) return;
|
|
5292
|
+
const dx = e.clientX - drag.pointerX;
|
|
5293
|
+
if (!drag.moved) {
|
|
5294
|
+
if (Math.abs(dx) <= DRAG_THRESHOLD_PX) return;
|
|
5295
|
+
drag.moved = true;
|
|
5296
|
+
setDragging(true);
|
|
5297
|
+
onDrag();
|
|
5298
|
+
}
|
|
5299
|
+
const dt = dx / pxPerSecond;
|
|
5300
|
+
if (drag.mode === "boundary" && steps && drag.stepDurations) {
|
|
5301
|
+
const index = drag.boundaryIndex ?? 0;
|
|
5302
|
+
const others = drag.stepDurations.reduce((sum, d, j) => j === index ? sum : sum + d, 0);
|
|
5303
|
+
DialStore.updateValue(
|
|
5304
|
+
timelineId,
|
|
5305
|
+
`${clip.key}.${steps[index].key ?? ""}.duration`,
|
|
5306
|
+
clampStepResize(drag.stepDurations[index] + dt, drag.at, others, timelineDuration)
|
|
5307
|
+
);
|
|
5308
|
+
} else if (drag.mode === "move") {
|
|
5309
|
+
if (delayMode) {
|
|
5310
|
+
DialStore.updateValue(
|
|
5311
|
+
timelineId,
|
|
5312
|
+
`${clip.key}.delay`,
|
|
5313
|
+
clampTrackDelay(drag.at + dt - baseAt, baseAt, drag.duration, timelineDuration)
|
|
5314
|
+
);
|
|
5315
|
+
} else {
|
|
5316
|
+
DialStore.updateValue(timelineId, `${clip.key}.at`, clampClipMove(drag.at + dt, drag.duration, timelineDuration));
|
|
5317
|
+
}
|
|
5318
|
+
} else if (drag.mode === "end") {
|
|
5319
|
+
DialStore.updateValue(
|
|
5320
|
+
timelineId,
|
|
5321
|
+
`${clip.key}.duration`,
|
|
5322
|
+
clampClipResizeEnd(drag.duration + dt, drag.at, timelineDuration)
|
|
5323
|
+
);
|
|
5324
|
+
} else if (steps && drag.stepDurations) {
|
|
5325
|
+
const limit = Math.max(baseAt, 0);
|
|
5326
|
+
const next = clampClipResizeStart(Math.max(drag.at + dt, limit), drag.at, drag.stepDurations[0]);
|
|
5327
|
+
DialStore.updateValues(timelineId, {
|
|
5328
|
+
[delayMode ? `${clip.key}.delay` : `${clip.key}.at`]: delayMode ? Math.max(0, next.at - baseAt) : next.at,
|
|
5329
|
+
[`${clip.key}.${steps[0].key ?? ""}.duration`]: next.duration
|
|
5330
|
+
});
|
|
5331
|
+
} else {
|
|
5332
|
+
const limit = Math.max(baseAt, 0);
|
|
5333
|
+
const next = clampClipResizeStart(Math.max(drag.at + dt, limit), drag.at, drag.duration);
|
|
5334
|
+
DialStore.updateValues(timelineId, {
|
|
5335
|
+
[delayMode ? `${clip.key}.delay` : `${clip.key}.at`]: delayMode ? Math.max(0, next.at - baseAt) : next.at,
|
|
5336
|
+
[`${clip.key}.duration`]: next.duration
|
|
5337
|
+
});
|
|
5338
|
+
}
|
|
5339
|
+
},
|
|
5340
|
+
[baseAt, clip.key, delayMode, onDrag, pxPerSecond, steps, timelineId, timelineDuration]
|
|
5341
|
+
);
|
|
5342
|
+
const handlePointerUp = useCallback14(
|
|
5343
|
+
(e) => {
|
|
5344
|
+
const drag = dragRef.current;
|
|
5345
|
+
dragRef.current = null;
|
|
5346
|
+
setDragging(false);
|
|
5347
|
+
if (drag && !drag.moved) {
|
|
5348
|
+
const stepKey = drag.clickEl?.dataset?.step;
|
|
5349
|
+
const anchorEl = drag.clickEl ?? e.currentTarget;
|
|
5350
|
+
onClick(clip, anchorEl.getBoundingClientRect(), stepKey);
|
|
5351
|
+
}
|
|
5352
|
+
},
|
|
5353
|
+
[clip, onClick]
|
|
5354
|
+
);
|
|
5355
|
+
const handlePointerCancel = useCallback14(() => {
|
|
5356
|
+
dragRef.current = null;
|
|
5357
|
+
setDragging(false);
|
|
5358
|
+
}, []);
|
|
5359
|
+
const width = Math.max(duration * pxPerSecond, 14);
|
|
5360
|
+
const resizable = duration > 0 && !fixedDuration && !composite;
|
|
5361
|
+
const durationText = `${fixedDuration && !composite ? "~" : ""}${formatSeconds(duration)}`;
|
|
5362
|
+
const looping = loop === "repeat" && duration > 0;
|
|
5363
|
+
const ghostCycles = [];
|
|
5364
|
+
if (looping) {
|
|
5365
|
+
const maxGhostCycles = 256;
|
|
5366
|
+
const firstGhostIndex = Math.max(1, Math.floor((viewStart - at) / duration));
|
|
5367
|
+
for (let offset = 0; offset < maxGhostCycles; offset++) {
|
|
5368
|
+
const index = firstGhostIndex + offset;
|
|
5369
|
+
const start = at + duration * index;
|
|
5370
|
+
if (start >= timelineDuration - 1e-6) break;
|
|
5371
|
+
ghostCycles.push({
|
|
5372
|
+
start,
|
|
5373
|
+
duration: Math.min(duration, timelineDuration - start),
|
|
5374
|
+
index
|
|
5375
|
+
});
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5378
|
+
const boundaryOffsets = [];
|
|
5379
|
+
if (steps) {
|
|
5380
|
+
let cumulative = 0;
|
|
5381
|
+
for (const step of steps) {
|
|
5382
|
+
cumulative += step.duration;
|
|
5383
|
+
boundaryOffsets.push(cumulative);
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
const barTitle = composite ? `${clip.label} \u2014 composite of its property tracks${looping ? " \xB7 repeats through timeline" : ""} \xB7 click to expand` : `${clip.label} \u2014 ${formatSeconds(at)} for ${durationText}${fixedDuration ? " (duration set by spring physics)" : ""}${looping ? " \xB7 repeats through timeline" : ""}${delayMode ? " \xB7 drag to phase-shift" : ""}`;
|
|
5387
|
+
return /* @__PURE__ */ jsxs14(Fragment5, { children: [
|
|
5388
|
+
ghostCycles.map((cycle) => {
|
|
5389
|
+
const ghostWidth = Math.max(1, cycle.duration * pxPerSecond - 2);
|
|
5390
|
+
return /* @__PURE__ */ jsx18(
|
|
5391
|
+
"div",
|
|
5392
|
+
{
|
|
5393
|
+
className: "dialkit-timeline-clip-ghost",
|
|
5394
|
+
"data-steps": isSteps || void 0,
|
|
5395
|
+
"aria-hidden": "true",
|
|
5396
|
+
style: {
|
|
5397
|
+
left: (cycle.start - viewStart) * pxPerSecond + 1,
|
|
5398
|
+
width: ghostWidth,
|
|
5399
|
+
background: clip.color
|
|
5400
|
+
},
|
|
5401
|
+
children: steps?.map((step, stepIndex) => /* @__PURE__ */ jsx18(
|
|
5402
|
+
"span",
|
|
5403
|
+
{
|
|
5404
|
+
className: "dialkit-timeline-clip-ghost-segment",
|
|
5405
|
+
style: { width: step.duration * pxPerSecond }
|
|
5406
|
+
},
|
|
5407
|
+
step.key ?? `step:${stepIndex}`
|
|
5408
|
+
))
|
|
5409
|
+
},
|
|
5410
|
+
`ghost:${cycle.index}`
|
|
5411
|
+
);
|
|
5412
|
+
}),
|
|
5413
|
+
/* @__PURE__ */ jsx18(
|
|
5414
|
+
"div",
|
|
5415
|
+
{
|
|
5416
|
+
className: "dialkit-timeline-clip",
|
|
5417
|
+
"data-steps": isSteps || void 0,
|
|
5418
|
+
"data-composite": composite || void 0,
|
|
5419
|
+
"data-selected": selected || void 0,
|
|
5420
|
+
"data-dragging": dragging || void 0,
|
|
5421
|
+
style: {
|
|
5422
|
+
left: (at - viewStart) * pxPerSecond,
|
|
5423
|
+
width,
|
|
5424
|
+
background: composite ? `${clip.color}80` : clip.color
|
|
5425
|
+
},
|
|
5426
|
+
onPointerDown: handlePointerDown,
|
|
5427
|
+
onPointerMove: handlePointerMove,
|
|
5428
|
+
onPointerUp: handlePointerUp,
|
|
5429
|
+
onPointerCancel: handlePointerCancel,
|
|
5430
|
+
onLostPointerCapture: handlePointerCancel,
|
|
5431
|
+
title: barTitle,
|
|
5432
|
+
children: composite ? /* @__PURE__ */ jsx18(Fragment5, { children: width > 56 && /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-clip-duration", children: durationText }) }) : isSteps ? /* @__PURE__ */ jsxs14(Fragment5, { children: [
|
|
5433
|
+
steps.map((step) => {
|
|
5434
|
+
const segmentWidth = step.duration * pxPerSecond;
|
|
5435
|
+
return /* @__PURE__ */ jsx18(
|
|
5436
|
+
"div",
|
|
5437
|
+
{
|
|
5438
|
+
className: "dialkit-timeline-clip-segment",
|
|
5439
|
+
"data-step": step.key ?? void 0,
|
|
5440
|
+
"data-selected": selectedStepKey === step.key || void 0,
|
|
5441
|
+
style: { width: segmentWidth },
|
|
5442
|
+
children: segmentWidth > 52 && /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-clip-duration", children: formatSeconds(step.duration) })
|
|
5443
|
+
},
|
|
5444
|
+
step.key ?? "step"
|
|
5445
|
+
);
|
|
5446
|
+
}),
|
|
5447
|
+
steps.map(
|
|
5448
|
+
(step, index) => step.isPhysics ? null : /* @__PURE__ */ jsx18(
|
|
5449
|
+
"div",
|
|
5450
|
+
{
|
|
5451
|
+
className: "dialkit-timeline-clip-handle",
|
|
5452
|
+
"data-boundary": index,
|
|
5453
|
+
style: { left: boundaryOffsets[index] * pxPerSecond - 4 }
|
|
5454
|
+
},
|
|
5455
|
+
`boundary:${step.key}`
|
|
5456
|
+
)
|
|
5457
|
+
),
|
|
5458
|
+
!steps[0].isPhysics && /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-clip-handle", "data-edge": "start" })
|
|
5459
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment5, { children: [
|
|
5460
|
+
resizable && /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-clip-handle", "data-edge": "start" }),
|
|
5461
|
+
width > 56 && /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-clip-duration", children: durationText }),
|
|
5462
|
+
resizable && /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-clip-handle", "data-edge": "end" })
|
|
5463
|
+
] })
|
|
5464
|
+
}
|
|
5465
|
+
),
|
|
5466
|
+
looping && /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-loop-infinity", "aria-hidden": "true", title: "Repeats indefinitely", children: "\u221E" })
|
|
5467
|
+
] });
|
|
5468
|
+
}
|
|
5469
|
+
|
|
2842
5470
|
// src/components/ButtonGroup.tsx
|
|
2843
|
-
import { jsx as
|
|
5471
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2844
5472
|
function ButtonGroup({ buttons }) {
|
|
2845
|
-
return /* @__PURE__ */
|
|
5473
|
+
return /* @__PURE__ */ jsx19("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ jsx19(
|
|
2846
5474
|
"button",
|
|
2847
5475
|
{
|
|
2848
5476
|
className: "dialkit-button",
|
|
@@ -2854,10 +5482,10 @@ function ButtonGroup({ buttons }) {
|
|
|
2854
5482
|
}
|
|
2855
5483
|
|
|
2856
5484
|
// src/components/ShortcutsMenu.tsx
|
|
2857
|
-
import { useState as
|
|
2858
|
-
import { createPortal as
|
|
2859
|
-
import { motion as
|
|
2860
|
-
import { Fragment as
|
|
5485
|
+
import { useState as useState12, useRef as useRef15, useEffect as useEffect12, useCallback as useCallback15 } from "react";
|
|
5486
|
+
import { createPortal as createPortal5 } from "react-dom";
|
|
5487
|
+
import { motion as motion8, AnimatePresence as AnimatePresence6 } from "motion/react";
|
|
5488
|
+
import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2861
5489
|
function formatShortcutKey(sc) {
|
|
2862
5490
|
if (!sc.key) return "\u2014";
|
|
2863
5491
|
const mod = sc.modifier === "alt" ? "\u2325" : sc.modifier === "shift" ? "\u21E7" : sc.modifier === "meta" ? "\u2318" : "";
|
|
@@ -2877,23 +5505,23 @@ function formatInteraction(sc) {
|
|
|
2877
5505
|
}
|
|
2878
5506
|
}
|
|
2879
5507
|
function ShortcutsMenu({ panelId }) {
|
|
2880
|
-
const [isOpen, setIsOpen] =
|
|
2881
|
-
const triggerRef =
|
|
2882
|
-
const dropdownRef =
|
|
2883
|
-
const [pos, setPos] =
|
|
2884
|
-
const open =
|
|
5508
|
+
const [isOpen, setIsOpen] = useState12(false);
|
|
5509
|
+
const triggerRef = useRef15(null);
|
|
5510
|
+
const dropdownRef = useRef15(null);
|
|
5511
|
+
const [pos, setPos] = useState12({ top: 0, right: 0 });
|
|
5512
|
+
const open = useCallback15(() => {
|
|
2885
5513
|
const rect = triggerRef.current?.getBoundingClientRect();
|
|
2886
5514
|
if (rect) {
|
|
2887
5515
|
setPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
|
|
2888
5516
|
}
|
|
2889
5517
|
setIsOpen(true);
|
|
2890
5518
|
}, []);
|
|
2891
|
-
const close =
|
|
2892
|
-
const toggle =
|
|
5519
|
+
const close = useCallback15(() => setIsOpen(false), []);
|
|
5520
|
+
const toggle = useCallback15(() => {
|
|
2893
5521
|
if (isOpen) close();
|
|
2894
5522
|
else open();
|
|
2895
5523
|
}, [isOpen, open, close]);
|
|
2896
|
-
|
|
5524
|
+
useEffect12(() => {
|
|
2897
5525
|
if (!isOpen) return;
|
|
2898
5526
|
const handler = (e) => {
|
|
2899
5527
|
const target = e.target;
|
|
@@ -2924,9 +5552,9 @@ function ShortcutsMenu({ panelId }) {
|
|
|
2924
5552
|
label: findLabel(panel.controls)
|
|
2925
5553
|
};
|
|
2926
5554
|
});
|
|
2927
|
-
return /* @__PURE__ */
|
|
2928
|
-
/* @__PURE__ */
|
|
2929
|
-
|
|
5555
|
+
return /* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
5556
|
+
/* @__PURE__ */ jsx20(
|
|
5557
|
+
motion8.button,
|
|
2930
5558
|
{
|
|
2931
5559
|
ref: triggerRef,
|
|
2932
5560
|
className: "dialkit-shortcuts-trigger",
|
|
@@ -2934,19 +5562,19 @@ function ShortcutsMenu({ panelId }) {
|
|
|
2934
5562
|
title: "Keyboard shortcuts",
|
|
2935
5563
|
whileTap: { scale: 0.9 },
|
|
2936
5564
|
transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
|
|
2937
|
-
children: /* @__PURE__ */
|
|
2938
|
-
/* @__PURE__ */
|
|
2939
|
-
/* @__PURE__ */
|
|
2940
|
-
/* @__PURE__ */
|
|
2941
|
-
/* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
2943
|
-
/* @__PURE__ */
|
|
5565
|
+
children: /* @__PURE__ */ jsxs15("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5566
|
+
/* @__PURE__ */ jsx20("rect", { x: "2", y: "6", width: "20", height: "12", rx: "2" }),
|
|
5567
|
+
/* @__PURE__ */ jsx20("path", { d: "M6 10H6.01" }),
|
|
5568
|
+
/* @__PURE__ */ jsx20("path", { d: "M10 10H10.01" }),
|
|
5569
|
+
/* @__PURE__ */ jsx20("path", { d: "M14 10H14.01" }),
|
|
5570
|
+
/* @__PURE__ */ jsx20("path", { d: "M18 10H18.01" }),
|
|
5571
|
+
/* @__PURE__ */ jsx20("path", { d: "M8 14H16" })
|
|
2944
5572
|
] })
|
|
2945
5573
|
}
|
|
2946
5574
|
),
|
|
2947
|
-
|
|
2948
|
-
/* @__PURE__ */
|
|
2949
|
-
|
|
5575
|
+
createPortal5(
|
|
5576
|
+
/* @__PURE__ */ jsx20(AnimatePresence6, { children: isOpen && /* @__PURE__ */ jsxs15(
|
|
5577
|
+
motion8.div,
|
|
2950
5578
|
{
|
|
2951
5579
|
ref: dropdownRef,
|
|
2952
5580
|
className: "dialkit-root dialkit-shortcuts-dropdown",
|
|
@@ -2956,13 +5584,13 @@ function ShortcutsMenu({ panelId }) {
|
|
|
2956
5584
|
exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
|
|
2957
5585
|
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
2958
5586
|
children: [
|
|
2959
|
-
/* @__PURE__ */
|
|
2960
|
-
/* @__PURE__ */
|
|
2961
|
-
/* @__PURE__ */
|
|
2962
|
-
/* @__PURE__ */
|
|
2963
|
-
/* @__PURE__ */
|
|
5587
|
+
/* @__PURE__ */ jsx20("div", { className: "dialkit-shortcuts-title", children: "Keyboard Shortcuts" }),
|
|
5588
|
+
/* @__PURE__ */ jsx20("div", { className: "dialkit-shortcuts-list", children: rows.map((row) => /* @__PURE__ */ jsxs15("div", { className: "dialkit-shortcuts-row", children: [
|
|
5589
|
+
/* @__PURE__ */ jsx20("span", { className: "dialkit-shortcuts-row-key", children: formatShortcutKey(row.shortcut) }),
|
|
5590
|
+
/* @__PURE__ */ jsx20("span", { className: "dialkit-shortcuts-row-label", children: row.label }),
|
|
5591
|
+
/* @__PURE__ */ jsx20("span", { className: "dialkit-shortcuts-row-mode", children: formatInteraction(row.shortcut) })
|
|
2964
5592
|
] }, row.path)) }),
|
|
2965
|
-
/* @__PURE__ */
|
|
5593
|
+
/* @__PURE__ */ jsx20("div", { className: "dialkit-shortcuts-hint", children: "See pill badges on controls for keys" })
|
|
2966
5594
|
]
|
|
2967
5595
|
}
|
|
2968
5596
|
) }),
|
|
@@ -2973,8 +5601,10 @@ function ShortcutsMenu({ panelId }) {
|
|
|
2973
5601
|
export {
|
|
2974
5602
|
ButtonGroup,
|
|
2975
5603
|
ColorControl,
|
|
5604
|
+
ControlRenderer,
|
|
2976
5605
|
DialRoot,
|
|
2977
5606
|
DialStore,
|
|
5607
|
+
DialTimeline,
|
|
2978
5608
|
EasingVisualization,
|
|
2979
5609
|
Folder,
|
|
2980
5610
|
PresetManager,
|
|
@@ -2984,9 +5614,12 @@ export {
|
|
|
2984
5614
|
SpringControl,
|
|
2985
5615
|
SpringVisualization,
|
|
2986
5616
|
TextControl,
|
|
5617
|
+
TimelineStore,
|
|
2987
5618
|
Toggle,
|
|
2988
5619
|
TransitionControl,
|
|
5620
|
+
formatClock,
|
|
2989
5621
|
useDialKit,
|
|
2990
|
-
useDialKitController
|
|
5622
|
+
useDialKitController,
|
|
5623
|
+
useDialTimeline
|
|
2991
5624
|
};
|
|
2992
5625
|
//# sourceMappingURL=index.js.map
|