dialkit 1.2.1 → 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 +653 -6
- package/dist/dropdown-position.d.ts +15 -0
- package/dist/dropdown-position.js +22 -0
- package/dist/dropdown-position.js.map +1 -0
- package/dist/icons.d.ts +4 -1
- package/dist/icons.js +13 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +3796 -768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +315 -10
- package/dist/index.d.ts +315 -10
- package/dist/index.js +3733 -711
- package/dist/index.js.map +1 -1
- package/dist/panel-drag.d.ts +19 -0
- package/dist/panel-drag.js +72 -0
- package/dist/panel-drag.js.map +1 -0
- package/dist/solid/index.d.ts +264 -5
- package/dist/solid/index.js +5277 -1632
- package/dist/solid/index.js.map +1 -1
- package/dist/store/index.cjs +329 -57
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +43 -4
- package/dist/store/index.d.ts +43 -4
- package/dist/store/index.js +321 -56
- package/dist/store/index.js.map +1 -1
- package/dist/styles.css +809 -7
- 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 +218 -16
- package/dist/svelte/components/DialRoot.svelte.d.ts +1 -0
- package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Folder.svelte +24 -13
- package/dist/svelte/components/Folder.svelte.d.ts +1 -0
- package/dist/svelte/components/Folder.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Panel.svelte +104 -71
- package/dist/svelte/components/Panel.svelte.d.ts +4 -0
- package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
- package/dist/svelte/components/PresetManager.svelte +5 -5
- package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/SelectControl.svelte +6 -14
- package/dist/svelte/components/SelectControl.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/createDialKit.svelte.d.ts +11 -1
- package/dist/svelte/createDialKit.svelte.d.ts.map +1 -1
- package/dist/svelte/createDialKit.svelte.js +61 -34
- 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 +7 -3
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +4 -1
- 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 +809 -7
- 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 +325 -12
- package/dist/vue/index.js +3226 -280
- package/dist/vue/index.js.map +1 -1
- package/package.json +23 -13
- package/dist/solid/index.cjs +0 -3079
- package/dist/solid/index.cjs.map +0 -1
- package/dist/solid/index.d.cts +0 -258
- package/dist/vue/index.cjs +0 -3056
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.cts +0 -675
package/dist/vue/index.js
CHANGED
|
@@ -3,9 +3,108 @@ import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
|
|
3
3
|
|
|
4
4
|
// src/store/DialStore.ts
|
|
5
5
|
var EMPTY_VALUES = Object.freeze({});
|
|
6
|
+
function resolveDialValues(config, flatValues) {
|
|
7
|
+
return resolveConfigValues(config, flatValues, "");
|
|
8
|
+
}
|
|
9
|
+
function flattenDialValueUpdates(config, updates) {
|
|
10
|
+
const values = {};
|
|
11
|
+
if (typeof updates === "object" && updates !== null) {
|
|
12
|
+
flattenConfigUpdates(config, updates, "", values);
|
|
13
|
+
}
|
|
14
|
+
return values;
|
|
15
|
+
}
|
|
16
|
+
function resolveConfigValues(config, flatValues, prefix) {
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const [key, configValue] of Object.entries(config)) {
|
|
19
|
+
if (key === "_collapsed") continue;
|
|
20
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
21
|
+
if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
|
|
22
|
+
result[key] = flatValues[path] ?? configValue[0];
|
|
23
|
+
} else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
|
|
24
|
+
result[key] = flatValues[path] ?? configValue;
|
|
25
|
+
} else if (isSpringConfigValue(configValue) || isEasingConfigValue(configValue)) {
|
|
26
|
+
result[key] = flatValues[path] ?? configValue;
|
|
27
|
+
} else if (isActionConfigValue(configValue)) {
|
|
28
|
+
result[key] = flatValues[path] ?? configValue;
|
|
29
|
+
} else if (isSelectConfigValue(configValue)) {
|
|
30
|
+
const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
|
|
31
|
+
result[key] = flatValues[path] ?? defaultValue;
|
|
32
|
+
} else if (isColorConfigValue(configValue)) {
|
|
33
|
+
result[key] = flatValues[path] ?? configValue.default ?? "#000000";
|
|
34
|
+
} else if (isTextConfigValue(configValue)) {
|
|
35
|
+
result[key] = flatValues[path] ?? configValue.default ?? "";
|
|
36
|
+
} else if (typeof configValue === "object" && configValue !== null) {
|
|
37
|
+
result[key] = resolveConfigValues(configValue, flatValues, path);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
function flattenConfigUpdates(config, updates, prefix, values) {
|
|
43
|
+
for (const [key, configValue] of Object.entries(config)) {
|
|
44
|
+
if (key === "_collapsed" || !(key in updates)) continue;
|
|
45
|
+
const nextValue = updates[key];
|
|
46
|
+
if (nextValue === void 0) continue;
|
|
47
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
48
|
+
if (isActionConfigValue(configValue)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (isLeafConfigValue(configValue)) {
|
|
52
|
+
values[path] = nextValue;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (typeof configValue === "object" && configValue !== null && typeof nextValue === "object" && nextValue !== null && !Array.isArray(nextValue)) {
|
|
56
|
+
flattenConfigUpdates(configValue, nextValue, path, values);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function isLeafConfigValue(value) {
|
|
61
|
+
return Array.isArray(value) && value.length <= 4 && typeof value[0] === "number" || typeof value === "number" || typeof value === "boolean" || typeof value === "string" || isSpringConfigValue(value) || isEasingConfigValue(value) || isActionConfigValue(value) || isSelectConfigValue(value) || isColorConfigValue(value) || isTextConfigValue(value);
|
|
62
|
+
}
|
|
63
|
+
function hasType(value, type) {
|
|
64
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === type;
|
|
65
|
+
}
|
|
66
|
+
function isSpringConfigValue(value) {
|
|
67
|
+
return hasType(value, "spring");
|
|
68
|
+
}
|
|
69
|
+
function isEasingConfigValue(value) {
|
|
70
|
+
return hasType(value, "easing");
|
|
71
|
+
}
|
|
72
|
+
function isHexColor(value) {
|
|
73
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value);
|
|
74
|
+
}
|
|
75
|
+
function formatLabel(key) {
|
|
76
|
+
return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
77
|
+
}
|
|
78
|
+
function inferStep(min, max) {
|
|
79
|
+
const range = max - min;
|
|
80
|
+
if (range <= 1) return 0.01;
|
|
81
|
+
if (range <= 10) return 0.1;
|
|
82
|
+
if (range <= 100) return 1;
|
|
83
|
+
return 10;
|
|
84
|
+
}
|
|
85
|
+
function isActionConfigValue(value) {
|
|
86
|
+
return hasType(value, "action");
|
|
87
|
+
}
|
|
88
|
+
function isSelectConfigValue(value) {
|
|
89
|
+
return hasType(value, "select") && "options" in value && Array.isArray(value.options);
|
|
90
|
+
}
|
|
91
|
+
function isColorConfigValue(value) {
|
|
92
|
+
return hasType(value, "color");
|
|
93
|
+
}
|
|
94
|
+
function isTextConfigValue(value) {
|
|
95
|
+
return hasType(value, "text");
|
|
96
|
+
}
|
|
97
|
+
function getFirstOptionValue(options) {
|
|
98
|
+
const first = options[0];
|
|
99
|
+
if (first === void 0) return "";
|
|
100
|
+
return typeof first === "string" ? first : first.value;
|
|
101
|
+
}
|
|
6
102
|
var DialStoreClass = class {
|
|
7
103
|
constructor() {
|
|
8
104
|
this.panels = /* @__PURE__ */ new Map();
|
|
105
|
+
this.panelsSnapshot = [];
|
|
106
|
+
this.standardPanelsSnapshot = [];
|
|
107
|
+
this.timelinePanelsSnapshot = [];
|
|
9
108
|
this.listeners = /* @__PURE__ */ new Map();
|
|
10
109
|
this.globalListeners = /* @__PURE__ */ new Set();
|
|
11
110
|
this.snapshots = /* @__PURE__ */ new Map();
|
|
@@ -13,87 +112,144 @@ var DialStoreClass = class {
|
|
|
13
112
|
this.presets = /* @__PURE__ */ new Map();
|
|
14
113
|
this.activePreset = /* @__PURE__ */ new Map();
|
|
15
114
|
this.baseValues = /* @__PURE__ */ new Map();
|
|
115
|
+
this.defaultValues = /* @__PURE__ */ new Map();
|
|
116
|
+
this.registrationCounts = /* @__PURE__ */ new Map();
|
|
117
|
+
this.retainedPanels = /* @__PURE__ */ new Set();
|
|
118
|
+
this.persistConfigs = /* @__PURE__ */ new Map();
|
|
16
119
|
}
|
|
17
|
-
registerPanel(id, name, config, shortcuts) {
|
|
120
|
+
registerPanel(id, name, config, shortcuts, options = {}) {
|
|
121
|
+
const existingPanel = this.panels.get(id);
|
|
122
|
+
if (existingPanel && existingPanel.kind !== options.kind) {
|
|
123
|
+
console.warn(
|
|
124
|
+
`[dialkit] Panel id "${id}" cannot be shared by a timeline and a standard panel; the most recent registration controls where it renders.`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
this.configurePanelRetention(id, options);
|
|
128
|
+
this.registrationCounts.set(id, (this.registrationCounts.get(id) ?? 0) + 1);
|
|
18
129
|
const controls = this.parseConfig(config, "", shortcuts);
|
|
19
|
-
const
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
130
|
+
const controlsByPath = this.mapControlsByPath(controls);
|
|
131
|
+
const defaultValues = this.flattenValues(config, "");
|
|
132
|
+
this.initTransitionModes(config, "", defaultValues);
|
|
133
|
+
const persisted = this.loadPersistedPanel(id);
|
|
134
|
+
const previousValues = this.panels.get(id)?.values ?? this.snapshots.get(id) ?? persisted?.values ?? {};
|
|
135
|
+
const values = this.reconcileValues(defaultValues, previousValues, controlsByPath);
|
|
136
|
+
const previousBaseValues = this.baseValues.get(id) ?? persisted?.baseValues ?? persisted?.values ?? {};
|
|
137
|
+
const baseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
|
|
138
|
+
this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {}, kind: options.kind });
|
|
22
139
|
this.snapshots.set(id, { ...values });
|
|
23
|
-
this.baseValues.set(id,
|
|
140
|
+
this.baseValues.set(id, baseValues);
|
|
141
|
+
this.defaultValues.set(id, { ...defaultValues });
|
|
142
|
+
const existingPresets = this.presets.get(id) ?? persisted?.presets;
|
|
143
|
+
if (existingPresets) {
|
|
144
|
+
this.presets.set(id, this.reconcilePresets(existingPresets, defaultValues, controlsByPath));
|
|
145
|
+
}
|
|
146
|
+
if (!this.activePreset.has(id) && persisted?.activePresetId !== void 0) {
|
|
147
|
+
this.activePreset.set(id, persisted.activePresetId);
|
|
148
|
+
}
|
|
149
|
+
this.persistPanel(id);
|
|
150
|
+
this.notify(id);
|
|
24
151
|
this.notifyGlobal();
|
|
25
152
|
}
|
|
26
|
-
updatePanel(id, name, config, shortcuts) {
|
|
153
|
+
updatePanel(id, name, config, shortcuts, options = {}) {
|
|
154
|
+
this.configurePanelRetention(id, options);
|
|
27
155
|
const existing = this.panels.get(id);
|
|
28
156
|
if (!existing) {
|
|
29
|
-
this.registerPanel(id, name, config, shortcuts);
|
|
157
|
+
this.registerPanel(id, name, config, shortcuts, options);
|
|
30
158
|
return;
|
|
31
159
|
}
|
|
32
160
|
const controls = this.parseConfig(config, "", shortcuts);
|
|
33
161
|
const controlsByPath = this.mapControlsByPath(controls);
|
|
34
162
|
const defaultValues = this.flattenValues(config, "");
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
existing.values[path],
|
|
39
|
-
defaultValue,
|
|
40
|
-
controlsByPath.get(path)
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
this.initTransitionModes(config, "", nextValues);
|
|
44
|
-
for (const [path, mode] of Object.entries(existing.values)) {
|
|
45
|
-
if (!path.endsWith(".__mode")) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const transitionPath = path.slice(0, -"__mode".length - 1);
|
|
49
|
-
const transitionControl = controlsByPath.get(transitionPath);
|
|
50
|
-
if (transitionControl?.type === "transition") {
|
|
51
|
-
nextValues[path] = mode;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
|
|
163
|
+
this.initTransitionModes(config, "", defaultValues);
|
|
164
|
+
const nextValues = this.reconcileValues(defaultValues, existing.values, controlsByPath);
|
|
165
|
+
const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts, kind: options.kind ?? existing.kind };
|
|
55
166
|
this.panels.set(id, nextPanel);
|
|
56
167
|
this.snapshots.set(id, { ...nextValues });
|
|
57
168
|
const previousBaseValues = this.baseValues.get(id) ?? {};
|
|
58
|
-
const nextBaseValues =
|
|
59
|
-
for (const [path, defaultValue] of Object.entries(defaultValues)) {
|
|
60
|
-
nextBaseValues[path] = this.normalizePreservedValue(
|
|
61
|
-
previousBaseValues[path],
|
|
62
|
-
defaultValue,
|
|
63
|
-
controlsByPath.get(path)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
169
|
+
const nextBaseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
|
|
66
170
|
for (const [path, value] of Object.entries(nextValues)) {
|
|
67
171
|
if (path.endsWith(".__mode")) {
|
|
68
172
|
nextBaseValues[path] = value;
|
|
69
173
|
}
|
|
70
174
|
}
|
|
71
175
|
this.baseValues.set(id, nextBaseValues);
|
|
176
|
+
this.defaultValues.set(id, { ...defaultValues });
|
|
177
|
+
this.presets.set(id, this.reconcilePresets(this.presets.get(id) ?? [], defaultValues, controlsByPath));
|
|
178
|
+
this.persistPanel(id);
|
|
72
179
|
this.notify(id);
|
|
73
180
|
this.notifyGlobal();
|
|
74
181
|
}
|
|
75
182
|
unregisterPanel(id) {
|
|
183
|
+
const nextCount = (this.registrationCounts.get(id) ?? 1) - 1;
|
|
184
|
+
if (nextCount > 0) {
|
|
185
|
+
this.registrationCounts.set(id, nextCount);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.registrationCounts.delete(id);
|
|
76
189
|
this.panels.delete(id);
|
|
77
|
-
this.listeners.delete(id);
|
|
78
|
-
this.
|
|
79
|
-
this.
|
|
80
|
-
|
|
190
|
+
if (this.listeners.get(id)?.size === 0) this.listeners.delete(id);
|
|
191
|
+
if (this.actionListeners.get(id)?.size === 0) this.actionListeners.delete(id);
|
|
192
|
+
if (!this.retainedPanels.has(id)) {
|
|
193
|
+
this.snapshots.delete(id);
|
|
194
|
+
this.baseValues.delete(id);
|
|
195
|
+
this.defaultValues.delete(id);
|
|
196
|
+
this.presets.delete(id);
|
|
197
|
+
this.activePreset.delete(id);
|
|
198
|
+
this.persistConfigs.delete(id);
|
|
199
|
+
}
|
|
81
200
|
this.notifyGlobal();
|
|
82
201
|
}
|
|
83
202
|
updateValue(panelId, path, value) {
|
|
203
|
+
this.updateValues(panelId, { [path]: value });
|
|
204
|
+
}
|
|
205
|
+
updateValues(panelId, updates) {
|
|
84
206
|
const panel = this.panels.get(panelId);
|
|
85
207
|
if (!panel) return;
|
|
86
|
-
|
|
208
|
+
const validUpdates = {};
|
|
209
|
+
for (const [path, value] of Object.entries(updates)) {
|
|
210
|
+
if (!Object.prototype.hasOwnProperty.call(panel.values, path)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const control = this.findControlByPath(panel.controls, path);
|
|
214
|
+
if (control?.type === "action") {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
panel.values[path] = value;
|
|
218
|
+
validUpdates[path] = value;
|
|
219
|
+
}
|
|
220
|
+
if (Object.keys(validUpdates).length === 0) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
87
223
|
const activeId = this.activePreset.get(panelId);
|
|
88
224
|
if (activeId) {
|
|
89
225
|
const presets = this.presets.get(panelId) ?? [];
|
|
90
226
|
const preset = presets.find((p) => p.id === activeId);
|
|
91
|
-
if (preset)
|
|
227
|
+
if (preset) {
|
|
228
|
+
for (const [path, value] of Object.entries(validUpdates)) {
|
|
229
|
+
preset.values[path] = value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
92
232
|
} else {
|
|
93
233
|
const base = this.baseValues.get(panelId);
|
|
94
|
-
if (base)
|
|
234
|
+
if (base) {
|
|
235
|
+
for (const [path, value] of Object.entries(validUpdates)) {
|
|
236
|
+
base[path] = value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
95
239
|
}
|
|
96
240
|
this.snapshots.set(panelId, { ...panel.values });
|
|
241
|
+
this.persistPanel(panelId);
|
|
242
|
+
this.notify(panelId);
|
|
243
|
+
}
|
|
244
|
+
resetValues(panelId) {
|
|
245
|
+
const panel = this.panels.get(panelId);
|
|
246
|
+
const defaults = this.defaultValues.get(panelId);
|
|
247
|
+
if (!panel || !defaults) return;
|
|
248
|
+
panel.values = { ...defaults };
|
|
249
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
250
|
+
this.baseValues.set(panelId, { ...defaults });
|
|
251
|
+
this.activePreset.set(panelId, null);
|
|
252
|
+
this.persistPanel(panelId);
|
|
97
253
|
this.notify(panelId);
|
|
98
254
|
}
|
|
99
255
|
updateSpringMode(panelId, path, mode) {
|
|
@@ -109,6 +265,7 @@ var DialStoreClass = class {
|
|
|
109
265
|
if (!panel) return;
|
|
110
266
|
panel.values[`${path}.__mode`] = mode;
|
|
111
267
|
this.snapshots.set(panelId, { ...panel.values });
|
|
268
|
+
this.persistPanel(panelId);
|
|
112
269
|
this.notify(panelId);
|
|
113
270
|
}
|
|
114
271
|
getTransitionMode(panelId, path) {
|
|
@@ -123,8 +280,10 @@ var DialStoreClass = class {
|
|
|
123
280
|
getValues(panelId) {
|
|
124
281
|
return this.snapshots.get(panelId) ?? EMPTY_VALUES;
|
|
125
282
|
}
|
|
126
|
-
getPanels() {
|
|
127
|
-
return
|
|
283
|
+
getPanels(kind) {
|
|
284
|
+
if (kind === "panel") return this.standardPanelsSnapshot;
|
|
285
|
+
if (kind === "timeline") return this.timelinePanelsSnapshot;
|
|
286
|
+
return this.panelsSnapshot;
|
|
128
287
|
}
|
|
129
288
|
getPanel(id) {
|
|
130
289
|
return this.panels.get(id);
|
|
@@ -135,7 +294,11 @@ var DialStoreClass = class {
|
|
|
135
294
|
}
|
|
136
295
|
this.listeners.get(panelId).add(listener);
|
|
137
296
|
return () => {
|
|
138
|
-
this.listeners.get(panelId)
|
|
297
|
+
const listeners = this.listeners.get(panelId);
|
|
298
|
+
listeners?.delete(listener);
|
|
299
|
+
if (listeners?.size === 0 && !this.panels.has(panelId)) {
|
|
300
|
+
this.listeners.delete(panelId);
|
|
301
|
+
}
|
|
139
302
|
};
|
|
140
303
|
}
|
|
141
304
|
subscribeGlobal(listener) {
|
|
@@ -148,7 +311,11 @@ var DialStoreClass = class {
|
|
|
148
311
|
}
|
|
149
312
|
this.actionListeners.get(panelId).add(listener);
|
|
150
313
|
return () => {
|
|
151
|
-
this.actionListeners.get(panelId)
|
|
314
|
+
const listeners = this.actionListeners.get(panelId);
|
|
315
|
+
listeners?.delete(listener);
|
|
316
|
+
if (listeners?.size === 0 && !this.panels.has(panelId)) {
|
|
317
|
+
this.actionListeners.delete(panelId);
|
|
318
|
+
}
|
|
152
319
|
};
|
|
153
320
|
}
|
|
154
321
|
triggerAction(panelId, path) {
|
|
@@ -167,6 +334,7 @@ var DialStoreClass = class {
|
|
|
167
334
|
this.presets.set(panelId, [...existing, preset]);
|
|
168
335
|
this.activePreset.set(panelId, id);
|
|
169
336
|
this.snapshots.set(panelId, { ...panel.values });
|
|
337
|
+
this.persistPanel(panelId);
|
|
170
338
|
this.notify(panelId);
|
|
171
339
|
return id;
|
|
172
340
|
}
|
|
@@ -179,6 +347,7 @@ var DialStoreClass = class {
|
|
|
179
347
|
panel.values = { ...preset.values };
|
|
180
348
|
this.snapshots.set(panelId, { ...panel.values });
|
|
181
349
|
this.activePreset.set(panelId, presetId);
|
|
350
|
+
this.persistPanel(panelId);
|
|
182
351
|
this.notify(panelId);
|
|
183
352
|
}
|
|
184
353
|
deletePreset(panelId, presetId) {
|
|
@@ -191,6 +360,7 @@ var DialStoreClass = class {
|
|
|
191
360
|
if (panel) {
|
|
192
361
|
this.snapshots.set(panelId, { ...panel.values });
|
|
193
362
|
}
|
|
363
|
+
this.persistPanel(panelId);
|
|
194
364
|
this.notify(panelId);
|
|
195
365
|
}
|
|
196
366
|
getPresets(panelId) {
|
|
@@ -207,6 +377,7 @@ var DialStoreClass = class {
|
|
|
207
377
|
this.snapshots.set(panelId, { ...panel.values });
|
|
208
378
|
}
|
|
209
379
|
this.activePreset.set(panelId, null);
|
|
380
|
+
this.persistPanel(panelId);
|
|
210
381
|
this.notify(panelId);
|
|
211
382
|
}
|
|
212
383
|
resolveShortcutTarget(key, modifier) {
|
|
@@ -237,6 +408,94 @@ var DialStoreClass = class {
|
|
|
237
408
|
}
|
|
238
409
|
return results;
|
|
239
410
|
}
|
|
411
|
+
configurePanelRetention(id, options) {
|
|
412
|
+
if (options.retainOnUnmount) {
|
|
413
|
+
this.retainedPanels.add(id);
|
|
414
|
+
}
|
|
415
|
+
const persistConfig = this.normalizePersistConfig(id, options.persist);
|
|
416
|
+
if (persistConfig) {
|
|
417
|
+
this.persistConfigs.set(id, persistConfig);
|
|
418
|
+
this.retainedPanels.add(id);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
reconcileValues(defaultValues, previousValues, controlsByPath) {
|
|
422
|
+
const nextValues = {};
|
|
423
|
+
for (const [path, defaultValue] of Object.entries(defaultValues)) {
|
|
424
|
+
if (path.endsWith(".__mode")) {
|
|
425
|
+
const transitionPath = path.slice(0, -".__mode".length);
|
|
426
|
+
const transitionControl = controlsByPath.get(transitionPath);
|
|
427
|
+
nextValues[path] = transitionControl?.type === "transition" && previousValues[path] !== void 0 ? previousValues[path] : defaultValue;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
nextValues[path] = this.normalizePreservedValue(
|
|
431
|
+
previousValues[path],
|
|
432
|
+
defaultValue,
|
|
433
|
+
controlsByPath.get(path)
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
return nextValues;
|
|
437
|
+
}
|
|
438
|
+
reconcilePresets(presets, defaultValues, controlsByPath) {
|
|
439
|
+
return presets.map((preset) => ({
|
|
440
|
+
...preset,
|
|
441
|
+
values: this.reconcileValues(defaultValues, preset.values, controlsByPath)
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
normalizePersistConfig(id, persist) {
|
|
445
|
+
if (!persist) return null;
|
|
446
|
+
const options = typeof persist === "object" ? persist : {};
|
|
447
|
+
return {
|
|
448
|
+
key: options.key ?? `dialkit:${id}`,
|
|
449
|
+
storage: options.storage ?? "localStorage",
|
|
450
|
+
presets: options.presets ?? true
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
loadPersistedPanel(id) {
|
|
454
|
+
const config = this.persistConfigs.get(id);
|
|
455
|
+
if (!config) return null;
|
|
456
|
+
const storage = this.getStorage(config.storage);
|
|
457
|
+
if (!storage) return null;
|
|
458
|
+
try {
|
|
459
|
+
const raw = storage.getItem(config.key);
|
|
460
|
+
if (!raw) return null;
|
|
461
|
+
const parsed = JSON.parse(raw);
|
|
462
|
+
if (parsed?.version !== 1 || typeof parsed !== "object") return null;
|
|
463
|
+
return parsed;
|
|
464
|
+
} catch {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
persistPanel(id) {
|
|
469
|
+
const config = this.persistConfigs.get(id);
|
|
470
|
+
if (!config) return;
|
|
471
|
+
const storage = this.getStorage(config.storage);
|
|
472
|
+
if (!storage) return;
|
|
473
|
+
const values = this.snapshots.get(id) ?? this.panels.get(id)?.values;
|
|
474
|
+
if (!values) return;
|
|
475
|
+
const state = {
|
|
476
|
+
version: 1,
|
|
477
|
+
values,
|
|
478
|
+
baseValues: this.baseValues.get(id) ?? values,
|
|
479
|
+
activePresetId: this.activePreset.get(id) ?? null
|
|
480
|
+
};
|
|
481
|
+
if (config.presets) {
|
|
482
|
+
state.presets = this.presets.get(id) ?? [];
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
storage.setItem(config.key, JSON.stringify(state));
|
|
486
|
+
} catch {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
getStorage(kind) {
|
|
490
|
+
if (typeof globalThis === "undefined" || !("window" in globalThis)) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
return kind === "sessionStorage" ? globalThis.window?.sessionStorage ?? null : globalThis.window?.localStorage ?? null;
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
240
499
|
findControlByPath(controls, path) {
|
|
241
500
|
for (const control of controls) {
|
|
242
501
|
if (control.path === path) return control;
|
|
@@ -251,6 +510,9 @@ var DialStoreClass = class {
|
|
|
251
510
|
this.listeners.get(panelId)?.forEach((fn) => fn());
|
|
252
511
|
}
|
|
253
512
|
notifyGlobal() {
|
|
513
|
+
this.panelsSnapshot = Array.from(this.panels.values());
|
|
514
|
+
this.standardPanelsSnapshot = this.panelsSnapshot.filter((panel) => panel.kind !== "timeline");
|
|
515
|
+
this.timelinePanelsSnapshot = this.panelsSnapshot.filter((panel) => panel.kind === "timeline");
|
|
254
516
|
this.globalListeners.forEach((fn) => fn());
|
|
255
517
|
}
|
|
256
518
|
initTransitionModes(config, prefix, values) {
|
|
@@ -366,10 +628,10 @@ var DialStoreClass = class {
|
|
|
366
628
|
return typeof value === "object" && value !== null && "type" in value && value.type === "text";
|
|
367
629
|
}
|
|
368
630
|
isHexColor(value) {
|
|
369
|
-
return
|
|
631
|
+
return isHexColor(value);
|
|
370
632
|
}
|
|
371
633
|
formatLabel(key) {
|
|
372
|
-
return key
|
|
634
|
+
return formatLabel(key);
|
|
373
635
|
}
|
|
374
636
|
inferRange(value) {
|
|
375
637
|
if (value >= 0 && value <= 1) {
|
|
@@ -385,11 +647,7 @@ var DialStoreClass = class {
|
|
|
385
647
|
}
|
|
386
648
|
}
|
|
387
649
|
inferStep(min, max) {
|
|
388
|
-
|
|
389
|
-
if (range <= 1) return 0.01;
|
|
390
|
-
if (range <= 10) return 0.1;
|
|
391
|
-
if (range <= 100) return 1;
|
|
392
|
-
return 10;
|
|
650
|
+
return inferStep(min, max);
|
|
393
651
|
}
|
|
394
652
|
normalizePreservedValue(existingValue, defaultValue, control) {
|
|
395
653
|
if (existingValue === void 0 || !control) {
|
|
@@ -461,26 +719,35 @@ var DialStoreClass = class {
|
|
|
461
719
|
return map;
|
|
462
720
|
}
|
|
463
721
|
};
|
|
464
|
-
var DialStore = new DialStoreClass();
|
|
722
|
+
var DialStore = /* @__PURE__ */ new DialStoreClass();
|
|
465
723
|
|
|
466
724
|
// src/vue/useDialKit.ts
|
|
467
725
|
var dialKitInstance = 0;
|
|
468
726
|
function useDialKit(name, config, options) {
|
|
469
|
-
|
|
727
|
+
return useDialKitController(name, config, options).values;
|
|
728
|
+
}
|
|
729
|
+
function useDialKitController(name, config, options) {
|
|
730
|
+
const hasStableId = options?.id !== void 0;
|
|
731
|
+
const panelId = options?.id ?? `${name}-${++dialKitInstance}`;
|
|
470
732
|
const configRef = shallowRef(config);
|
|
471
733
|
const onActionRef = ref(options?.onAction);
|
|
472
734
|
const shortcutsRef = shallowRef(options?.shortcuts);
|
|
473
|
-
const
|
|
735
|
+
const persistRef = shallowRef(options?.persist);
|
|
736
|
+
const flatValues = ref(DialStore.getValues(panelId));
|
|
474
737
|
const mounted = ref(false);
|
|
475
738
|
const serializedConfig = computed(() => JSON.stringify(config));
|
|
476
739
|
const serializedShortcuts = computed(() => JSON.stringify(options?.shortcuts));
|
|
740
|
+
const serializedPersist = computed(() => JSON.stringify(options?.persist));
|
|
477
741
|
let unsubscribeValues;
|
|
478
742
|
let unsubscribeActions;
|
|
479
743
|
const register = () => {
|
|
480
|
-
DialStore.registerPanel(panelId, name, configRef.value, shortcutsRef.value
|
|
481
|
-
|
|
744
|
+
DialStore.registerPanel(panelId, name, configRef.value, shortcutsRef.value, {
|
|
745
|
+
retainOnUnmount: hasStableId,
|
|
746
|
+
persist: persistRef.value
|
|
747
|
+
});
|
|
748
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
482
749
|
unsubscribeValues = DialStore.subscribe(panelId, () => {
|
|
483
|
-
|
|
750
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
484
751
|
});
|
|
485
752
|
unsubscribeActions = DialStore.subscribeActions(panelId, (action) => {
|
|
486
753
|
onActionRef.value?.(action);
|
|
@@ -492,12 +759,19 @@ function useDialKit(name, config, options) {
|
|
|
492
759
|
watch(() => options?.shortcuts, (next) => {
|
|
493
760
|
shortcutsRef.value = next;
|
|
494
761
|
});
|
|
495
|
-
watch(
|
|
762
|
+
watch(() => options?.persist, (next) => {
|
|
763
|
+
persistRef.value = next;
|
|
764
|
+
});
|
|
765
|
+
watch([serializedConfig, serializedShortcuts, serializedPersist], () => {
|
|
496
766
|
configRef.value = config;
|
|
497
767
|
shortcutsRef.value = options?.shortcuts;
|
|
768
|
+
persistRef.value = options?.persist;
|
|
498
769
|
if (mounted.value) {
|
|
499
|
-
DialStore.updatePanel(panelId, name, configRef.value, shortcutsRef.value
|
|
500
|
-
|
|
770
|
+
DialStore.updatePanel(panelId, name, configRef.value, shortcutsRef.value, {
|
|
771
|
+
retainOnUnmount: hasStableId,
|
|
772
|
+
persist: persistRef.value
|
|
773
|
+
});
|
|
774
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
501
775
|
}
|
|
502
776
|
});
|
|
503
777
|
onMounted(register);
|
|
@@ -509,88 +783,1344 @@ function useDialKit(name, config, options) {
|
|
|
509
783
|
unsubscribeActions?.();
|
|
510
784
|
DialStore.unregisterPanel(panelId);
|
|
511
785
|
});
|
|
512
|
-
|
|
786
|
+
const values = computed(() => resolveDialValues(configRef.value, flatValues.value));
|
|
787
|
+
return {
|
|
788
|
+
values,
|
|
789
|
+
setValue(path, value) {
|
|
790
|
+
DialStore.updateValue(panelId, path, value);
|
|
791
|
+
},
|
|
792
|
+
setValues(nextValues) {
|
|
793
|
+
DialStore.updateValues(panelId, flattenDialValueUpdates(configRef.value, nextValues));
|
|
794
|
+
},
|
|
795
|
+
resetValues() {
|
|
796
|
+
DialStore.resetValues(panelId);
|
|
797
|
+
},
|
|
798
|
+
getValues() {
|
|
799
|
+
return resolveDialValues(configRef.value, DialStore.getValues(panelId));
|
|
800
|
+
}
|
|
801
|
+
};
|
|
513
802
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
803
|
+
|
|
804
|
+
// src/vue/useDialTimeline.ts
|
|
805
|
+
import { computed as computed2, onMounted as onMounted2, onUnmounted as onUnmounted2, shallowRef as shallowRef2, watch as watch2 } from "vue";
|
|
806
|
+
|
|
807
|
+
// src/store/TimelineStore.ts
|
|
808
|
+
function loopSpan(duration, loopStart) {
|
|
809
|
+
if (!Number.isFinite(duration) || duration <= 0) return 0;
|
|
810
|
+
if (!Number.isFinite(loopStart)) loopStart = 0;
|
|
811
|
+
const start = Math.min(Math.max(0, loopStart), duration);
|
|
812
|
+
return duration - start > 0 ? duration - start : duration;
|
|
813
|
+
}
|
|
814
|
+
function foldLoopTime(time, duration, loopStart = 0) {
|
|
815
|
+
if (!Number.isFinite(time) || !Number.isFinite(duration) || duration <= 0) {
|
|
816
|
+
return { time: 0, wraps: 0 };
|
|
817
|
+
}
|
|
818
|
+
if (time < duration) return { time, wraps: 0 };
|
|
819
|
+
const span = loopSpan(duration, loopStart);
|
|
820
|
+
const base = duration - span;
|
|
821
|
+
const over = time - base;
|
|
822
|
+
return { time: base + over % span, wraps: Math.floor(over / span) };
|
|
823
|
+
}
|
|
824
|
+
var TIMELINE_CLIP_COLORS = [
|
|
825
|
+
"#E8E8E8"
|
|
826
|
+
// neutral white — slightly off-white so the pure-white selection ring still reads
|
|
827
|
+
];
|
|
828
|
+
var EMPTY_TRANSPORT = Object.freeze({ time: 0, playing: false, duration: 0, wraps: 0 });
|
|
829
|
+
var TimelineStoreClass = class {
|
|
830
|
+
constructor() {
|
|
831
|
+
this.timelines = /* @__PURE__ */ new Map();
|
|
832
|
+
this.transports = /* @__PURE__ */ new Map();
|
|
833
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
834
|
+
this.globalListeners = /* @__PURE__ */ new Set();
|
|
835
|
+
this.registrationCounts = /* @__PURE__ */ new Map();
|
|
836
|
+
this.listCache = null;
|
|
837
|
+
this.rafId = null;
|
|
838
|
+
this.lastTick = 0;
|
|
839
|
+
this.tick = (now) => {
|
|
840
|
+
const dt = Math.max(0, (now - this.lastTick) / 1e3);
|
|
841
|
+
this.lastTick = now;
|
|
842
|
+
let anyPlaying = false;
|
|
843
|
+
for (const [id, transport] of this.transports) {
|
|
844
|
+
if (!transport.playing) continue;
|
|
845
|
+
const meta = this.timelines.get(id);
|
|
846
|
+
const duration = meta?.duration ?? transport.duration;
|
|
847
|
+
if (!Number.isFinite(duration) || duration <= 0) {
|
|
848
|
+
this.transports.set(id, { time: 0, playing: false, duration: 0, wraps: 0 });
|
|
849
|
+
this.notify(id);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
let time = transport.time + dt;
|
|
853
|
+
let playing = true;
|
|
854
|
+
let wraps = transport.wraps;
|
|
855
|
+
if (time >= duration) {
|
|
856
|
+
if (meta?.loop) {
|
|
857
|
+
const folded = foldLoopTime(time, duration, meta.loopStart);
|
|
858
|
+
time = folded.time;
|
|
859
|
+
wraps += folded.wraps;
|
|
860
|
+
} else {
|
|
861
|
+
time = duration;
|
|
862
|
+
playing = false;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
this.transports.set(id, { time, playing, duration, wraps });
|
|
866
|
+
if (playing) anyPlaying = true;
|
|
867
|
+
this.notify(id);
|
|
868
|
+
}
|
|
869
|
+
this.rafId = anyPlaying ? window.requestAnimationFrame(this.tick) : null;
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
register(meta, options) {
|
|
873
|
+
const existing = this.timelines.get(meta.id);
|
|
874
|
+
if (existing && existing.name !== meta.name) {
|
|
875
|
+
console.warn(
|
|
876
|
+
`[dialkit] Timeline id "${meta.id}" is already registered by "${existing.name}"; "${meta.name}" will share and overwrite that transport.`
|
|
877
|
+
);
|
|
536
878
|
}
|
|
879
|
+
this.registrationCounts.set(meta.id, (this.registrationCounts.get(meta.id) ?? 0) + 1);
|
|
880
|
+
this.applyMeta(meta, options.autoplay);
|
|
537
881
|
}
|
|
538
|
-
|
|
882
|
+
update(meta) {
|
|
883
|
+
if (!this.timelines.has(meta.id)) return;
|
|
884
|
+
this.applyMeta(meta, false);
|
|
885
|
+
}
|
|
886
|
+
unregister(id) {
|
|
887
|
+
const nextCount = (this.registrationCounts.get(id) ?? 1) - 1;
|
|
888
|
+
if (nextCount > 0) {
|
|
889
|
+
this.registrationCounts.set(id, nextCount);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
this.registrationCounts.delete(id);
|
|
893
|
+
this.timelines.delete(id);
|
|
894
|
+
this.transports.delete(id);
|
|
895
|
+
if (this.listeners.get(id)?.size === 0) this.listeners.delete(id);
|
|
896
|
+
this.listCache = null;
|
|
897
|
+
this.notifyGlobal();
|
|
898
|
+
}
|
|
899
|
+
play(id) {
|
|
900
|
+
const transport = this.transports.get(id);
|
|
901
|
+
if (!transport || transport.duration <= 0 || transport.playing) return;
|
|
902
|
+
const restart = transport.time >= transport.duration;
|
|
903
|
+
this.transports.set(id, {
|
|
904
|
+
...transport,
|
|
905
|
+
time: restart ? 0 : transport.time,
|
|
906
|
+
wraps: restart ? 0 : transport.wraps,
|
|
907
|
+
playing: true
|
|
908
|
+
});
|
|
909
|
+
this.notify(id);
|
|
910
|
+
this.ensureLoop();
|
|
911
|
+
}
|
|
912
|
+
pause(id) {
|
|
913
|
+
const transport = this.transports.get(id);
|
|
914
|
+
if (!transport || !transport.playing) return;
|
|
915
|
+
this.transports.set(id, { ...transport, playing: false });
|
|
916
|
+
this.notify(id);
|
|
917
|
+
}
|
|
918
|
+
replay(id) {
|
|
919
|
+
const transport = this.transports.get(id);
|
|
920
|
+
if (!transport || transport.duration <= 0) return;
|
|
921
|
+
this.transports.set(id, { ...transport, time: 0, wraps: 0, playing: true });
|
|
922
|
+
this.notify(id);
|
|
923
|
+
this.ensureLoop();
|
|
924
|
+
}
|
|
925
|
+
seek(id, time) {
|
|
926
|
+
const transport = this.transports.get(id);
|
|
927
|
+
if (!transport || !Number.isFinite(time)) return;
|
|
928
|
+
const clamped = Math.min(transport.duration, Math.max(0, time));
|
|
929
|
+
this.transports.set(id, { ...transport, time: clamped, wraps: 0 });
|
|
930
|
+
this.notify(id);
|
|
931
|
+
}
|
|
932
|
+
getTransport(id) {
|
|
933
|
+
return this.transports.get(id) ?? EMPTY_TRANSPORT;
|
|
934
|
+
}
|
|
935
|
+
getTimeline(id) {
|
|
936
|
+
return this.timelines.get(id);
|
|
937
|
+
}
|
|
938
|
+
getTimelines() {
|
|
939
|
+
if (!this.listCache) {
|
|
940
|
+
this.listCache = Array.from(this.timelines.values());
|
|
941
|
+
}
|
|
942
|
+
return this.listCache;
|
|
943
|
+
}
|
|
944
|
+
subscribe(id, listener) {
|
|
945
|
+
if (!this.listeners.has(id)) {
|
|
946
|
+
this.listeners.set(id, /* @__PURE__ */ new Set());
|
|
947
|
+
}
|
|
948
|
+
this.listeners.get(id).add(listener);
|
|
949
|
+
return () => {
|
|
950
|
+
const listeners = this.listeners.get(id);
|
|
951
|
+
listeners?.delete(listener);
|
|
952
|
+
if (listeners?.size === 0 && !this.timelines.has(id)) {
|
|
953
|
+
this.listeners.delete(id);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
subscribeGlobal(listener) {
|
|
958
|
+
this.globalListeners.add(listener);
|
|
959
|
+
return () => {
|
|
960
|
+
this.globalListeners.delete(listener);
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
applyMeta(meta, autoplay) {
|
|
964
|
+
const duration = Number.isFinite(meta.duration) ? Math.max(0, meta.duration) : 0;
|
|
965
|
+
const loopStart = Number.isFinite(meta.loopStart) ? Math.min(duration, Math.max(0, meta.loopStart)) : 0;
|
|
966
|
+
const safeMeta = { ...meta, duration, loopStart };
|
|
967
|
+
this.timelines.set(meta.id, safeMeta);
|
|
968
|
+
const existing = this.transports.get(meta.id);
|
|
969
|
+
if (existing) {
|
|
970
|
+
this.transports.set(meta.id, {
|
|
971
|
+
time: Math.min(existing.time, duration),
|
|
972
|
+
playing: duration > 0 && existing.playing,
|
|
973
|
+
duration,
|
|
974
|
+
wraps: existing.wraps
|
|
975
|
+
});
|
|
976
|
+
} else {
|
|
977
|
+
const playing = duration > 0 && autoplay;
|
|
978
|
+
this.transports.set(meta.id, { time: 0, playing, duration, wraps: 0 });
|
|
979
|
+
if (playing) this.ensureLoop();
|
|
980
|
+
}
|
|
981
|
+
this.listCache = null;
|
|
982
|
+
this.notify(meta.id);
|
|
983
|
+
this.notifyGlobal();
|
|
984
|
+
}
|
|
985
|
+
ensureLoop() {
|
|
986
|
+
if (this.rafId !== null || typeof window === "undefined") return;
|
|
987
|
+
this.lastTick = performance.now();
|
|
988
|
+
this.rafId = window.requestAnimationFrame(this.tick);
|
|
989
|
+
}
|
|
990
|
+
notify(id) {
|
|
991
|
+
this.listeners.get(id)?.forEach((fn) => fn());
|
|
992
|
+
}
|
|
993
|
+
notifyGlobal() {
|
|
994
|
+
this.globalListeners.forEach((fn) => fn());
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
var TimelineStore = /* @__PURE__ */ new TimelineStoreClass();
|
|
998
|
+
|
|
999
|
+
// src/transition-math.ts
|
|
1000
|
+
function round2(value) {
|
|
1001
|
+
return Math.round(value * 100) / 100;
|
|
539
1002
|
}
|
|
540
|
-
function
|
|
541
|
-
return
|
|
1003
|
+
function clamp(value, min, max) {
|
|
1004
|
+
return Math.min(max, Math.max(min, value));
|
|
542
1005
|
}
|
|
543
|
-
function
|
|
544
|
-
return
|
|
1006
|
+
function isTransitionConfig(value) {
|
|
1007
|
+
return isSpringConfigValue(value) || isEasingConfigValue(value);
|
|
545
1008
|
}
|
|
546
|
-
function
|
|
547
|
-
return
|
|
1009
|
+
function isPhysicsSpring(transition) {
|
|
1010
|
+
return transition.type === "spring" && (transition.stiffness !== void 0 || transition.damping !== void 0 || transition.mass !== void 0);
|
|
548
1011
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
1012
|
+
function springParams(spring) {
|
|
1013
|
+
if (isPhysicsSpring(spring)) {
|
|
1014
|
+
return { stiffness: spring.stiffness ?? 200, damping: spring.damping ?? 25, mass: spring.mass ?? 1 };
|
|
1015
|
+
}
|
|
1016
|
+
const visualDuration = Math.max(0.05, spring.visualDuration ?? 0.3);
|
|
1017
|
+
const bounce = spring.bounce ?? 0.3;
|
|
1018
|
+
const root = 2 * Math.PI / (visualDuration * 1.2);
|
|
1019
|
+
const stiffness = root * root;
|
|
1020
|
+
const damping = 2 * Math.min(1, Math.max(0.05, 1 - bounce)) * Math.sqrt(stiffness);
|
|
1021
|
+
return { stiffness, damping, mass: 1 };
|
|
551
1022
|
}
|
|
552
|
-
function
|
|
553
|
-
|
|
1023
|
+
function springProgress(t, { stiffness, damping, mass }) {
|
|
1024
|
+
if (t <= 0) return 0;
|
|
1025
|
+
const w0 = Math.sqrt(stiffness / mass);
|
|
1026
|
+
const zeta = damping / (2 * Math.sqrt(stiffness * mass));
|
|
1027
|
+
if (zeta < 0.9999) {
|
|
1028
|
+
const wd2 = w0 * Math.sqrt(1 - zeta * zeta);
|
|
1029
|
+
return 1 - Math.exp(-zeta * w0 * t) * (Math.cos(wd2 * t) + zeta * w0 / wd2 * Math.sin(wd2 * t));
|
|
1030
|
+
}
|
|
1031
|
+
if (zeta < 1.0001) {
|
|
1032
|
+
return 1 - Math.exp(-w0 * t) * (1 + w0 * t);
|
|
1033
|
+
}
|
|
1034
|
+
const wd = w0 * Math.sqrt(zeta * zeta - 1);
|
|
1035
|
+
const r1 = -zeta * w0 + wd;
|
|
1036
|
+
const r2 = -zeta * w0 - wd;
|
|
1037
|
+
return 1 + (r2 * Math.exp(r1 * t) - r1 * Math.exp(r2 * t)) / (r1 - r2);
|
|
554
1038
|
}
|
|
555
|
-
function
|
|
556
|
-
|
|
1039
|
+
function springSettleDuration(params) {
|
|
1040
|
+
const w0 = Math.sqrt(params.stiffness / params.mass);
|
|
1041
|
+
const zeta = params.damping / (2 * Math.sqrt(params.stiffness * params.mass));
|
|
1042
|
+
const decay = zeta >= 1 ? zeta * w0 - w0 * Math.sqrt(Math.max(0, zeta * zeta - 1)) : zeta * w0;
|
|
1043
|
+
const duration = Math.log(200) / Math.max(decay, 1e-6);
|
|
1044
|
+
return round2(clamp(duration, 0.05, 10));
|
|
557
1045
|
}
|
|
558
|
-
function
|
|
559
|
-
|
|
1046
|
+
function cubicBezierProgress(p, [x1, y1, x2, y2]) {
|
|
1047
|
+
if (p <= 0) return 0;
|
|
1048
|
+
if (p >= 1) return 1;
|
|
1049
|
+
const sampleX = (t2) => bezierAxis(t2, x1, x2);
|
|
1050
|
+
const sampleY = (t2) => bezierAxis(t2, y1, y2);
|
|
1051
|
+
let t = p;
|
|
1052
|
+
for (let i = 0; i < 8; i++) {
|
|
1053
|
+
const x = sampleX(t) - p;
|
|
1054
|
+
if (Math.abs(x) < 1e-5) return sampleY(t);
|
|
1055
|
+
const dx = bezierAxisDerivative(t, x1, x2);
|
|
1056
|
+
if (Math.abs(dx) < 1e-6) break;
|
|
1057
|
+
t -= x / dx;
|
|
1058
|
+
}
|
|
1059
|
+
let lo = 0;
|
|
1060
|
+
let hi = 1;
|
|
1061
|
+
t = p;
|
|
1062
|
+
while (hi - lo > 1e-5) {
|
|
1063
|
+
if (sampleX(t) < p) lo = t;
|
|
1064
|
+
else hi = t;
|
|
1065
|
+
t = (lo + hi) / 2;
|
|
1066
|
+
}
|
|
1067
|
+
return sampleY(t);
|
|
560
1068
|
}
|
|
561
|
-
function
|
|
562
|
-
|
|
563
|
-
|
|
1069
|
+
function bezierAxis(t, a1, a2) {
|
|
1070
|
+
return (1 - 3 * a2 + 3 * a1) * t * t * t + (3 * a2 - 6 * a1) * t * t + 3 * a1 * t;
|
|
1071
|
+
}
|
|
1072
|
+
function bezierAxisDerivative(t, a1, a2) {
|
|
1073
|
+
return 3 * (1 - 3 * a2 + 3 * a1) * t * t + 2 * (3 * a2 - 6 * a1) * t + 3 * a1;
|
|
1074
|
+
}
|
|
1075
|
+
function resolveClipTransition(raw, clipDuration) {
|
|
1076
|
+
const safeDuration = Math.max(0.05, clipDuration);
|
|
1077
|
+
if (raw.type === "easing") {
|
|
1078
|
+
return {
|
|
1079
|
+
transition: { ...raw, duration: safeDuration },
|
|
1080
|
+
duration: safeDuration,
|
|
1081
|
+
isPhysics: false
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
if (isPhysicsSpring(raw)) {
|
|
1085
|
+
return {
|
|
1086
|
+
transition: raw,
|
|
1087
|
+
duration: springSettleDuration(springParams(raw)),
|
|
1088
|
+
isPhysics: true
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
transition: { type: "spring", bounce: raw.bounce ?? 0.2, visualDuration: safeDuration },
|
|
1093
|
+
duration: safeDuration,
|
|
1094
|
+
isPhysics: false
|
|
1095
|
+
};
|
|
564
1096
|
}
|
|
565
1097
|
|
|
566
|
-
// src/
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
"
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
1098
|
+
// src/timeline-core.ts
|
|
1099
|
+
var CLIP_VALUE_STEP = 0.01;
|
|
1100
|
+
var TIMELINE_MIN_CLIP_DURATION = 0.05;
|
|
1101
|
+
var DEFAULT_STEP_DURATION = 0.3;
|
|
1102
|
+
var DEFAULT_CLIP_TRANSITION = { type: "spring", bounce: 0.2 };
|
|
1103
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["time", "playing", "duration", "play", "pause", "replay", "seek"]);
|
|
1104
|
+
function isClipConfig(value) {
|
|
1105
|
+
return isPlainObject(value) && Number.isFinite(value.at);
|
|
1106
|
+
}
|
|
1107
|
+
function isGroupConfig(value) {
|
|
1108
|
+
if (!isPlainObject(value) || "at" in value) return false;
|
|
1109
|
+
const entries = Object.values(value);
|
|
1110
|
+
return entries.length > 0 && entries.some(isClipConfig);
|
|
1111
|
+
}
|
|
1112
|
+
function isPlainObject(value) {
|
|
1113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1114
|
+
}
|
|
1115
|
+
function nonNegativeFinite(value, fallback = 0) {
|
|
1116
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : fallback;
|
|
1117
|
+
}
|
|
1118
|
+
function animatedDuration(value, fallback = DEFAULT_STEP_DURATION) {
|
|
1119
|
+
return Math.max(TIMELINE_MIN_CLIP_DURATION, nonNegativeFinite(value, fallback));
|
|
1120
|
+
}
|
|
1121
|
+
function transitionDefaultDuration(transition) {
|
|
1122
|
+
if (transition.type === "easing") return animatedDuration(transition.duration);
|
|
1123
|
+
if (isPhysicsSpring(transition)) {
|
|
1124
|
+
return animatedDuration(springSettleDuration(springParams(transition)));
|
|
1125
|
+
}
|
|
1126
|
+
if (transition.visualDuration !== void 0) return animatedDuration(transition.visualDuration);
|
|
1127
|
+
return animatedDuration(springSettleDuration(springParams(transition)));
|
|
1128
|
+
}
|
|
1129
|
+
function defaultStepDuration(step, inheritedTransition) {
|
|
1130
|
+
const curve = step.transition ?? inheritedTransition;
|
|
1131
|
+
if (curve && isPhysicsSpring(curve)) return transitionDefaultDuration(curve);
|
|
1132
|
+
if (step.duration !== void 0) return animatedDuration(step.duration);
|
|
1133
|
+
if (step.transition) return transitionDefaultDuration(step.transition);
|
|
1134
|
+
return DEFAULT_STEP_DURATION;
|
|
1135
|
+
}
|
|
1136
|
+
function defaultTrackDuration(track, inheritedTransition) {
|
|
1137
|
+
const curve = track.transition ?? inheritedTransition;
|
|
1138
|
+
if (track.steps?.length) {
|
|
1139
|
+
return track.steps.reduce((sum, step) => sum + defaultStepDuration(step, curve), 0);
|
|
1140
|
+
}
|
|
1141
|
+
if (curve && isPhysicsSpring(curve)) return transitionDefaultDuration(curve);
|
|
1142
|
+
if (track.duration !== void 0) return animatedDuration(track.duration);
|
|
1143
|
+
if (track.transition) return transitionDefaultDuration(track.transition);
|
|
1144
|
+
return DEFAULT_STEP_DURATION;
|
|
1145
|
+
}
|
|
1146
|
+
function defaultClipDuration(clip) {
|
|
1147
|
+
const defaultCurve = isTransitionConfig(clip.transition) ? clip.transition : DEFAULT_CLIP_TRANSITION;
|
|
1148
|
+
if (clip.props) {
|
|
1149
|
+
return Object.values(clip.props).reduce(
|
|
1150
|
+
(max, track) => Math.max(
|
|
1151
|
+
max,
|
|
1152
|
+
nonNegativeFinite(track.delay) + defaultTrackDuration(track, defaultCurve)
|
|
1153
|
+
),
|
|
1154
|
+
0
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
if (clip.steps?.length) {
|
|
1158
|
+
return clip.steps.reduce((sum, step) => sum + defaultStepDuration(step, defaultCurve), 0);
|
|
1159
|
+
}
|
|
1160
|
+
const animating = Boolean(clip.transition || clip.from || clip.to);
|
|
1161
|
+
if (!animating) return nonNegativeFinite(clip.duration);
|
|
1162
|
+
if (isPhysicsSpring(defaultCurve)) return transitionDefaultDuration(defaultCurve);
|
|
1163
|
+
if (clip.duration !== void 0) return animatedDuration(clip.duration);
|
|
1164
|
+
if (isTransitionConfig(clip.transition)) return transitionDefaultDuration(clip.transition);
|
|
1165
|
+
return clip.from || clip.to ? transitionDefaultDuration(DEFAULT_CLIP_TRANSITION) : 0;
|
|
1166
|
+
}
|
|
1167
|
+
function normalizeLoopMode(value) {
|
|
1168
|
+
if (value === true || value === "mirror" || value === "repeat") return "repeat";
|
|
1169
|
+
return "off";
|
|
1170
|
+
}
|
|
1171
|
+
function normalizeStoredTransition(transition, clipDuration) {
|
|
1172
|
+
if (transition.type === "easing") {
|
|
1173
|
+
return { ...transition, duration: clipDuration };
|
|
1174
|
+
}
|
|
1175
|
+
if (isPhysicsSpring(transition)) {
|
|
1176
|
+
return transition;
|
|
1177
|
+
}
|
|
1178
|
+
return { type: "spring", bounce: transition.bounce ?? 0.2 };
|
|
1179
|
+
}
|
|
1180
|
+
function collectClipEntries(config) {
|
|
1181
|
+
const entries = [];
|
|
1182
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1183
|
+
if (key === "duration") continue;
|
|
1184
|
+
if (RESERVED_KEYS.has(key)) {
|
|
1185
|
+
console.warn(`[dialkit] Timeline key "${key}" collides with a reserved key and was skipped.`);
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (isClipConfig(value)) {
|
|
1189
|
+
entries.push({ path: key, childKey: key, clip: value });
|
|
1190
|
+
} else if (isGroupConfig(value)) {
|
|
1191
|
+
for (const [childKey, childClip] of Object.entries(value)) {
|
|
1192
|
+
if (isClipConfig(childClip)) {
|
|
1193
|
+
entries.push({ path: `${key}.${childKey}`, childKey, group: key, clip: childClip });
|
|
1194
|
+
} else {
|
|
1195
|
+
console.warn(
|
|
1196
|
+
`[dialkit] Timeline clip "${key}.${childKey}" is missing a numeric "at" and was skipped.`
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
console.warn(
|
|
1202
|
+
`[dialkit] Timeline entry "${key}" is neither a clip (needs a numeric "at") nor a group of clips and was skipped.`
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return entries;
|
|
1207
|
+
}
|
|
1208
|
+
function definedValues(values) {
|
|
1209
|
+
if (!values) return void 0;
|
|
1210
|
+
const result = {};
|
|
1211
|
+
for (const [key, value] of Object.entries(values)) {
|
|
1212
|
+
if (value !== void 0) result[key] = value;
|
|
1213
|
+
}
|
|
1214
|
+
return result;
|
|
1215
|
+
}
|
|
1216
|
+
function setDialPath(dialConfig, path, value) {
|
|
1217
|
+
const segments = path.split(".");
|
|
1218
|
+
let node = dialConfig;
|
|
1219
|
+
for (const segment of segments.slice(0, -1)) {
|
|
1220
|
+
node = node[segment] ?? (node[segment] = {});
|
|
1221
|
+
}
|
|
1222
|
+
node[segments[segments.length - 1]] = value;
|
|
1223
|
+
}
|
|
1224
|
+
function parseTimelineConfig(config) {
|
|
1225
|
+
const entries = collectClipEntries(config);
|
|
1226
|
+
let maxEnd = 0;
|
|
1227
|
+
for (const { clip } of entries) {
|
|
1228
|
+
maxEnd = Math.max(maxEnd, nonNegativeFinite(clip.at) + defaultClipDuration(clip));
|
|
1229
|
+
}
|
|
1230
|
+
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;
|
|
1231
|
+
const dialConfig = {};
|
|
1232
|
+
const clips = [];
|
|
1233
|
+
entries.forEach(({ path, childKey, group, clip }, index) => {
|
|
1234
|
+
const raw = clip;
|
|
1235
|
+
if (raw.props && (raw.steps?.length || raw.from || raw.to)) {
|
|
1236
|
+
console.warn(
|
|
1237
|
+
`[dialkit] Timeline clip "${path}": "props" is mutually exclusive with from/to/steps \u2014 using "props".`
|
|
1238
|
+
);
|
|
1239
|
+
} else if (raw.steps?.length && raw.to) {
|
|
1240
|
+
console.warn(
|
|
1241
|
+
`[dialkit] Timeline clip "${path}": "to" is ignored when "steps" is present \u2014 each leg's "to" defines its targets.`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
const hasSteps = Boolean(clip.steps?.length) && !clip.props;
|
|
1245
|
+
const hasProps = Boolean(clip.props);
|
|
1246
|
+
const single = isTransitionConfig(clip.transition) ? clip.transition : void 0;
|
|
1247
|
+
const total = defaultClipDuration(clip);
|
|
1248
|
+
const defaultCurve = single ?? DEFAULT_CLIP_TRANSITION;
|
|
1249
|
+
const clipAt = nonNegativeFinite(clip.at);
|
|
1250
|
+
const clipDial = {
|
|
1251
|
+
at: [clipAt, 0, duration, CLIP_VALUE_STEP]
|
|
1252
|
+
};
|
|
1253
|
+
if (!hasSteps && !hasProps) {
|
|
1254
|
+
clipDial.duration = [total, 0, duration, CLIP_VALUE_STEP];
|
|
1255
|
+
}
|
|
1256
|
+
if (!hasSteps && !hasProps && (clip.transition || clip.from || clip.to)) {
|
|
1257
|
+
clipDial.transition = normalizeStoredTransition(defaultCurve, total);
|
|
1258
|
+
}
|
|
1259
|
+
let tracks;
|
|
1260
|
+
if (clip.props) {
|
|
1261
|
+
tracks = [];
|
|
1262
|
+
for (const [prop, track] of Object.entries(clip.props)) {
|
|
1263
|
+
if (TRACK_RESERVED.has(prop) || /^step\d+$/.test(prop)) {
|
|
1264
|
+
console.warn(`[dialkit] Timeline property "${prop}" collides with a clip field and was skipped.`);
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
const trackDuration = defaultTrackDuration(track, defaultCurve);
|
|
1268
|
+
const trackCurve = track.transition ?? defaultCurve;
|
|
1269
|
+
const hasTrackSteps = Boolean(track.steps?.length);
|
|
1270
|
+
const trackDial = {
|
|
1271
|
+
delay: [nonNegativeFinite(track.delay), 0, duration, CLIP_VALUE_STEP]
|
|
1272
|
+
};
|
|
1273
|
+
if (!hasTrackSteps) {
|
|
1274
|
+
trackDial.duration = [trackDuration, 0, duration, CLIP_VALUE_STEP];
|
|
1275
|
+
trackDial.transition = normalizeStoredTransition(trackCurve, trackDuration);
|
|
1276
|
+
}
|
|
1277
|
+
const fromValue = track.from ?? (hasTrackSteps ? void 0 : track.to);
|
|
1278
|
+
if (hasTrackSteps && fromValue === void 0) {
|
|
1279
|
+
console.warn(
|
|
1280
|
+
`[dialkit] Timeline clip "${path}": track "${prop}" has steps but no "from" \u2014 declare its starting value.`
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
if (fromValue !== void 0) {
|
|
1284
|
+
trackDial.from = scalarDial(prop, fromValue, hasTrackSteps ? track.steps[0]?.to : track.to);
|
|
1285
|
+
}
|
|
1286
|
+
if (!hasTrackSteps && track.to !== void 0) {
|
|
1287
|
+
trackDial.to = scalarDial(prop, track.to, fromValue);
|
|
1288
|
+
}
|
|
1289
|
+
let trackStepKeys;
|
|
1290
|
+
if (hasTrackSteps) {
|
|
1291
|
+
trackStepKeys = [];
|
|
1292
|
+
let previous = fromValue;
|
|
1293
|
+
track.steps.forEach((step, stepIndex) => {
|
|
1294
|
+
const stepKey = `step${stepIndex + 1}`;
|
|
1295
|
+
trackStepKeys.push(stepKey);
|
|
1296
|
+
const stepDuration = defaultStepDuration(step, trackCurve);
|
|
1297
|
+
const stepDial = {
|
|
1298
|
+
duration: [stepDuration, 0, duration, CLIP_VALUE_STEP],
|
|
1299
|
+
transition: normalizeStoredTransition(step.transition ?? trackCurve, stepDuration)
|
|
1300
|
+
};
|
|
1301
|
+
if (step.to !== void 0) {
|
|
1302
|
+
stepDial.to = scalarDial(prop, step.to, previous);
|
|
1303
|
+
previous = step.to;
|
|
1304
|
+
}
|
|
1305
|
+
trackDial[stepKey] = stepDial;
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
clipDial[prop] = trackDial;
|
|
1309
|
+
tracks.push({ prop, stepKeys: trackStepKeys });
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (clip.from && !hasProps) {
|
|
1313
|
+
clipDial.from = withFromToRanges(
|
|
1314
|
+
clip.from,
|
|
1315
|
+
hasSteps ? definedValues(clip.steps[0]?.to) : clip.to
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
if (!hasSteps && !hasProps && clip.to) {
|
|
1319
|
+
clipDial.to = withFromToRanges(clip.to, clip.from);
|
|
1320
|
+
}
|
|
1321
|
+
let stepKeys;
|
|
1322
|
+
if (hasSteps) {
|
|
1323
|
+
stepKeys = [];
|
|
1324
|
+
let running = clip.from;
|
|
1325
|
+
clip.steps.forEach((step, stepIndex) => {
|
|
1326
|
+
const stepKey = `step${stepIndex + 1}`;
|
|
1327
|
+
stepKeys.push(stepKey);
|
|
1328
|
+
const stepDuration = defaultStepDuration(step, defaultCurve);
|
|
1329
|
+
const stepDial = {
|
|
1330
|
+
duration: [stepDuration, 0, duration, CLIP_VALUE_STEP],
|
|
1331
|
+
transition: normalizeStoredTransition(step.transition ?? defaultCurve, stepDuration)
|
|
1332
|
+
};
|
|
1333
|
+
const stepTo = definedValues(step.to);
|
|
1334
|
+
if (stepTo) {
|
|
1335
|
+
for (const prop of Object.keys(stepTo)) {
|
|
1336
|
+
if (!running || !(prop in running)) {
|
|
1337
|
+
console.warn(
|
|
1338
|
+
`[dialkit] Timeline clip "${path}": property "${prop}" first animates in step ${stepIndex + 1} with no starting value \u2014 declare it in "from".`
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
stepDial.to = withFromToRanges(stepTo, running);
|
|
1343
|
+
}
|
|
1344
|
+
clipDial[stepKey] = stepDial;
|
|
1345
|
+
running = { ...running ?? {}, ...stepTo ?? {} };
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
setDialPath(dialConfig, path, clipDial);
|
|
1349
|
+
clips.push({
|
|
1350
|
+
key: path,
|
|
1351
|
+
label: formatLabel(childKey),
|
|
1352
|
+
color: TIMELINE_CLIP_COLORS[index % TIMELINE_CLIP_COLORS.length],
|
|
1353
|
+
loop: normalizeLoopMode(clip.loop),
|
|
1354
|
+
group,
|
|
1355
|
+
stepKeys,
|
|
1356
|
+
tracks
|
|
1357
|
+
});
|
|
1358
|
+
});
|
|
1359
|
+
return { duration, dialConfig, clips };
|
|
1360
|
+
}
|
|
1361
|
+
var TRACK_RESERVED = /* @__PURE__ */ new Set(["at", "duration", "loop", "from", "to", "transition", "delay"]);
|
|
1362
|
+
function scalarDial(prop, value, counterpart) {
|
|
1363
|
+
const record = withFromToRanges(
|
|
1364
|
+
{ [prop]: value },
|
|
1365
|
+
counterpart === void 0 ? void 0 : { [prop]: counterpart }
|
|
1366
|
+
);
|
|
1367
|
+
return record[prop];
|
|
1368
|
+
}
|
|
1369
|
+
var FROM_TO_RANGE_PRESETS = [
|
|
1370
|
+
[/^(x|y|z|tx|ty|offsetx|offsety|translatex|translatey)$/i, { min: -100, max: 100, step: 1 }],
|
|
1371
|
+
[/rotat|angle|skew/i, { min: -180, max: 180, step: 1 }],
|
|
1372
|
+
[/^scale/i, { min: 0, max: 2, step: 0.01 }],
|
|
1373
|
+
[/opacity|alpha/i, { min: 0, max: 1, step: 0.01 }],
|
|
1374
|
+
[/blur|radius|spread/i, { min: 0, max: 100, step: 1 }]
|
|
1375
|
+
];
|
|
1376
|
+
function inferFromToRange(key, value, counterpart) {
|
|
1377
|
+
const lo = Math.min(value, counterpart ?? value);
|
|
1378
|
+
const hi = Math.max(value, counterpart ?? value);
|
|
1379
|
+
const preset = FROM_TO_RANGE_PRESETS.find(([pattern]) => pattern.test(key))?.[1];
|
|
1380
|
+
if (preset) {
|
|
1381
|
+
return [value, Math.min(preset.min, lo), Math.max(preset.max, hi), preset.step];
|
|
1382
|
+
}
|
|
1383
|
+
if (lo >= 0 && hi <= 1) {
|
|
1384
|
+
return [value, 0, 1, 0.01];
|
|
1385
|
+
}
|
|
1386
|
+
const extent = Math.max(Math.abs(lo), Math.abs(hi), 1);
|
|
1387
|
+
const min = lo < 0 ? -extent * 2 : 0;
|
|
1388
|
+
const max = Math.max(extent * 2, hi);
|
|
1389
|
+
return [value, min, max, inferStep(min, max)];
|
|
1390
|
+
}
|
|
1391
|
+
function withFromToRanges(config, counterpart) {
|
|
1392
|
+
const result = {};
|
|
1393
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1394
|
+
const other = counterpart?.[key];
|
|
1395
|
+
if (typeof value === "number") {
|
|
1396
|
+
result[key] = inferFromToRange(key, value, typeof other === "number" ? other : void 0);
|
|
1397
|
+
} else if (isPlainObject(value) && !("type" in value)) {
|
|
1398
|
+
result[key] = withFromToRanges(
|
|
1399
|
+
value,
|
|
1400
|
+
isPlainObject(other) && !("type" in other) ? other : void 0
|
|
1401
|
+
);
|
|
1402
|
+
} else {
|
|
1403
|
+
result[key] = value;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
return result;
|
|
1407
|
+
}
|
|
1408
|
+
function curveStatic(transition, duration) {
|
|
1409
|
+
if (!transition) return { duration };
|
|
1410
|
+
if (transition.type === "easing") return { duration, ease: transition.ease };
|
|
1411
|
+
const spring = springParams(transition);
|
|
1412
|
+
return { duration, spring, settle: springSettleDuration(spring) };
|
|
1413
|
+
}
|
|
1414
|
+
function sampleCurve(curve, elapsed) {
|
|
1415
|
+
if (elapsed <= 0) return 0;
|
|
1416
|
+
if (curve.spring) {
|
|
1417
|
+
if (curve.settle !== void 0 && elapsed >= curve.settle) return 1;
|
|
1418
|
+
return springProgress(elapsed, curve.spring);
|
|
1419
|
+
}
|
|
1420
|
+
if (curve.ease) {
|
|
1421
|
+
return cubicBezierProgress(clamp(curve.duration > 0 ? elapsed / curve.duration : 1, 0, 1), curve.ease);
|
|
1422
|
+
}
|
|
1423
|
+
return curve.duration > 0 ? Math.min(1, elapsed / curve.duration) : 1;
|
|
1424
|
+
}
|
|
1425
|
+
function resolvedAtPath(resolved, path) {
|
|
1426
|
+
let node = resolved;
|
|
1427
|
+
for (const segment of path.split(".")) {
|
|
1428
|
+
node = isPlainObject(node) ? node[segment] : void 0;
|
|
1429
|
+
}
|
|
1430
|
+
return isPlainObject(node) ? node : {};
|
|
1431
|
+
}
|
|
1432
|
+
function computeStaticClips(parsed, flatValues) {
|
|
1433
|
+
const resolved = resolveDialValues(parsed.dialConfig, flatValues);
|
|
1434
|
+
return parsed.clips.map(
|
|
1435
|
+
(clip) => buildClipStatic(resolvedAtPath(resolved, clip.key), clip, parsed.duration)
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
function computeStaticTimeline(parsed, flatValues) {
|
|
1439
|
+
let clips = computeStaticClips(parsed, flatValues);
|
|
1440
|
+
const maxEnd = clips.reduce(
|
|
1441
|
+
(end, clip) => Math.max(end, clip.at + clip.duration),
|
|
1442
|
+
parsed.duration
|
|
1443
|
+
);
|
|
1444
|
+
const duration = maxEnd > parsed.duration ? Math.ceil(maxEnd * 100 - 1e-4) / 100 : parsed.duration;
|
|
1445
|
+
if (duration !== parsed.duration) {
|
|
1446
|
+
clips = clips.map(
|
|
1447
|
+
(clip) => clip.loop === "repeat" ? { ...clip, end: duration } : clip
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
return { duration, clips };
|
|
1451
|
+
}
|
|
1452
|
+
function computeClipStaticFromValues(values, clip, timelineDuration) {
|
|
1453
|
+
return buildClipStatic(unflattenClipValues(values, clip.key), clip, timelineDuration);
|
|
1454
|
+
}
|
|
1455
|
+
function buildClipStatic(clipResolved, clip, timelineDuration) {
|
|
1456
|
+
{
|
|
1457
|
+
const at = typeof clipResolved.at === "number" ? clipResolved.at : 0;
|
|
1458
|
+
const from = isPlainObject(clipResolved.from) ? clipResolved.from : void 0;
|
|
1459
|
+
const single = isTransitionConfig(clipResolved.transition) ? clipResolved.transition : void 0;
|
|
1460
|
+
const staticClip = {
|
|
1461
|
+
key: clip.key,
|
|
1462
|
+
childKey: clip.group ? clip.key.slice(clip.group.length + 1) : clip.key,
|
|
1463
|
+
group: clip.group,
|
|
1464
|
+
at,
|
|
1465
|
+
duration: 0,
|
|
1466
|
+
loop: "off",
|
|
1467
|
+
end: 0,
|
|
1468
|
+
isPhysics: false,
|
|
1469
|
+
from,
|
|
1470
|
+
tracks: [],
|
|
1471
|
+
explicitSteps: Boolean(clip.stepKeys?.length)
|
|
1472
|
+
};
|
|
1473
|
+
if (clip.tracks?.length) {
|
|
1474
|
+
const tracks = clip.tracks.map(({ prop, stepKeys }) => {
|
|
1475
|
+
const trackResolved = isPlainObject(clipResolved[prop]) ? clipResolved[prop] : {};
|
|
1476
|
+
const delay = typeof trackResolved.delay === "number" ? trackResolved.delay : 0;
|
|
1477
|
+
const fromValue = trackResolved.from;
|
|
1478
|
+
let steps;
|
|
1479
|
+
let trackDuration = 0;
|
|
1480
|
+
if (stepKeys?.length) {
|
|
1481
|
+
let running = fromValue;
|
|
1482
|
+
steps = stepKeys.map((stepKey) => {
|
|
1483
|
+
const stepResolved = isPlainObject(trackResolved[stepKey]) ? trackResolved[stepKey] : {};
|
|
1484
|
+
const storedDuration = typeof stepResolved.duration === "number" ? stepResolved.duration : 0;
|
|
1485
|
+
const raw = isTransitionConfig(stepResolved.transition) ? stepResolved.transition : void 0;
|
|
1486
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
1487
|
+
const toValue = stepResolved.to;
|
|
1488
|
+
const step = {
|
|
1489
|
+
key: stepKey,
|
|
1490
|
+
offset: trackDuration,
|
|
1491
|
+
duration: effective.duration,
|
|
1492
|
+
isPhysics: effective.isPhysics,
|
|
1493
|
+
start: running === void 0 ? {} : { [prop]: running },
|
|
1494
|
+
to: toValue === void 0 ? {} : { [prop]: toValue },
|
|
1495
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
1496
|
+
};
|
|
1497
|
+
if (toValue !== void 0) running = toValue;
|
|
1498
|
+
trackDuration += effective.duration;
|
|
1499
|
+
return step;
|
|
1500
|
+
});
|
|
1501
|
+
} else {
|
|
1502
|
+
const storedDuration = typeof trackResolved.duration === "number" ? trackResolved.duration : 0;
|
|
1503
|
+
const raw = isTransitionConfig(trackResolved.transition) ? trackResolved.transition : void 0;
|
|
1504
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
1505
|
+
const toValue = trackResolved.to;
|
|
1506
|
+
trackDuration = effective.duration;
|
|
1507
|
+
steps = [
|
|
1508
|
+
{
|
|
1509
|
+
key: null,
|
|
1510
|
+
offset: 0,
|
|
1511
|
+
duration: effective.duration,
|
|
1512
|
+
isPhysics: effective.isPhysics,
|
|
1513
|
+
start: fromValue === void 0 ? {} : { [prop]: fromValue },
|
|
1514
|
+
to: toValue === void 0 ? {} : { [prop]: toValue },
|
|
1515
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
1516
|
+
}
|
|
1517
|
+
];
|
|
1518
|
+
}
|
|
1519
|
+
return { prop, delay, duration: trackDuration, steps };
|
|
1520
|
+
});
|
|
1521
|
+
staticClip.tracks = tracks;
|
|
1522
|
+
staticClip.props = tracks.map((track) => track.prop);
|
|
1523
|
+
staticClip.duration = tracks.reduce((max, track) => Math.max(max, track.delay + track.duration), 0);
|
|
1524
|
+
staticClip.from = Object.fromEntries(
|
|
1525
|
+
tracks.map((track) => [track.prop, track.steps[0].start[track.prop]])
|
|
1526
|
+
);
|
|
1527
|
+
staticClip.to = Object.fromEntries(
|
|
1528
|
+
tracks.map((track) => {
|
|
1529
|
+
const last = track.steps[track.steps.length - 1];
|
|
1530
|
+
return [track.prop, last.to[track.prop] ?? last.start[track.prop]];
|
|
1531
|
+
})
|
|
1532
|
+
);
|
|
1533
|
+
staticClip.loop = staticClip.duration > 0 ? clip.loop : "off";
|
|
1534
|
+
staticClip.end = staticClip.loop === "off" ? staticClip.at + staticClip.duration : timelineDuration;
|
|
1535
|
+
return staticClip;
|
|
1536
|
+
}
|
|
1537
|
+
if (clip.stepKeys?.length) {
|
|
1538
|
+
let running = { ...from ?? {} };
|
|
1539
|
+
let offset = 0;
|
|
1540
|
+
const steps = clip.stepKeys.map((stepKey) => {
|
|
1541
|
+
const stepResolved = isPlainObject(clipResolved[stepKey]) ? clipResolved[stepKey] : {};
|
|
1542
|
+
const storedDuration = typeof stepResolved.duration === "number" ? stepResolved.duration : 0;
|
|
1543
|
+
const raw = isTransitionConfig(stepResolved.transition) ? stepResolved.transition : void 0;
|
|
1544
|
+
const effective = raw ? resolveClipTransition(raw, storedDuration) : { transition: void 0, duration: storedDuration, isPhysics: false };
|
|
1545
|
+
const to = isPlainObject(stepResolved.to) ? stepResolved.to : {};
|
|
1546
|
+
const step = {
|
|
1547
|
+
key: stepKey,
|
|
1548
|
+
offset,
|
|
1549
|
+
duration: effective.duration,
|
|
1550
|
+
isPhysics: effective.isPhysics,
|
|
1551
|
+
start: running,
|
|
1552
|
+
to,
|
|
1553
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
1554
|
+
};
|
|
1555
|
+
running = { ...running, ...to };
|
|
1556
|
+
offset += effective.duration;
|
|
1557
|
+
return step;
|
|
1558
|
+
});
|
|
1559
|
+
staticClip.tracks = [{ delay: 0, duration: offset, steps }];
|
|
1560
|
+
staticClip.duration = offset;
|
|
1561
|
+
staticClip.to = running;
|
|
1562
|
+
} else {
|
|
1563
|
+
const storedDuration = typeof clipResolved.duration === "number" ? clipResolved.duration : 0;
|
|
1564
|
+
const to = isPlainObject(clipResolved.to) ? clipResolved.to : void 0;
|
|
1565
|
+
if (single) {
|
|
1566
|
+
const effective = resolveClipTransition(single, storedDuration);
|
|
1567
|
+
staticClip.duration = effective.duration;
|
|
1568
|
+
staticClip.isPhysics = effective.isPhysics;
|
|
1569
|
+
staticClip.transition = effective.transition;
|
|
1570
|
+
staticClip.css = transitionToCss(effective.transition);
|
|
1571
|
+
staticClip.to = to;
|
|
1572
|
+
if (from && to) {
|
|
1573
|
+
staticClip.tracks = [
|
|
1574
|
+
{
|
|
1575
|
+
delay: 0,
|
|
1576
|
+
duration: effective.duration,
|
|
1577
|
+
steps: [
|
|
1578
|
+
{
|
|
1579
|
+
key: null,
|
|
1580
|
+
offset: 0,
|
|
1581
|
+
duration: effective.duration,
|
|
1582
|
+
isPhysics: effective.isPhysics,
|
|
1583
|
+
start: from,
|
|
1584
|
+
to,
|
|
1585
|
+
curve: curveStatic(effective.transition, effective.duration)
|
|
1586
|
+
}
|
|
1587
|
+
]
|
|
1588
|
+
}
|
|
1589
|
+
];
|
|
1590
|
+
}
|
|
1591
|
+
} else {
|
|
1592
|
+
staticClip.duration = storedDuration;
|
|
1593
|
+
staticClip.to = to;
|
|
1594
|
+
if (from && to) {
|
|
1595
|
+
const base = resolveClipTransition(DEFAULT_CLIP_TRANSITION, storedDuration);
|
|
1596
|
+
staticClip.duration = base.duration;
|
|
1597
|
+
staticClip.tracks = [
|
|
1598
|
+
{
|
|
1599
|
+
delay: 0,
|
|
1600
|
+
duration: base.duration,
|
|
1601
|
+
steps: [
|
|
1602
|
+
{
|
|
1603
|
+
key: null,
|
|
1604
|
+
offset: 0,
|
|
1605
|
+
duration: base.duration,
|
|
1606
|
+
isPhysics: false,
|
|
1607
|
+
start: from,
|
|
1608
|
+
to,
|
|
1609
|
+
curve: curveStatic(base.transition, base.duration)
|
|
1610
|
+
}
|
|
1611
|
+
]
|
|
1612
|
+
}
|
|
1613
|
+
];
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
if (staticClip.tracks.length) {
|
|
1618
|
+
const props = new Set(Object.keys(from ?? {}));
|
|
1619
|
+
for (const track of staticClip.tracks) {
|
|
1620
|
+
for (const step of track.steps) {
|
|
1621
|
+
for (const prop of Object.keys(step.to)) props.add(prop);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
staticClip.props = Array.from(props);
|
|
1625
|
+
}
|
|
1626
|
+
staticClip.loop = staticClip.duration > 0 ? clip.loop : "off";
|
|
1627
|
+
staticClip.end = staticClip.loop === "off" ? staticClip.at + staticClip.duration : timelineDuration;
|
|
1628
|
+
return staticClip;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
function stepAtPosition(steps, pos) {
|
|
1632
|
+
for (const step of steps) {
|
|
1633
|
+
if (pos < step.offset + step.duration) return step;
|
|
1634
|
+
}
|
|
1635
|
+
return steps[steps.length - 1];
|
|
1636
|
+
}
|
|
1637
|
+
function evalPropAtPos(steps, prop, pos) {
|
|
1638
|
+
const step = stepAtPosition(steps, pos);
|
|
1639
|
+
const within = Math.max(0, pos - step.offset);
|
|
1640
|
+
if (prop in step.to) {
|
|
1641
|
+
const eased = sampleCurve(step.curve, within);
|
|
1642
|
+
return interpolateResolved(step.start[prop], step.to[prop], eased);
|
|
1643
|
+
}
|
|
1644
|
+
return step.start[prop];
|
|
1645
|
+
}
|
|
1646
|
+
function computeClipState(clip, time, cycleTime = time) {
|
|
1647
|
+
const total = clip.duration;
|
|
1648
|
+
const looping = clip.loop === "repeat" && total > 0;
|
|
1649
|
+
const started = time >= clip.at || looping && cycleTime > time;
|
|
1650
|
+
const done = time >= clip.end;
|
|
1651
|
+
const elapsed = time - clip.at;
|
|
1652
|
+
const phaseElapsed = looping ? cycleTime - clip.at : elapsed;
|
|
1653
|
+
const fold = (e) => looping ? e % total : e;
|
|
1654
|
+
const basePos = started ? fold(Math.max(0, phaseElapsed)) : 0;
|
|
1655
|
+
const progress = total > 0 ? clamp(basePos / total, 0, 1) : started ? 1 : 0;
|
|
1656
|
+
let current;
|
|
1657
|
+
let stepIndex = 0;
|
|
1658
|
+
if (clip.tracks.length && clip.props?.length) {
|
|
1659
|
+
current = {};
|
|
1660
|
+
for (const track of clip.tracks) {
|
|
1661
|
+
const props = track.prop !== void 0 ? [track.prop] : clip.props;
|
|
1662
|
+
for (const prop of props) {
|
|
1663
|
+
const startValue = track.steps[0]?.start[prop];
|
|
1664
|
+
if (!started) {
|
|
1665
|
+
if (startValue !== void 0) current[prop] = startValue;
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
const phase = phaseElapsed - track.delay;
|
|
1669
|
+
if (phase <= 0) {
|
|
1670
|
+
if (startValue !== void 0) current[prop] = startValue;
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const pos = looping && track.duration > 0 ? phase % track.duration : phase;
|
|
1674
|
+
const value = evalPropAtPos(track.steps, prop, pos);
|
|
1675
|
+
if (value !== void 0) current[prop] = value;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
const shared = clip.tracks[0];
|
|
1679
|
+
if (started && clip.explicitSteps && shared.prop === void 0) {
|
|
1680
|
+
stepIndex = shared.steps.indexOf(stepAtPosition(shared.steps, basePos));
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
return {
|
|
1684
|
+
at: clip.at,
|
|
1685
|
+
duration: clip.duration,
|
|
1686
|
+
loop: clip.loop,
|
|
1687
|
+
started,
|
|
1688
|
+
active: started && !done,
|
|
1689
|
+
done,
|
|
1690
|
+
progress,
|
|
1691
|
+
step: clip.explicitSteps ? stepIndex : void 0,
|
|
1692
|
+
from: clip.from,
|
|
1693
|
+
to: clip.to,
|
|
1694
|
+
animate: started ? clip.to : clip.from,
|
|
1695
|
+
transition: clip.transition,
|
|
1696
|
+
css: clip.css,
|
|
1697
|
+
current
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
function interpolateResolved(from, to, p) {
|
|
1701
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
1702
|
+
return from + (to - from) * p;
|
|
1703
|
+
}
|
|
1704
|
+
if (typeof from === "string" && typeof to === "string") {
|
|
1705
|
+
const mixed = mixHexColors(from, to, p);
|
|
1706
|
+
if (mixed) return mixed;
|
|
1707
|
+
}
|
|
1708
|
+
if (isPlainObject(from) && isPlainObject(to)) {
|
|
1709
|
+
const result = {};
|
|
1710
|
+
for (const key of Object.keys(from)) {
|
|
1711
|
+
result[key] = key in to ? interpolateResolved(from[key], to[key], p) : from[key];
|
|
1712
|
+
}
|
|
1713
|
+
for (const key of Object.keys(to)) {
|
|
1714
|
+
if (!(key in from)) result[key] = to[key];
|
|
1715
|
+
}
|
|
1716
|
+
return result;
|
|
1717
|
+
}
|
|
1718
|
+
return p < 0.5 ? from : to;
|
|
1719
|
+
}
|
|
1720
|
+
function parseHex(hex) {
|
|
1721
|
+
if (!isHexColor(hex)) return null;
|
|
1722
|
+
let h22 = hex.slice(1);
|
|
1723
|
+
if (h22.length === 3) h22 = h22.split("").map((c) => c + c).join("");
|
|
1724
|
+
return [
|
|
1725
|
+
parseInt(h22.slice(0, 2), 16),
|
|
1726
|
+
parseInt(h22.slice(2, 4), 16),
|
|
1727
|
+
parseInt(h22.slice(4, 6), 16),
|
|
1728
|
+
h22.length === 8 ? parseInt(h22.slice(6, 8), 16) : 255
|
|
1729
|
+
];
|
|
1730
|
+
}
|
|
1731
|
+
function mixHexColors(a, b, p) {
|
|
1732
|
+
const ca = parseHex(a);
|
|
1733
|
+
const cb = parseHex(b);
|
|
1734
|
+
if (!ca || !cb) return null;
|
|
1735
|
+
const t = clamp(p, 0, 1);
|
|
1736
|
+
const mixed = ca.map((v, i) => Math.round(v + (cb[i] - v) * t));
|
|
1737
|
+
const hex = (n) => n.toString(16).padStart(2, "0");
|
|
1738
|
+
const rgb = `#${hex(mixed[0])}${hex(mixed[1])}${hex(mixed[2])}`;
|
|
1739
|
+
return mixed[3] === 255 ? rgb : `${rgb}${hex(mixed[3])}`;
|
|
1740
|
+
}
|
|
1741
|
+
function transitionToCss(transition) {
|
|
1742
|
+
if (!transition) return void 0;
|
|
1743
|
+
if (transition.type === "easing") {
|
|
1744
|
+
return {
|
|
1745
|
+
transitionDuration: `${round2(transition.duration)}s`,
|
|
1746
|
+
transitionTimingFunction: `cubic-bezier(${transition.ease.map((v) => round2(v)).join(", ")})`
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
const params = springParams(transition);
|
|
1750
|
+
const dampingRatio = params.damping / (2 * Math.sqrt(params.stiffness * params.mass));
|
|
1751
|
+
const duration = transition.visualDuration ?? springSettleDuration(params);
|
|
1752
|
+
const bounce = transition.bounce ?? Math.max(0, round2(1 - dampingRatio));
|
|
1753
|
+
return {
|
|
1754
|
+
transitionDuration: `${round2(duration)}s`,
|
|
1755
|
+
transitionTimingFunction: bounce > 0.05 ? `cubic-bezier(0.34, ${round2(1.2 + bounce)}, 0.64, 1)` : "cubic-bezier(0.25, 0.6, 0.35, 1)"
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
function timelinePopoverDisplayValues(values, clipKey, stepKeys, stepKey) {
|
|
1759
|
+
const display = { ...values };
|
|
1760
|
+
const swap = (path, duration) => {
|
|
1761
|
+
const raw = display[path];
|
|
1762
|
+
if (isTransitionConfig(raw)) display[path] = resolveClipTransition(raw, duration).transition;
|
|
1763
|
+
};
|
|
1764
|
+
if (stepKey) {
|
|
1765
|
+
swap(`${clipKey}.${stepKey}.transition`, numberValue(values[`${clipKey}.${stepKey}.duration`]));
|
|
1766
|
+
return display;
|
|
1767
|
+
}
|
|
1768
|
+
const cycle = stepKeys?.length ? stepKeys.reduce((sum, sk) => sum + numberValue(values[`${clipKey}.${sk}.duration`]), 0) : numberValue(values[`${clipKey}.duration`]);
|
|
1769
|
+
swap(`${clipKey}.transition`, cycle);
|
|
1770
|
+
return display;
|
|
1771
|
+
}
|
|
1772
|
+
function numberValue(value) {
|
|
1773
|
+
return typeof value === "number" ? value : 0;
|
|
1774
|
+
}
|
|
1775
|
+
function unflattenClipValues(values, clipKey) {
|
|
1776
|
+
const prefix = `${clipKey}.`;
|
|
1777
|
+
const result = {};
|
|
1778
|
+
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);
|
|
1779
|
+
for (const { segments, value } of entries) {
|
|
1780
|
+
let node = result;
|
|
1781
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1782
|
+
const existing = node[segments[i]];
|
|
1783
|
+
node = isPlainObject(existing) ? existing : node[segments[i]] = {};
|
|
1784
|
+
}
|
|
1785
|
+
node[segments[segments.length - 1]] = cloneTimelineValue(value);
|
|
1786
|
+
}
|
|
1787
|
+
return result;
|
|
1788
|
+
}
|
|
1789
|
+
function cloneTimelineValue(value) {
|
|
1790
|
+
if (Array.isArray(value)) return value.map(cloneTimelineValue);
|
|
1791
|
+
if (!isPlainObject(value)) return value;
|
|
1792
|
+
return Object.fromEntries(
|
|
1793
|
+
Object.entries(value).map(([key, nested]) => [key, cloneTimelineValue(nested)])
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
function clampTrackDelay(delay, at, trackDuration, timelineDuration) {
|
|
1797
|
+
return clamp(round2(delay), 0, Math.max(0, round2(timelineDuration - at - trackDuration)));
|
|
1798
|
+
}
|
|
1799
|
+
function clampClipMove(at, duration, timelineDuration) {
|
|
1800
|
+
return clamp(round2(at), 0, Math.max(0, timelineDuration - duration));
|
|
1801
|
+
}
|
|
1802
|
+
function clampClipResizeEnd(duration, at, timelineDuration) {
|
|
1803
|
+
return clamp(round2(duration), TIMELINE_MIN_CLIP_DURATION, timelineDuration - at);
|
|
1804
|
+
}
|
|
1805
|
+
function clampClipResizeStart(newAt, at, duration) {
|
|
1806
|
+
const clampedAt = clamp(round2(newAt), 0, at + duration - TIMELINE_MIN_CLIP_DURATION);
|
|
1807
|
+
return { at: clampedAt, duration: round2(at + duration - clampedAt) };
|
|
1808
|
+
}
|
|
1809
|
+
function clampStepResize(duration, at, otherStepsTotal, timelineDuration) {
|
|
1810
|
+
const max = Math.max(TIMELINE_MIN_CLIP_DURATION, timelineDuration - at - otherStepsTotal);
|
|
1811
|
+
return clamp(round2(duration), TIMELINE_MIN_CLIP_DURATION, max);
|
|
1812
|
+
}
|
|
1813
|
+
function normalizeTimelineValuesForCopy(values, clips) {
|
|
1814
|
+
const normalized = { ...values };
|
|
1815
|
+
for (const path of Object.keys(normalized)) {
|
|
1816
|
+
if (path.endsWith(".__mode")) delete normalized[path];
|
|
1817
|
+
}
|
|
1818
|
+
const normalizeTransitionAt = (transitionPath, durationPath) => {
|
|
1819
|
+
const raw = normalized[transitionPath];
|
|
1820
|
+
if (!isTransitionConfig(raw)) return;
|
|
1821
|
+
if (isPhysicsSpring(raw)) {
|
|
1822
|
+
normalized[durationPath] = transitionDefaultDuration(raw);
|
|
1823
|
+
}
|
|
1824
|
+
normalized[transitionPath] = normalizeStoredTransition(
|
|
1825
|
+
raw,
|
|
1826
|
+
numberValue(normalized[durationPath])
|
|
1827
|
+
);
|
|
1828
|
+
};
|
|
1829
|
+
for (const clip of clips) {
|
|
1830
|
+
for (const stepKey of clip.stepKeys ?? []) {
|
|
1831
|
+
normalizeTransitionAt(
|
|
1832
|
+
`${clip.key}.${stepKey}.transition`,
|
|
1833
|
+
`${clip.key}.${stepKey}.duration`
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
normalizeTransitionAt(`${clip.key}.transition`, `${clip.key}.duration`);
|
|
1837
|
+
for (const track of clip.tracks ?? []) {
|
|
1838
|
+
const trackKey = `${clip.key}.${track.prop}`;
|
|
1839
|
+
for (const stepKey of track.stepKeys ?? []) {
|
|
1840
|
+
normalizeTransitionAt(
|
|
1841
|
+
`${trackKey}.${stepKey}.transition`,
|
|
1842
|
+
`${trackKey}.${stepKey}.duration`
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
normalizeTransitionAt(`${trackKey}.transition`, `${trackKey}.duration`);
|
|
1846
|
+
if (normalized[`${trackKey}.delay`] === 0) {
|
|
1847
|
+
delete normalized[`${trackKey}.delay`];
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
delete normalized[`${clip.key}.loop`];
|
|
1851
|
+
}
|
|
1852
|
+
return normalized;
|
|
1853
|
+
}
|
|
1854
|
+
function formatClock(time, tenths = false) {
|
|
1855
|
+
const safe = Math.max(0, time);
|
|
1856
|
+
const minutes = Math.floor(safe / 60);
|
|
1857
|
+
const seconds = safe - minutes * 60;
|
|
1858
|
+
const secondsText = tenths ? seconds.toFixed(1).padStart(4, "0") : String(Math.floor(seconds)).padStart(2, "0");
|
|
1859
|
+
return `${String(minutes).padStart(2, "0")}:${secondsText}`;
|
|
1860
|
+
}
|
|
1861
|
+
function formatSeconds(value) {
|
|
1862
|
+
return `${round2(value)}s`;
|
|
1863
|
+
}
|
|
1864
|
+
function formatStepLabel(stepKey) {
|
|
1865
|
+
const match = /^step(\d+)$/.exec(stepKey);
|
|
1866
|
+
return match ? `Step ${match[1]}` : formatLabel(stepKey);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// src/timeline/adapter.ts
|
|
1870
|
+
function resolveTimelineLoop(loop) {
|
|
1871
|
+
if (typeof loop === "object" && loop !== null) {
|
|
1872
|
+
return {
|
|
1873
|
+
enabled: true,
|
|
1874
|
+
start: Number.isFinite(loop.from) ? Math.max(0, loop.from) : 0
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
return { enabled: Boolean(loop), start: 0 };
|
|
1878
|
+
}
|
|
1879
|
+
function buildTimelineMeta(id, name, duration, parsed, loop) {
|
|
1880
|
+
const resolvedLoop = resolveTimelineLoop(loop);
|
|
1881
|
+
return {
|
|
1882
|
+
id,
|
|
1883
|
+
name,
|
|
1884
|
+
duration,
|
|
1885
|
+
loop: resolvedLoop.enabled,
|
|
1886
|
+
loopStart: resolvedLoop.start,
|
|
1887
|
+
clips: parsed.clips
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
function buildTimelineValues(staticClips, transport, timelineDuration, loopStart, actions) {
|
|
1891
|
+
var _a;
|
|
1892
|
+
const result = {
|
|
1893
|
+
time: transport.time,
|
|
1894
|
+
playing: transport.playing,
|
|
1895
|
+
duration: timelineDuration,
|
|
1896
|
+
...actions
|
|
1897
|
+
};
|
|
1898
|
+
const span = loopSpan(transport.duration, loopStart);
|
|
1899
|
+
const cycleTime = (span > 0 ? transport.wraps * span : 0) + transport.time;
|
|
1900
|
+
for (const clip of staticClips) {
|
|
1901
|
+
const state = computeClipState(clip, transport.time, cycleTime);
|
|
1902
|
+
if (clip.group) {
|
|
1903
|
+
const bucket = result[_a = clip.group] ?? (result[_a] = {});
|
|
1904
|
+
bucket[clip.childKey] = state;
|
|
1905
|
+
} else {
|
|
1906
|
+
result[clip.key] = state;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
return result;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// src/store/TimelineUiStore.ts
|
|
1913
|
+
var TimelineUiStoreClass = class {
|
|
1914
|
+
constructor() {
|
|
1915
|
+
this.visible = true;
|
|
1916
|
+
this.initialized = false;
|
|
1917
|
+
this.controllers = /* @__PURE__ */ new Map();
|
|
1918
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1919
|
+
}
|
|
1920
|
+
getVisible() {
|
|
1921
|
+
for (const controller of this.controllers.values()) {
|
|
1922
|
+
if (controller.visible !== void 0) return controller.visible;
|
|
1923
|
+
}
|
|
1924
|
+
return this.visible;
|
|
1925
|
+
}
|
|
1926
|
+
registerController(id, controller) {
|
|
1927
|
+
const previous = this.getVisible();
|
|
1928
|
+
if (!this.initialized) {
|
|
1929
|
+
this.visible = controller.defaultVisible;
|
|
1930
|
+
this.initialized = true;
|
|
1931
|
+
}
|
|
1932
|
+
this.controllers.set(id, controller);
|
|
1933
|
+
if (previous !== this.getVisible()) this.notify();
|
|
1934
|
+
return () => {
|
|
1935
|
+
const before = this.getVisible();
|
|
1936
|
+
this.controllers.delete(id);
|
|
1937
|
+
if (this.controllers.size === 0) this.initialized = false;
|
|
1938
|
+
if (before !== this.getVisible()) this.notify();
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
updateController(id, controller) {
|
|
1942
|
+
if (!this.controllers.has(id)) return;
|
|
1943
|
+
const previous = this.getVisible();
|
|
1944
|
+
this.controllers.set(id, controller);
|
|
1945
|
+
if (previous !== this.getVisible()) this.notify();
|
|
1946
|
+
}
|
|
1947
|
+
requestVisible(visible) {
|
|
1948
|
+
const current = this.getVisible();
|
|
1949
|
+
if (current === visible) return;
|
|
1950
|
+
const controlled = Array.from(this.controllers.values()).filter(
|
|
1951
|
+
(controller) => controller.visible !== void 0
|
|
1952
|
+
);
|
|
1953
|
+
if (controlled.length > 0) {
|
|
1954
|
+
controlled.forEach((controller) => controller.onVisibilityChange?.(visible));
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
this.visible = visible;
|
|
1958
|
+
this.controllers.forEach((controller) => controller.onVisibilityChange?.(visible));
|
|
1959
|
+
this.notify();
|
|
1960
|
+
}
|
|
1961
|
+
toggle() {
|
|
1962
|
+
this.requestVisible(!this.getVisible());
|
|
1963
|
+
}
|
|
1964
|
+
subscribe(listener) {
|
|
1965
|
+
this.listeners.add(listener);
|
|
1966
|
+
return () => this.listeners.delete(listener);
|
|
1967
|
+
}
|
|
1968
|
+
notify() {
|
|
1969
|
+
this.listeners.forEach((listener) => listener());
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
var TimelineUiStore = /* @__PURE__ */ new TimelineUiStoreClass();
|
|
1973
|
+
|
|
1974
|
+
// src/copy-instruction.ts
|
|
1975
|
+
function buildCopyInstruction(hookName, panelName, values) {
|
|
1976
|
+
const jsonStr = JSON.stringify(values, null, 2);
|
|
1977
|
+
if (hookName === "useDialTimeline" || hookName === "createDialTimeline") {
|
|
1978
|
+
return `Update the ${hookName} configuration for "${panelName}" with these values:
|
|
1979
|
+
|
|
1980
|
+
\`\`\`json
|
|
1981
|
+
${jsonStr}
|
|
1982
|
+
\`\`\`
|
|
1983
|
+
|
|
1984
|
+
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.
|
|
1985
|
+
|
|
1986
|
+
Add this comment immediately above the ${hookName} call as a production handoff note:
|
|
1987
|
+
|
|
1988
|
+
\`\`\`tsx
|
|
1989
|
+
// TODO(production): DialKit's clip.current values are the scrubbable authoring preview.
|
|
1990
|
+
// Replace them with equivalent real Motion animations using the tuned timeline
|
|
1991
|
+
// timings and transitions, then remove ${hookName} and <DialTimeline />.
|
|
1992
|
+
\`\`\``;
|
|
1993
|
+
}
|
|
1994
|
+
return `Update the ${hookName} configuration for "${panelName}" with these values:
|
|
1995
|
+
|
|
1996
|
+
\`\`\`json
|
|
1997
|
+
${jsonStr}
|
|
1998
|
+
\`\`\`
|
|
1999
|
+
|
|
2000
|
+
Apply these values as the new defaults in the ${hookName} call.`;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/env.ts
|
|
2004
|
+
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;
|
|
2005
|
+
|
|
2006
|
+
// src/vue/useDialTimeline.ts
|
|
2007
|
+
var timelineInstance = 0;
|
|
2008
|
+
function useDialTimeline(name, config, options) {
|
|
2009
|
+
const hasStableId = options?.id !== void 0;
|
|
2010
|
+
const panelId = options?.id ?? `${name}-${++timelineInstance}`;
|
|
2011
|
+
const serializedConfig = computed2(() => JSON.stringify(config));
|
|
2012
|
+
const serializedPersist = computed2(() => JSON.stringify(options?.persist));
|
|
2013
|
+
const serializedLoop = computed2(() => JSON.stringify(options?.loop));
|
|
2014
|
+
const parsed = computed2(() => {
|
|
2015
|
+
serializedConfig.value;
|
|
2016
|
+
return parseTimelineConfig(config);
|
|
2017
|
+
});
|
|
2018
|
+
const flatValues = shallowRef2(DialStore.getValues(panelId));
|
|
2019
|
+
const transport = shallowRef2(TimelineStore.getTransport(panelId));
|
|
2020
|
+
const staticTimeline = computed2(() => computeStaticTimeline(parsed.value, flatValues.value));
|
|
2021
|
+
const meta = computed2(() => {
|
|
2022
|
+
serializedLoop.value;
|
|
2023
|
+
return buildTimelineMeta(
|
|
2024
|
+
panelId,
|
|
2025
|
+
name,
|
|
2026
|
+
staticTimeline.value.duration,
|
|
2027
|
+
parsed.value,
|
|
2028
|
+
options?.loop
|
|
2029
|
+
);
|
|
2030
|
+
});
|
|
2031
|
+
let mounted = false;
|
|
2032
|
+
let unsubscribeValues;
|
|
2033
|
+
let unsubscribeTransport;
|
|
2034
|
+
const play = () => TimelineStore.play(panelId);
|
|
2035
|
+
const pause = () => TimelineStore.pause(panelId);
|
|
2036
|
+
const replay = () => TimelineStore.replay(panelId);
|
|
2037
|
+
const seek = (time) => TimelineStore.seek(panelId, time);
|
|
2038
|
+
watch2([serializedConfig, serializedPersist], () => {
|
|
2039
|
+
if (!mounted) return;
|
|
2040
|
+
DialStore.updatePanel(panelId, name, parsed.value.dialConfig, void 0, {
|
|
2041
|
+
retainOnUnmount: hasStableId,
|
|
2042
|
+
persist: options?.persist,
|
|
2043
|
+
kind: "timeline"
|
|
2044
|
+
});
|
|
2045
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
2046
|
+
});
|
|
2047
|
+
watch2(meta, (nextMeta) => {
|
|
2048
|
+
if (mounted) TimelineStore.update(nextMeta);
|
|
2049
|
+
});
|
|
2050
|
+
onMounted2(() => {
|
|
2051
|
+
unsubscribeValues = DialStore.subscribe(panelId, () => {
|
|
2052
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
2053
|
+
});
|
|
2054
|
+
unsubscribeTransport = TimelineStore.subscribe(panelId, () => {
|
|
2055
|
+
transport.value = TimelineStore.getTransport(panelId);
|
|
2056
|
+
});
|
|
2057
|
+
DialStore.registerPanel(panelId, name, parsed.value.dialConfig, void 0, {
|
|
2058
|
+
retainOnUnmount: hasStableId,
|
|
2059
|
+
persist: options?.persist,
|
|
2060
|
+
kind: "timeline"
|
|
2061
|
+
});
|
|
2062
|
+
flatValues.value = DialStore.getValues(panelId);
|
|
2063
|
+
TimelineStore.register(meta.value, { autoplay: options?.autoplay ?? true });
|
|
2064
|
+
transport.value = TimelineStore.getTransport(panelId);
|
|
2065
|
+
mounted = true;
|
|
2066
|
+
});
|
|
2067
|
+
onUnmounted2(() => {
|
|
2068
|
+
mounted = false;
|
|
2069
|
+
unsubscribeValues?.();
|
|
2070
|
+
unsubscribeTransport?.();
|
|
2071
|
+
TimelineStore.unregister(panelId);
|
|
2072
|
+
DialStore.unregisterPanel(panelId);
|
|
2073
|
+
});
|
|
2074
|
+
return computed2(() => {
|
|
2075
|
+
const currentStatic = staticTimeline.value;
|
|
2076
|
+
return buildTimelineValues(
|
|
2077
|
+
currentStatic.clips,
|
|
2078
|
+
transport.value,
|
|
2079
|
+
currentStatic.duration,
|
|
2080
|
+
resolveTimelineLoop(options?.loop).start,
|
|
2081
|
+
{ play, pause, replay, seek }
|
|
2082
|
+
);
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
// src/vue/directives/dialkit.ts
|
|
2087
|
+
import {
|
|
2088
|
+
createApp,
|
|
2089
|
+
defineComponent as defineComponent17,
|
|
2090
|
+
h as h17,
|
|
2091
|
+
shallowRef as shallowRef3
|
|
2092
|
+
} from "vue";
|
|
2093
|
+
|
|
2094
|
+
// src/vue/components/DialRoot.ts
|
|
2095
|
+
import { computed as computed6, defineComponent as defineComponent16, h as h16, nextTick as nextTick3, onMounted as onMounted12, onUnmounted as onUnmounted11, ref as ref14, Teleport as Teleport3 } from "vue";
|
|
2096
|
+
|
|
2097
|
+
// src/vue/components/Folder.ts
|
|
2098
|
+
import { defineComponent, h, onMounted as onMounted3, onUnmounted as onUnmounted3, ref as ref2 } from "vue";
|
|
2099
|
+
import { AnimatePresence, motion } from "motion-v";
|
|
2100
|
+
|
|
2101
|
+
// src/icons.ts
|
|
2102
|
+
var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
|
|
2103
|
+
var ICON_CHECK = "M5 12.75L10 19L19 5";
|
|
2104
|
+
var ICON_PAUSE = [
|
|
2105
|
+
"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",
|
|
2106
|
+
"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"
|
|
2107
|
+
];
|
|
2108
|
+
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";
|
|
2109
|
+
var ICON_TIMELINE = [
|
|
2110
|
+
"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",
|
|
2111
|
+
"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",
|
|
2112
|
+
"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"
|
|
2113
|
+
];
|
|
2114
|
+
var ICON_CLIPBOARD = {
|
|
2115
|
+
board: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z",
|
|
2116
|
+
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",
|
|
2117
|
+
body: "M16 5H17C18.6569 5 20 6.34315 20 8V11M8 5H7C5.34315 5 4 6.34315 4 8V18C4 19.6569 5.34315 21 7 21H12"
|
|
2118
|
+
};
|
|
2119
|
+
var ICON_ADD_PRESET = [
|
|
2120
|
+
"M4 6H20",
|
|
2121
|
+
"M4 12H10",
|
|
2122
|
+
"M15 15L21 15",
|
|
2123
|
+
"M18 12V18",
|
|
594
2124
|
"M4 18H10"
|
|
595
2125
|
];
|
|
596
2126
|
var ICON_TRASH = [
|
|
@@ -610,8 +2140,6 @@ var ICON_PANEL = {
|
|
|
610
2140
|
};
|
|
611
2141
|
|
|
612
2142
|
// src/vue/components/Folder.ts
|
|
613
|
-
import { defineComponent, h, onMounted as onMounted2, onUnmounted as onUnmounted2, ref as ref2 } from "vue";
|
|
614
|
-
import { AnimatePresence, motion } from "motion-v";
|
|
615
2143
|
var Folder = defineComponent({
|
|
616
2144
|
name: "DialKitFolder",
|
|
617
2145
|
props: {
|
|
@@ -623,6 +2151,10 @@ var Folder = defineComponent({
|
|
|
623
2151
|
type: null,
|
|
624
2152
|
required: false,
|
|
625
2153
|
default: null
|
|
2154
|
+
},
|
|
2155
|
+
panelHeightOffset: {
|
|
2156
|
+
type: Number,
|
|
2157
|
+
default: 10
|
|
626
2158
|
}
|
|
627
2159
|
},
|
|
628
2160
|
emits: ["openChange"],
|
|
@@ -639,7 +2171,7 @@ var Folder = defineComponent({
|
|
|
639
2171
|
};
|
|
640
2172
|
window.addEventListener("resize", resizeHandler);
|
|
641
2173
|
}
|
|
642
|
-
|
|
2174
|
+
onUnmounted3(() => {
|
|
643
2175
|
if (resizeHandler) window.removeEventListener("resize", resizeHandler);
|
|
644
2176
|
});
|
|
645
2177
|
const handleToggle = () => {
|
|
@@ -650,7 +2182,7 @@ var Folder = defineComponent({
|
|
|
650
2182
|
emit("openChange", next);
|
|
651
2183
|
};
|
|
652
2184
|
let ro = null;
|
|
653
|
-
|
|
2185
|
+
onMounted3(() => {
|
|
654
2186
|
if (!props.isRoot || typeof ResizeObserver === "undefined") return;
|
|
655
2187
|
const el = contentRef.value;
|
|
656
2188
|
if (!el) return;
|
|
@@ -667,7 +2199,7 @@ var Folder = defineComponent({
|
|
|
667
2199
|
contentHeight.value = el.offsetHeight;
|
|
668
2200
|
}
|
|
669
2201
|
});
|
|
670
|
-
|
|
2202
|
+
onUnmounted3(() => {
|
|
671
2203
|
ro?.disconnect();
|
|
672
2204
|
});
|
|
673
2205
|
const renderHeader = () => h("div", {
|
|
@@ -722,7 +2254,8 @@ var Folder = defineComponent({
|
|
|
722
2254
|
};
|
|
723
2255
|
const folderContent = () => h("div", {
|
|
724
2256
|
ref: props.isRoot ? contentRef : void 0,
|
|
725
|
-
class: `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}
|
|
2257
|
+
class: `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`,
|
|
2258
|
+
"data-open": String(isOpen.value)
|
|
726
2259
|
}, [
|
|
727
2260
|
renderHeader(),
|
|
728
2261
|
renderContent()
|
|
@@ -734,7 +2267,7 @@ var Folder = defineComponent({
|
|
|
734
2267
|
}
|
|
735
2268
|
const panelStyle = isOpen.value ? {
|
|
736
2269
|
width: 280,
|
|
737
|
-
height: contentHeight.value !== void 0 ? Math.min(contentHeight.value +
|
|
2270
|
+
height: contentHeight.value !== void 0 ? Math.min(contentHeight.value + props.panelHeightOffset, windowHeight.value - 32) : "auto",
|
|
738
2271
|
borderRadius: 14,
|
|
739
2272
|
boxShadow: "var(--dial-shadow)",
|
|
740
2273
|
cursor: void 0,
|
|
@@ -761,8 +2294,12 @@ var Folder = defineComponent({
|
|
|
761
2294
|
}
|
|
762
2295
|
});
|
|
763
2296
|
|
|
2297
|
+
// src/vue/components/Panel.ts
|
|
2298
|
+
import { Fragment, defineComponent as defineComponent14, h as h14, onMounted as onMounted10, onUnmounted as onUnmounted9, ref as ref12 } from "vue";
|
|
2299
|
+
import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion-v";
|
|
2300
|
+
|
|
764
2301
|
// src/vue/components/Slider.ts
|
|
765
|
-
import { defineComponent as defineComponent2, h as h2, computed as
|
|
2302
|
+
import { defineComponent as defineComponent2, h as h2, computed as computed3, nextTick, onMounted as onMounted4, onUnmounted as onUnmounted4, ref as ref3, watch as watch3 } from "vue";
|
|
766
2303
|
import { animate, motionValue } from "motion-v";
|
|
767
2304
|
|
|
768
2305
|
// src/shortcut-utils.ts
|
|
@@ -869,9 +2406,9 @@ var Slider = defineComponent2({
|
|
|
869
2406
|
},
|
|
870
2407
|
emits: ["change"],
|
|
871
2408
|
setup(props, { emit }) {
|
|
872
|
-
const min =
|
|
873
|
-
const max =
|
|
874
|
-
const step =
|
|
2409
|
+
const min = computed3(() => props.min ?? 0);
|
|
2410
|
+
const max = computed3(() => props.max ?? 1);
|
|
2411
|
+
const step = computed3(() => props.step ?? 0.01);
|
|
875
2412
|
const wrapperRef = ref3(null);
|
|
876
2413
|
const trackRef = ref3(null);
|
|
877
2414
|
const fillRef = ref3(null);
|
|
@@ -891,9 +2428,9 @@ var Slider = defineComponent2({
|
|
|
891
2428
|
const handleOpacityMv = motionValue(0);
|
|
892
2429
|
const handleScaleXMv = motionValue(0.25);
|
|
893
2430
|
const handleScaleYMv = motionValue(1);
|
|
894
|
-
const percentage =
|
|
895
|
-
const isActive =
|
|
896
|
-
const displayValue =
|
|
2431
|
+
const percentage = computed3(() => (props.value - min.value) / (max.value - min.value) * 100);
|
|
2432
|
+
const isActive = computed3(() => isInteracting.value || isHovered.value);
|
|
2433
|
+
const displayValue = computed3(() => props.value.toFixed(decimalsForStep(step.value)));
|
|
897
2434
|
let pointerDownPos = null;
|
|
898
2435
|
let isClickFlag = true;
|
|
899
2436
|
let wrapperRect = null;
|
|
@@ -1082,15 +2619,15 @@ var Slider = defineComponent2({
|
|
|
1082
2619
|
isValueHovered.value = false;
|
|
1083
2620
|
}
|
|
1084
2621
|
};
|
|
1085
|
-
|
|
2622
|
+
watch3(() => props.value, () => {
|
|
1086
2623
|
if (!isInteracting.value && !snapAnim) {
|
|
1087
2624
|
fillPercent.jump(percentage.value);
|
|
1088
2625
|
}
|
|
1089
2626
|
});
|
|
1090
|
-
|
|
2627
|
+
watch3([isInteracting, isHovered, isDragging, () => props.value], () => {
|
|
1091
2628
|
animateHandleState();
|
|
1092
2629
|
});
|
|
1093
|
-
|
|
2630
|
+
watch3([isValueHovered, showInput, isValueEditable], () => {
|
|
1094
2631
|
if (hoverTimeout) {
|
|
1095
2632
|
clearTimeout(hoverTimeout);
|
|
1096
2633
|
hoverTimeout = null;
|
|
@@ -1104,14 +2641,14 @@ var Slider = defineComponent2({
|
|
|
1104
2641
|
isValueEditable.value = false;
|
|
1105
2642
|
}
|
|
1106
2643
|
});
|
|
1107
|
-
|
|
2644
|
+
watch3(showInput, async (visible) => {
|
|
1108
2645
|
if (!visible) return;
|
|
1109
2646
|
await nextTick();
|
|
1110
2647
|
inputRef.value?.focus();
|
|
1111
2648
|
inputRef.value?.select();
|
|
1112
2649
|
});
|
|
1113
|
-
const discreteSteps =
|
|
1114
|
-
const hashMarks =
|
|
2650
|
+
const discreteSteps = computed3(() => (max.value - min.value) / step.value);
|
|
2651
|
+
const hashMarks = computed3(() => {
|
|
1115
2652
|
const marks = [];
|
|
1116
2653
|
if (discreteSteps.value <= 10) {
|
|
1117
2654
|
const count = Math.max(0, Math.floor(discreteSteps.value) - 1);
|
|
@@ -1132,7 +2669,7 @@ var Slider = defineComponent2({
|
|
|
1132
2669
|
let unsubHandleOpacity = null;
|
|
1133
2670
|
let unsubHandleScaleX = null;
|
|
1134
2671
|
let unsubHandleScaleY = null;
|
|
1135
|
-
|
|
2672
|
+
onMounted4(() => {
|
|
1136
2673
|
unsubFill = fillPercent.on("change", applyFillStyles);
|
|
1137
2674
|
unsubRubber = rubberStretchPx.on("change", applyRubberStyles);
|
|
1138
2675
|
unsubHandleOpacity = handleOpacityMv.on("change", applyHandleVisualStyles);
|
|
@@ -1143,7 +2680,7 @@ var Slider = defineComponent2({
|
|
|
1143
2680
|
applyHandleVisualStyles();
|
|
1144
2681
|
animateHandleState();
|
|
1145
2682
|
});
|
|
1146
|
-
|
|
2683
|
+
onUnmounted4(() => {
|
|
1147
2684
|
if (hoverTimeout) clearTimeout(hoverTimeout);
|
|
1148
2685
|
snapAnim?.stop();
|
|
1149
2686
|
rubberAnim?.stop();
|
|
@@ -1232,7 +2769,7 @@ var Slider = defineComponent2({
|
|
|
1232
2769
|
import { defineComponent as defineComponent4, h as h4 } from "vue";
|
|
1233
2770
|
|
|
1234
2771
|
// src/vue/components/SegmentedControl.ts
|
|
1235
|
-
import { defineComponent as defineComponent3, h as h3, nextTick as nextTick2, onMounted as
|
|
2772
|
+
import { defineComponent as defineComponent3, h as h3, nextTick as nextTick2, onMounted as onMounted5, onUnmounted as onUnmounted5, ref as ref4, watch as watch4 } from "vue";
|
|
1236
2773
|
import { animate as animate2 } from "motion";
|
|
1237
2774
|
var SegmentedControl = defineComponent3({
|
|
1238
2775
|
name: "DialKitSegmentedControl",
|
|
@@ -1303,7 +2840,7 @@ var SegmentedControl = defineComponent3({
|
|
|
1303
2840
|
);
|
|
1304
2841
|
};
|
|
1305
2842
|
let ro;
|
|
1306
|
-
|
|
2843
|
+
onMounted5(() => {
|
|
1307
2844
|
nextTick2(() => {
|
|
1308
2845
|
updatePill(false);
|
|
1309
2846
|
hasAnimated = true;
|
|
@@ -1313,11 +2850,11 @@ var SegmentedControl = defineComponent3({
|
|
|
1313
2850
|
ro.observe(containerRef.value);
|
|
1314
2851
|
}
|
|
1315
2852
|
});
|
|
1316
|
-
|
|
2853
|
+
onUnmounted5(() => {
|
|
1317
2854
|
pillAnim?.stop();
|
|
1318
2855
|
ro?.disconnect();
|
|
1319
2856
|
});
|
|
1320
|
-
|
|
2857
|
+
watch4(
|
|
1321
2858
|
() => props.value,
|
|
1322
2859
|
() => {
|
|
1323
2860
|
updatePill(true);
|
|
@@ -1381,10 +2918,10 @@ var Toggle = defineComponent4({
|
|
|
1381
2918
|
});
|
|
1382
2919
|
|
|
1383
2920
|
// src/vue/components/SpringControl.ts
|
|
1384
|
-
import { defineComponent as defineComponent6, h as h6, onMounted as
|
|
2921
|
+
import { defineComponent as defineComponent6, h as h6, onMounted as onMounted6, onUnmounted as onUnmounted6, ref as ref5 } from "vue";
|
|
1385
2922
|
|
|
1386
2923
|
// src/vue/components/SpringVisualization.ts
|
|
1387
|
-
import { defineComponent as defineComponent5, h as h5, computed as
|
|
2924
|
+
import { defineComponent as defineComponent5, h as h5, computed as computed4 } from "vue";
|
|
1388
2925
|
function generateSpringCurve(stiffness, damping, mass, duration) {
|
|
1389
2926
|
const points = [];
|
|
1390
2927
|
const steps = 100;
|
|
@@ -1418,7 +2955,7 @@ var SpringVisualization = defineComponent5({
|
|
|
1418
2955
|
setup(props) {
|
|
1419
2956
|
const width = 256;
|
|
1420
2957
|
const height = 140;
|
|
1421
|
-
const pathData =
|
|
2958
|
+
const pathData = computed4(() => {
|
|
1422
2959
|
let stiffness;
|
|
1423
2960
|
let damping;
|
|
1424
2961
|
let mass;
|
|
@@ -1494,12 +3031,12 @@ var SpringControl = defineComponent6({
|
|
|
1494
3031
|
setup(props, { emit }) {
|
|
1495
3032
|
const mode = ref5(DialStore.getSpringMode(props.panelId, props.path));
|
|
1496
3033
|
let unsub;
|
|
1497
|
-
|
|
3034
|
+
onMounted6(() => {
|
|
1498
3035
|
unsub = DialStore.subscribe(props.panelId, () => {
|
|
1499
3036
|
mode.value = DialStore.getSpringMode(props.panelId, props.path);
|
|
1500
3037
|
});
|
|
1501
3038
|
});
|
|
1502
|
-
|
|
3039
|
+
onUnmounted6(() => {
|
|
1503
3040
|
unsub?.();
|
|
1504
3041
|
});
|
|
1505
3042
|
const isSimpleMode = () => mode.value === "simple";
|
|
@@ -1595,10 +3132,10 @@ var SpringControl = defineComponent6({
|
|
|
1595
3132
|
});
|
|
1596
3133
|
|
|
1597
3134
|
// src/vue/components/TransitionControl.ts
|
|
1598
|
-
import { defineComponent as defineComponent8, h as h8, onMounted as
|
|
3135
|
+
import { defineComponent as defineComponent8, h as h8, onMounted as onMounted7, onUnmounted as onUnmounted7, ref as ref6 } from "vue";
|
|
1599
3136
|
|
|
1600
3137
|
// src/vue/components/EasingVisualization.ts
|
|
1601
|
-
import { defineComponent as defineComponent7, h as h7, computed as
|
|
3138
|
+
import { defineComponent as defineComponent7, h as h7, computed as computed5 } from "vue";
|
|
1602
3139
|
var EasingVisualization = defineComponent7({
|
|
1603
3140
|
name: "DialKitEasingVisualization",
|
|
1604
3141
|
props: {
|
|
@@ -1612,7 +3149,7 @@ var EasingVisualization = defineComponent7({
|
|
|
1612
3149
|
const pad = 10;
|
|
1613
3150
|
const inner = size - pad * 2;
|
|
1614
3151
|
const unit = inner / 2;
|
|
1615
|
-
const curve =
|
|
3152
|
+
const curve = computed5(() => {
|
|
1616
3153
|
const [x1, y1, x2, y2] = props.easing.ease;
|
|
1617
3154
|
const toSvg = (nx, ny) => ({
|
|
1618
3155
|
x: pad + (nx + 0.5) * unit,
|
|
@@ -1715,18 +3252,20 @@ var TransitionControl = defineComponent8({
|
|
|
1715
3252
|
value: {
|
|
1716
3253
|
type: Object,
|
|
1717
3254
|
required: true
|
|
1718
|
-
}
|
|
3255
|
+
},
|
|
3256
|
+
hideDuration: { type: Boolean, default: false },
|
|
3257
|
+
durationControl: Object
|
|
1719
3258
|
},
|
|
1720
3259
|
emits: ["change"],
|
|
1721
3260
|
setup(props, { emit }) {
|
|
1722
3261
|
const mode = ref6(DialStore.getTransitionMode(props.panelId, props.path));
|
|
1723
3262
|
let unsub;
|
|
1724
|
-
|
|
3263
|
+
onMounted7(() => {
|
|
1725
3264
|
unsub = DialStore.subscribe(props.panelId, () => {
|
|
1726
3265
|
mode.value = DialStore.getTransitionMode(props.panelId, props.path);
|
|
1727
3266
|
});
|
|
1728
3267
|
});
|
|
1729
|
-
|
|
3268
|
+
onUnmounted7(() => unsub?.());
|
|
1730
3269
|
const cache = {
|
|
1731
3270
|
easing: props.value.type === "easing" ? { ...props.value } : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] },
|
|
1732
3271
|
simple: props.value.type === "spring" && props.value.visualDuration !== void 0 ? { ...props.value } : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
@@ -1778,6 +3317,18 @@ var TransitionControl = defineComponent8({
|
|
|
1778
3317
|
const isSimpleSpring = mode.value === "simple";
|
|
1779
3318
|
const currentSpring = spring();
|
|
1780
3319
|
const currentEasing = easing();
|
|
3320
|
+
const durationSlider = !props.hideDuration && (isEasing || isSimpleSpring) ? h8(Slider, {
|
|
3321
|
+
label: "Duration",
|
|
3322
|
+
value: props.durationControl?.value ?? (isEasing ? currentEasing.duration : currentSpring.visualDuration ?? 0.3),
|
|
3323
|
+
min: props.durationControl?.min ?? 0.1,
|
|
3324
|
+
max: props.durationControl?.max ?? (isEasing ? 2 : 1),
|
|
3325
|
+
step: props.durationControl?.step ?? 0.05,
|
|
3326
|
+
unit: "s",
|
|
3327
|
+
onChange: props.durationControl?.onChange ?? ((next) => {
|
|
3328
|
+
if (isEasing) emit("change", { ...currentEasing, duration: next });
|
|
3329
|
+
else handleSpringUpdate("visualDuration", next);
|
|
3330
|
+
})
|
|
3331
|
+
}) : null;
|
|
1781
3332
|
return h8(Folder, { title: props.label, defaultOpen: true }, {
|
|
1782
3333
|
default: () => [
|
|
1783
3334
|
h8("div", { style: { display: "flex", flexDirection: "column", gap: "6px" } }, [
|
|
@@ -1799,29 +3350,11 @@ var TransitionControl = defineComponent8({
|
|
|
1799
3350
|
h8(Slider, { label: "y1", value: currentEasing.ease[1], min: -1, max: 2, step: 0.01, onChange: (next) => updateEase(1, next) }),
|
|
1800
3351
|
h8(Slider, { label: "x2", value: currentEasing.ease[2], min: 0, max: 1, step: 0.01, onChange: (next) => updateEase(2, next) }),
|
|
1801
3352
|
h8(Slider, { label: "y2", value: currentEasing.ease[3], min: -1, max: 2, step: 0.01, onChange: (next) => updateEase(3, next) }),
|
|
1802
|
-
h8(Slider, {
|
|
1803
|
-
label: "Duration",
|
|
1804
|
-
value: currentEasing.duration,
|
|
1805
|
-
min: 0.1,
|
|
1806
|
-
max: 2,
|
|
1807
|
-
step: 0.05,
|
|
1808
|
-
unit: "s",
|
|
1809
|
-
onChange: (next) => emit("change", { ...currentEasing, duration: next })
|
|
1810
|
-
}),
|
|
1811
3353
|
h8(EaseTextInput, {
|
|
1812
3354
|
ease: currentEasing.ease,
|
|
1813
3355
|
onChange: (next) => emit("change", { ...currentEasing, ease: next })
|
|
1814
3356
|
})
|
|
1815
3357
|
] : isSimpleSpring ? [
|
|
1816
|
-
h8(Slider, {
|
|
1817
|
-
label: "Duration",
|
|
1818
|
-
value: currentSpring.visualDuration ?? 0.3,
|
|
1819
|
-
min: 0.1,
|
|
1820
|
-
max: 1,
|
|
1821
|
-
step: 0.05,
|
|
1822
|
-
unit: "s",
|
|
1823
|
-
onChange: (next) => handleSpringUpdate("visualDuration", next)
|
|
1824
|
-
}),
|
|
1825
3358
|
h8(Slider, {
|
|
1826
3359
|
label: "Bounce",
|
|
1827
3360
|
value: currentSpring.bounce ?? 0.2,
|
|
@@ -1855,7 +3388,8 @@ var TransitionControl = defineComponent8({
|
|
|
1855
3388
|
step: 0.1,
|
|
1856
3389
|
onChange: (next) => handleSpringUpdate("mass", next)
|
|
1857
3390
|
})
|
|
1858
|
-
]
|
|
3391
|
+
],
|
|
3392
|
+
durationSlider
|
|
1859
3393
|
])
|
|
1860
3394
|
]
|
|
1861
3395
|
});
|
|
@@ -1891,8 +3425,28 @@ var TextControl = defineComponent9({
|
|
|
1891
3425
|
});
|
|
1892
3426
|
|
|
1893
3427
|
// src/vue/components/SelectControl.ts
|
|
1894
|
-
import { Teleport, defineComponent as defineComponent10, h as h10, onMounted as
|
|
3428
|
+
import { Teleport, defineComponent as defineComponent10, h as h10, onMounted as onMounted8, ref as ref8, watch as watch5 } from "vue";
|
|
1895
3429
|
import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion-v";
|
|
3430
|
+
|
|
3431
|
+
// src/dropdown-position.ts
|
|
3432
|
+
function getDropdownPosition(trigger, portalRoot, options = {}) {
|
|
3433
|
+
const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;
|
|
3434
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
3435
|
+
const rootRect = portalRoot.getBoundingClientRect();
|
|
3436
|
+
const spaceBelow = window.innerHeight - triggerRect.bottom - gap;
|
|
3437
|
+
const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;
|
|
3438
|
+
return {
|
|
3439
|
+
top: above ? triggerRect.top - rootRect.top - dropdownHeight - gap : triggerRect.bottom - rootRect.top + gap,
|
|
3440
|
+
left: triggerRect.left - rootRect.left,
|
|
3441
|
+
width: triggerRect.width,
|
|
3442
|
+
above
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3445
|
+
function getDialKitPortalRoot(trigger) {
|
|
3446
|
+
return trigger?.closest(".dialkit-root") ?? null;
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
// src/vue/components/SelectControl.ts
|
|
1896
3450
|
function toTitleCase(value) {
|
|
1897
3451
|
return value.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1898
3452
|
}
|
|
@@ -1921,17 +3475,9 @@ var SelectControl = defineComponent10({
|
|
|
1921
3475
|
const normalizedOptions = () => normalizeOptions(props.options);
|
|
1922
3476
|
const selectedLabel = () => normalizedOptions().find((option) => option.value === props.value)?.label ?? props.value;
|
|
1923
3477
|
const updatePos = () => {
|
|
1924
|
-
if (!triggerRef.value) return;
|
|
1925
|
-
const rect = triggerRef.value.getBoundingClientRect();
|
|
3478
|
+
if (!triggerRef.value || !portalTarget.value) return;
|
|
1926
3479
|
const dropdownHeight = 8 + normalizedOptions().length * 36;
|
|
1927
|
-
|
|
1928
|
-
const above = spaceBelow < dropdownHeight && rect.top > spaceBelow;
|
|
1929
|
-
pos.value = {
|
|
1930
|
-
top: above ? rect.top - 4 : rect.bottom + 4,
|
|
1931
|
-
left: rect.left,
|
|
1932
|
-
width: rect.width,
|
|
1933
|
-
above
|
|
1934
|
-
};
|
|
3480
|
+
pos.value = getDropdownPosition(triggerRef.value, portalTarget.value, { dropdownHeight });
|
|
1935
3481
|
};
|
|
1936
3482
|
const openDropdown = () => {
|
|
1937
3483
|
updatePos();
|
|
@@ -1956,7 +3502,7 @@ var SelectControl = defineComponent10({
|
|
|
1956
3502
|
if (isOpen.value) closeDropdown();
|
|
1957
3503
|
else openDropdown();
|
|
1958
3504
|
};
|
|
1959
|
-
|
|
3505
|
+
watch5(isOpen, (open, _, onCleanup) => {
|
|
1960
3506
|
if (!open) return;
|
|
1961
3507
|
const handleViewportChange = () => updatePos();
|
|
1962
3508
|
const handleDocumentClick = (event) => {
|
|
@@ -1974,9 +3520,8 @@ var SelectControl = defineComponent10({
|
|
|
1974
3520
|
window.removeEventListener("scroll", handleViewportChange, true);
|
|
1975
3521
|
});
|
|
1976
3522
|
});
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
portalTarget.value = root ?? document.body;
|
|
3523
|
+
onMounted8(() => {
|
|
3524
|
+
portalTarget.value = getDialKitPortalRoot(triggerRef.value) ?? document.body;
|
|
1980
3525
|
});
|
|
1981
3526
|
return () => h10("div", { class: "dialkit-select-row" }, [
|
|
1982
3527
|
h10("button", {
|
|
@@ -2012,16 +3557,11 @@ var SelectControl = defineComponent10({
|
|
|
2012
3557
|
exit: { opacity: 0, y: pos.value.above ? 8 : -8, scale: 0.95 },
|
|
2013
3558
|
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
2014
3559
|
style: {
|
|
2015
|
-
position: "
|
|
3560
|
+
position: "absolute",
|
|
2016
3561
|
left: `${pos.value.left}px`,
|
|
3562
|
+
top: `${pos.value.top}px`,
|
|
2017
3563
|
width: `${pos.value.width}px`,
|
|
2018
|
-
|
|
2019
|
-
bottom: `${window.innerHeight - pos.value.top}px`,
|
|
2020
|
-
transformOrigin: "bottom"
|
|
2021
|
-
} : {
|
|
2022
|
-
top: `${pos.value.top}px`,
|
|
2023
|
-
transformOrigin: "top"
|
|
2024
|
-
}
|
|
3564
|
+
transformOrigin: pos.value.above ? "bottom" : "top"
|
|
2025
3565
|
}
|
|
2026
3566
|
}, normalizedOptions().map((option) => h10("button", {
|
|
2027
3567
|
key: option.value,
|
|
@@ -2039,7 +3579,7 @@ var SelectControl = defineComponent10({
|
|
|
2039
3579
|
});
|
|
2040
3580
|
|
|
2041
3581
|
// src/vue/components/ColorControl.ts
|
|
2042
|
-
import { defineComponent as defineComponent11, h as h11, ref as ref9, watch as
|
|
3582
|
+
import { defineComponent as defineComponent11, h as h11, ref as ref9, watch as watch6 } from "vue";
|
|
2043
3583
|
var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
2044
3584
|
function expandShorthandHex(hex) {
|
|
2045
3585
|
if (hex.length !== 4) return hex;
|
|
@@ -2058,7 +3598,7 @@ var ColorControl = defineComponent11({
|
|
|
2058
3598
|
const isEditing = ref9(false);
|
|
2059
3599
|
const editValue = ref9(props.value);
|
|
2060
3600
|
const colorInputRef = ref9(null);
|
|
2061
|
-
|
|
3601
|
+
watch6(() => props.value, (value) => {
|
|
2062
3602
|
if (!isEditing.value) editValue.value = value;
|
|
2063
3603
|
});
|
|
2064
3604
|
const submit = () => {
|
|
@@ -2113,7 +3653,7 @@ var ColorControl = defineComponent11({
|
|
|
2113
3653
|
});
|
|
2114
3654
|
|
|
2115
3655
|
// src/vue/components/PresetManager.ts
|
|
2116
|
-
import { Teleport as Teleport2, defineComponent as defineComponent12, h as h12, ref as ref10, watch as
|
|
3656
|
+
import { Teleport as Teleport2, defineComponent as defineComponent12, h as h12, ref as ref10, watch as watch7 } from "vue";
|
|
2117
3657
|
import { AnimatePresence as AnimatePresence3, motion as motion3 } from "motion-v";
|
|
2118
3658
|
var PresetManager = defineComponent12({
|
|
2119
3659
|
name: "DialKitPresetManager",
|
|
@@ -2163,7 +3703,7 @@ var PresetManager = defineComponent12({
|
|
|
2163
3703
|
if (isOpen.value) close();
|
|
2164
3704
|
else open();
|
|
2165
3705
|
};
|
|
2166
|
-
|
|
3706
|
+
watch7(isOpen, (open2, _, onCleanup) => {
|
|
2167
3707
|
if (!open2) return;
|
|
2168
3708
|
const handler = (event) => {
|
|
2169
3709
|
const target = event.target;
|
|
@@ -2261,7 +3801,7 @@ var PresetManager = defineComponent12({
|
|
|
2261
3801
|
});
|
|
2262
3802
|
|
|
2263
3803
|
// src/vue/components/ShortcutListener.ts
|
|
2264
|
-
import { defineComponent as defineComponent13, inject, onMounted as
|
|
3804
|
+
import { defineComponent as defineComponent13, inject, onMounted as onMounted9, onUnmounted as onUnmounted8, provide, ref as ref11 } from "vue";
|
|
2265
3805
|
var ShortcutKey = /* @__PURE__ */ Symbol("DialKitShortcut");
|
|
2266
3806
|
function useShortcutContext() {
|
|
2267
3807
|
return inject(ShortcutKey, {
|
|
@@ -2441,7 +3981,7 @@ var ShortcutListener = defineComponent13({
|
|
|
2441
3981
|
activePanelId.value = null;
|
|
2442
3982
|
activePath.value = null;
|
|
2443
3983
|
};
|
|
2444
|
-
|
|
3984
|
+
onMounted9(() => {
|
|
2445
3985
|
window.addEventListener("keydown", handleKeyDown);
|
|
2446
3986
|
window.addEventListener("keyup", handleKeyUp);
|
|
2447
3987
|
window.addEventListener("wheel", handleWheel, { passive: false });
|
|
@@ -2450,7 +3990,7 @@ var ShortcutListener = defineComponent13({
|
|
|
2450
3990
|
window.addEventListener("mousemove", handleMouseMove);
|
|
2451
3991
|
window.addEventListener("blur", handleWindowBlur);
|
|
2452
3992
|
});
|
|
2453
|
-
|
|
3993
|
+
onUnmounted8(() => {
|
|
2454
3994
|
window.removeEventListener("keydown", handleKeyDown);
|
|
2455
3995
|
window.removeEventListener("keyup", handleKeyUp);
|
|
2456
3996
|
window.removeEventListener("wheel", handleWheel);
|
|
@@ -2478,9 +4018,15 @@ var Panel = defineComponent14({
|
|
|
2478
4018
|
inline: {
|
|
2479
4019
|
type: Boolean,
|
|
2480
4020
|
default: false
|
|
2481
|
-
}
|
|
4021
|
+
},
|
|
4022
|
+
variant: {
|
|
4023
|
+
type: String,
|
|
4024
|
+
default: "root"
|
|
4025
|
+
},
|
|
4026
|
+
toolbarExtra: Function
|
|
2482
4027
|
},
|
|
2483
|
-
|
|
4028
|
+
emits: ["openChange"],
|
|
4029
|
+
setup(props, { emit }) {
|
|
2484
4030
|
const shortcutCtx = useShortcutContext();
|
|
2485
4031
|
const values = ref12(DialStore.getValues(props.panel.id));
|
|
2486
4032
|
const presets = ref12(DialStore.getPresets(props.panel.id));
|
|
@@ -2489,14 +4035,14 @@ var Panel = defineComponent14({
|
|
|
2489
4035
|
const hasShortcuts = () => Object.keys(DialStore.getPanel(props.panel.id)?.shortcuts ?? {}).length > 0;
|
|
2490
4036
|
let unsubscribe;
|
|
2491
4037
|
let copiedTimeout = null;
|
|
2492
|
-
|
|
4038
|
+
onMounted10(() => {
|
|
2493
4039
|
unsubscribe = DialStore.subscribe(props.panel.id, () => {
|
|
2494
4040
|
values.value = DialStore.getValues(props.panel.id);
|
|
2495
4041
|
presets.value = DialStore.getPresets(props.panel.id);
|
|
2496
4042
|
activePresetId.value = DialStore.getActivePresetId(props.panel.id);
|
|
2497
4043
|
});
|
|
2498
4044
|
});
|
|
2499
|
-
|
|
4045
|
+
onUnmounted9(() => {
|
|
2500
4046
|
unsubscribe?.();
|
|
2501
4047
|
if (copiedTimeout) {
|
|
2502
4048
|
window.clearTimeout(copiedTimeout);
|
|
@@ -2529,6 +4075,9 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2529
4075
|
copied.value = false;
|
|
2530
4076
|
}, 1500);
|
|
2531
4077
|
};
|
|
4078
|
+
const handleOpenChange = (open) => {
|
|
4079
|
+
emit("openChange", open);
|
|
4080
|
+
};
|
|
2532
4081
|
const renderControl = (control) => {
|
|
2533
4082
|
const value = values.value[control.path];
|
|
2534
4083
|
switch (control.type) {
|
|
@@ -2695,15 +4244,32 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2695
4244
|
})
|
|
2696
4245
|
]),
|
|
2697
4246
|
"Copy"
|
|
2698
|
-
])
|
|
4247
|
+
]),
|
|
4248
|
+
props.toolbarExtra?.()
|
|
2699
4249
|
]);
|
|
4250
|
+
if (props.variant === "section") {
|
|
4251
|
+
return h14(Folder, {
|
|
4252
|
+
title: props.panel.name,
|
|
4253
|
+
defaultOpen: props.defaultOpen,
|
|
4254
|
+
onOpenChange: handleOpenChange
|
|
4255
|
+
}, {
|
|
4256
|
+
default: () => [
|
|
4257
|
+
h14("div", {
|
|
4258
|
+
class: "dialkit-panel-section-toolbar",
|
|
4259
|
+
onClick: (event) => event.stopPropagation()
|
|
4260
|
+
}, [toolbarNode]),
|
|
4261
|
+
...props.panel.controls.map(renderControl)
|
|
4262
|
+
]
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
2700
4265
|
return h14("div", { class: "dialkit-panel-wrapper" }, [
|
|
2701
4266
|
h14(Folder, {
|
|
2702
4267
|
title: props.panel.name,
|
|
2703
4268
|
defaultOpen: props.defaultOpen,
|
|
2704
4269
|
isRoot: true,
|
|
2705
4270
|
inline: props.inline,
|
|
2706
|
-
toolbar: () => toolbarNode
|
|
4271
|
+
toolbar: () => toolbarNode,
|
|
4272
|
+
onOpenChange: handleOpenChange
|
|
2707
4273
|
}, {
|
|
2708
4274
|
default: () => props.panel.controls.map(renderControl)
|
|
2709
4275
|
})
|
|
@@ -2712,9 +4278,106 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
2712
4278
|
}
|
|
2713
4279
|
});
|
|
2714
4280
|
|
|
4281
|
+
// src/vue/components/Timeline/TimelineToggleButton.ts
|
|
4282
|
+
import { defineComponent as defineComponent15, h as h15, onMounted as onMounted11, onUnmounted as onUnmounted10, ref as ref13 } from "vue";
|
|
4283
|
+
var TimelineToggleButton = defineComponent15({
|
|
4284
|
+
name: "DialKitTimelineToggleButton",
|
|
4285
|
+
setup() {
|
|
4286
|
+
const visible = ref13(TimelineUiStore.getVisible());
|
|
4287
|
+
let unsubscribe;
|
|
4288
|
+
onMounted11(() => {
|
|
4289
|
+
unsubscribe = TimelineUiStore.subscribe(() => {
|
|
4290
|
+
visible.value = TimelineUiStore.getVisible();
|
|
4291
|
+
});
|
|
4292
|
+
});
|
|
4293
|
+
onUnmounted10(() => unsubscribe?.());
|
|
4294
|
+
return () => {
|
|
4295
|
+
const label = visible.value ? "Hide timeline" : "Show timeline";
|
|
4296
|
+
return h15("button", {
|
|
4297
|
+
class: "dialkit-toolbar-add dialkit-timeline-toolbar-toggle",
|
|
4298
|
+
"data-active": visible.value || void 0,
|
|
4299
|
+
"aria-pressed": visible.value,
|
|
4300
|
+
"aria-label": label,
|
|
4301
|
+
title: label,
|
|
4302
|
+
onClick: () => TimelineUiStore.toggle()
|
|
4303
|
+
}, [
|
|
4304
|
+
h15(
|
|
4305
|
+
"svg",
|
|
4306
|
+
{ viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" },
|
|
4307
|
+
ICON_TIMELINE.map((path) => h15("path", { d: path, fill: "currentColor" }))
|
|
4308
|
+
)
|
|
4309
|
+
]);
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
});
|
|
4313
|
+
|
|
4314
|
+
// src/panel-drag.ts
|
|
4315
|
+
var PANEL_DRAG_THRESHOLD = 8;
|
|
4316
|
+
var COLLAPSED_PANEL_SIZE = 42;
|
|
4317
|
+
var DRAG_EXCLUSION_SELECTOR = [
|
|
4318
|
+
".dialkit-panel-icon",
|
|
4319
|
+
".dialkit-panel-toolbar",
|
|
4320
|
+
"button",
|
|
4321
|
+
"input",
|
|
4322
|
+
"select",
|
|
4323
|
+
"textarea",
|
|
4324
|
+
"a",
|
|
4325
|
+
'[role="button"]',
|
|
4326
|
+
'[contenteditable="true"]'
|
|
4327
|
+
].join(",");
|
|
4328
|
+
function getPanelDragHandle(target, panel) {
|
|
4329
|
+
if (!(target instanceof Element) || !panel) return null;
|
|
4330
|
+
const inner = target.closest(".dialkit-panel-inner");
|
|
4331
|
+
if (!inner || !panel.contains(inner)) return null;
|
|
4332
|
+
if (inner.getAttribute("data-collapsed") === "true") {
|
|
4333
|
+
return inner;
|
|
4334
|
+
}
|
|
4335
|
+
const header = target.closest(".dialkit-panel-header");
|
|
4336
|
+
if (!header || !inner.contains(header)) return null;
|
|
4337
|
+
if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;
|
|
4338
|
+
return header;
|
|
4339
|
+
}
|
|
4340
|
+
function getPanelDragStart(pointerX, pointerY, panel) {
|
|
4341
|
+
const rect = panel.getBoundingClientRect();
|
|
4342
|
+
return {
|
|
4343
|
+
pointerX,
|
|
4344
|
+
pointerY,
|
|
4345
|
+
elX: rect.left,
|
|
4346
|
+
elY: rect.top
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
function getPanelDragOffset(start, pointerX, pointerY) {
|
|
4350
|
+
return {
|
|
4351
|
+
x: start.elX + pointerX - start.pointerX,
|
|
4352
|
+
y: start.elY + pointerY - start.pointerY
|
|
4353
|
+
};
|
|
4354
|
+
}
|
|
4355
|
+
function hasPanelDragMoved(start, pointerX, pointerY) {
|
|
4356
|
+
const dx = pointerX - start.pointerX;
|
|
4357
|
+
const dy = pointerY - start.pointerY;
|
|
4358
|
+
return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;
|
|
4359
|
+
}
|
|
4360
|
+
function getPanelOriginX(position, offset, viewportWidth = typeof window !== "undefined" ? window.innerWidth : void 0) {
|
|
4361
|
+
if (offset && viewportWidth) {
|
|
4362
|
+
return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? "left" : "right";
|
|
4363
|
+
}
|
|
4364
|
+
return position.endsWith("left") ? "left" : "right";
|
|
4365
|
+
}
|
|
4366
|
+
function blockPanelDragClick(handle) {
|
|
4367
|
+
const blocker = (event) => {
|
|
4368
|
+
event.preventDefault();
|
|
4369
|
+
event.stopImmediatePropagation();
|
|
4370
|
+
event.stopPropagation();
|
|
4371
|
+
};
|
|
4372
|
+
handle.addEventListener("click", blocker, { capture: true, once: true });
|
|
4373
|
+
window.setTimeout(() => {
|
|
4374
|
+
handle.removeEventListener("click", blocker, true);
|
|
4375
|
+
}, 0);
|
|
4376
|
+
}
|
|
4377
|
+
|
|
2715
4378
|
// src/vue/components/DialRoot.ts
|
|
2716
|
-
var
|
|
2717
|
-
var DialRoot =
|
|
4379
|
+
var isDevDefault2 = 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;
|
|
4380
|
+
var DialRoot = defineComponent16({
|
|
2718
4381
|
name: "DialKitDialRoot",
|
|
2719
4382
|
props: {
|
|
2720
4383
|
position: {
|
|
@@ -2735,45 +4398,203 @@ var DialRoot = defineComponent15({
|
|
|
2735
4398
|
},
|
|
2736
4399
|
productionEnabled: {
|
|
2737
4400
|
type: Boolean,
|
|
2738
|
-
default:
|
|
4401
|
+
default: isDevDefault2
|
|
2739
4402
|
}
|
|
2740
4403
|
},
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
const
|
|
2744
|
-
|
|
2745
|
-
|
|
4404
|
+
emits: ["openChange"],
|
|
4405
|
+
setup(props, { emit }) {
|
|
4406
|
+
const panels = ref14([]);
|
|
4407
|
+
const timelines = ref14([]);
|
|
4408
|
+
const mounted = ref14(false);
|
|
4409
|
+
const panelRef = ref14(null);
|
|
4410
|
+
const dragOffset = ref14(null);
|
|
4411
|
+
const activePosition = ref14(props.position);
|
|
4412
|
+
let unsubscribePanels;
|
|
4413
|
+
let unsubscribeTimelines;
|
|
4414
|
+
let observer;
|
|
4415
|
+
let lastDragOffset = null;
|
|
4416
|
+
let dragging = false;
|
|
4417
|
+
let dragStart = null;
|
|
4418
|
+
let didDrag = false;
|
|
4419
|
+
let dragTarget = null;
|
|
4420
|
+
let panelOpenStates = /* @__PURE__ */ new Map();
|
|
4421
|
+
let rootOpen;
|
|
4422
|
+
const syncPanelOpenStates = () => {
|
|
4423
|
+
const fallbackOpen = props.mode === "inline" || props.defaultOpen;
|
|
4424
|
+
const nextStates = /* @__PURE__ */ new Map();
|
|
4425
|
+
for (const panel of panels.value) {
|
|
4426
|
+
nextStates.set(panel.id, panelOpenStates.get(panel.id) ?? fallbackOpen);
|
|
4427
|
+
}
|
|
4428
|
+
panelOpenStates = nextStates;
|
|
4429
|
+
rootOpen = Array.from(nextStates.values()).some(Boolean);
|
|
4430
|
+
};
|
|
4431
|
+
const connectObserver = () => {
|
|
4432
|
+
if (observer || props.mode === "inline" || !panelRef.value) return;
|
|
4433
|
+
observer = new MutationObserver(() => {
|
|
4434
|
+
const inners = panelRef.value?.querySelectorAll(".dialkit-panel-inner");
|
|
4435
|
+
if (!inners || inners.length === 0) return;
|
|
4436
|
+
const collapsed = Array.from(inners).every((el) => el.getAttribute("data-collapsed") === "true");
|
|
4437
|
+
const currentDragOffset = dragOffset.value;
|
|
4438
|
+
if (!collapsed) {
|
|
4439
|
+
if (currentDragOffset) {
|
|
4440
|
+
lastDragOffset = currentDragOffset;
|
|
4441
|
+
const bubbleCenterX = currentDragOffset.x + 21;
|
|
4442
|
+
activePosition.value = bubbleCenterX < window.innerWidth / 2 ? "top-left" : "top-right";
|
|
4443
|
+
} else {
|
|
4444
|
+
activePosition.value = props.position;
|
|
4445
|
+
}
|
|
4446
|
+
dragOffset.value = null;
|
|
4447
|
+
} else if (currentDragOffset) {
|
|
4448
|
+
lastDragOffset = currentDragOffset;
|
|
4449
|
+
} else if (lastDragOffset) {
|
|
4450
|
+
dragOffset.value = lastDragOffset;
|
|
4451
|
+
}
|
|
4452
|
+
});
|
|
4453
|
+
observer.observe(panelRef.value, { subtree: true, attributes: true, attributeFilter: ["data-collapsed"] });
|
|
4454
|
+
};
|
|
4455
|
+
const handlePointerDown = (event) => {
|
|
4456
|
+
const panel = panelRef.value;
|
|
4457
|
+
const handle = getPanelDragHandle(event.target, panel);
|
|
4458
|
+
if (!panel || !handle) return;
|
|
4459
|
+
dragTarget = handle;
|
|
4460
|
+
dragStart = getPanelDragStart(event.clientX, event.clientY, panel);
|
|
4461
|
+
didDrag = false;
|
|
4462
|
+
dragging = true;
|
|
4463
|
+
handle.setPointerCapture(event.pointerId);
|
|
4464
|
+
};
|
|
4465
|
+
const handlePointerMove = (event) => {
|
|
4466
|
+
if (!dragging || !dragStart) return;
|
|
4467
|
+
if (!didDrag && !hasPanelDragMoved(dragStart, event.clientX, event.clientY)) return;
|
|
4468
|
+
didDrag = true;
|
|
4469
|
+
dragOffset.value = getPanelDragOffset(dragStart, event.clientX, event.clientY);
|
|
4470
|
+
};
|
|
4471
|
+
const handlePointerUp = (event) => {
|
|
4472
|
+
if (!dragging) return;
|
|
4473
|
+
dragging = false;
|
|
4474
|
+
dragStart = null;
|
|
4475
|
+
const handle = dragTarget;
|
|
4476
|
+
if (handle?.hasPointerCapture(event.pointerId)) {
|
|
4477
|
+
handle.releasePointerCapture(event.pointerId);
|
|
4478
|
+
}
|
|
4479
|
+
if (didDrag) {
|
|
4480
|
+
event.stopPropagation();
|
|
4481
|
+
if (handle) {
|
|
4482
|
+
blockPanelDragClick(handle);
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
dragTarget = null;
|
|
4486
|
+
};
|
|
4487
|
+
const handlePanelOpenChange = (panelId, open) => {
|
|
4488
|
+
panelOpenStates.set(panelId, open);
|
|
4489
|
+
const fallbackOpen = props.mode === "inline" || props.defaultOpen;
|
|
4490
|
+
const nextRootOpen = panels.value.some((panel) => panelOpenStates.get(panel.id) ?? fallbackOpen);
|
|
4491
|
+
if (rootOpen === nextRootOpen) return;
|
|
4492
|
+
rootOpen = nextRootOpen;
|
|
4493
|
+
emit("openChange", nextRootOpen);
|
|
4494
|
+
};
|
|
4495
|
+
const handleRootOpenChange = (open) => {
|
|
4496
|
+
if (rootOpen === open) return;
|
|
4497
|
+
rootOpen = open;
|
|
4498
|
+
emit("openChange", open);
|
|
4499
|
+
};
|
|
4500
|
+
const getDragStyle = () => dragOffset.value ? {
|
|
4501
|
+
top: `${dragOffset.value.y}px`,
|
|
4502
|
+
left: `${dragOffset.value.x}px`,
|
|
4503
|
+
right: "auto",
|
|
4504
|
+
bottom: "auto"
|
|
4505
|
+
} : void 0;
|
|
4506
|
+
const originX = computed6(() => props.mode === "inline" ? void 0 : getPanelOriginX(activePosition.value, dragOffset.value));
|
|
4507
|
+
onMounted12(() => {
|
|
2746
4508
|
mounted.value = true;
|
|
2747
|
-
panels.value = DialStore.getPanels();
|
|
2748
|
-
|
|
2749
|
-
|
|
4509
|
+
panels.value = DialStore.getPanels("panel");
|
|
4510
|
+
timelines.value = TimelineStore.getTimelines();
|
|
4511
|
+
syncPanelOpenStates();
|
|
4512
|
+
unsubscribePanels = DialStore.subscribeGlobal(() => {
|
|
4513
|
+
panels.value = DialStore.getPanels("panel");
|
|
4514
|
+
syncPanelOpenStates();
|
|
2750
4515
|
});
|
|
4516
|
+
unsubscribeTimelines = TimelineStore.subscribeGlobal(() => {
|
|
4517
|
+
timelines.value = TimelineStore.getTimelines();
|
|
4518
|
+
});
|
|
4519
|
+
nextTick3(connectObserver);
|
|
2751
4520
|
});
|
|
2752
|
-
|
|
2753
|
-
|
|
4521
|
+
onUnmounted11(() => {
|
|
4522
|
+
unsubscribePanels?.();
|
|
4523
|
+
unsubscribeTimelines?.();
|
|
4524
|
+
observer?.disconnect();
|
|
2754
4525
|
});
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
|
|
4526
|
+
const timelineToggle = () => timelines.value.length > 0 ? h16(TimelineToggleButton) : null;
|
|
4527
|
+
const renderPanels = () => {
|
|
4528
|
+
if (panels.value.length === 0) {
|
|
4529
|
+
return [h16("div", { class: "dialkit-panel-wrapper" }, [
|
|
4530
|
+
h16(Folder, {
|
|
4531
|
+
title: "DialKit",
|
|
4532
|
+
defaultOpen: props.mode === "inline" || props.defaultOpen,
|
|
4533
|
+
isRoot: true,
|
|
4534
|
+
inline: props.mode === "inline",
|
|
4535
|
+
toolbar: timelineToggle,
|
|
4536
|
+
onOpenChange: handleRootOpenChange,
|
|
4537
|
+
panelHeightOffset: 2
|
|
4538
|
+
}, { default: () => [h16("div", { class: "dialkit-timeline-toolkit-only" }, "Timeline")] })
|
|
4539
|
+
])];
|
|
4540
|
+
}
|
|
4541
|
+
if (panels.value.length > 1) {
|
|
4542
|
+
return [h16("div", { class: "dialkit-panel-wrapper" }, [
|
|
4543
|
+
h16(Folder, {
|
|
4544
|
+
title: "DialKit",
|
|
4545
|
+
defaultOpen: props.mode === "inline" || props.defaultOpen,
|
|
4546
|
+
isRoot: true,
|
|
4547
|
+
inline: props.mode === "inline",
|
|
4548
|
+
toolbar: timelineToggle,
|
|
4549
|
+
onOpenChange: handleRootOpenChange,
|
|
4550
|
+
panelHeightOffset: 2
|
|
4551
|
+
}, {
|
|
4552
|
+
default: () => panels.value.map((panel) => h16(Panel, {
|
|
4553
|
+
key: panel.id,
|
|
4554
|
+
panel,
|
|
4555
|
+
defaultOpen: true,
|
|
4556
|
+
variant: "section"
|
|
4557
|
+
}))
|
|
4558
|
+
})
|
|
4559
|
+
])];
|
|
4560
|
+
}
|
|
4561
|
+
return panels.value.map((panel) => h16(Panel, {
|
|
4562
|
+
key: panel.id,
|
|
4563
|
+
panel,
|
|
4564
|
+
defaultOpen: props.mode === "inline" || props.defaultOpen,
|
|
4565
|
+
inline: props.mode === "inline",
|
|
4566
|
+
toolbarExtra: timelineToggle,
|
|
4567
|
+
onOpenChange: (open) => handlePanelOpenChange(panel.id, open)
|
|
4568
|
+
}));
|
|
4569
|
+
};
|
|
4570
|
+
const renderContent = () => h16(ShortcutListener, null, {
|
|
4571
|
+
default: () => h16("div", { class: "dialkit-root", "data-mode": props.mode, "data-theme": props.theme }, [
|
|
4572
|
+
h16("div", {
|
|
4573
|
+
ref: (el) => {
|
|
4574
|
+
panelRef.value = el;
|
|
4575
|
+
connectObserver();
|
|
4576
|
+
},
|
|
2758
4577
|
class: "dialkit-panel",
|
|
2759
|
-
"data-position": props.mode === "inline" ? void 0 :
|
|
2760
|
-
"data-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
4578
|
+
"data-position": props.mode === "inline" ? void 0 : dragOffset.value ? void 0 : activePosition.value,
|
|
4579
|
+
"data-origin-x": originX.value,
|
|
4580
|
+
"data-mode": props.mode,
|
|
4581
|
+
"data-multiple": panels.value.length > 1 ? "true" : void 0,
|
|
4582
|
+
style: getDragStyle(),
|
|
4583
|
+
onPointerdown: props.mode === "inline" ? void 0 : handlePointerDown,
|
|
4584
|
+
onPointermove: props.mode === "inline" ? void 0 : handlePointerMove,
|
|
4585
|
+
onPointerup: props.mode === "inline" ? void 0 : handlePointerUp,
|
|
4586
|
+
onPointercancel: props.mode === "inline" ? void 0 : handlePointerUp
|
|
4587
|
+
}, renderPanels())
|
|
2767
4588
|
])
|
|
2768
4589
|
});
|
|
2769
4590
|
return () => {
|
|
2770
|
-
if (!props.productionEnabled || !mounted.value || typeof window === "undefined" || panels.value.length === 0) {
|
|
4591
|
+
if (!props.productionEnabled || !mounted.value || typeof window === "undefined" || panels.value.length === 0 && timelines.value.length === 0) {
|
|
2771
4592
|
return null;
|
|
2772
4593
|
}
|
|
2773
4594
|
if (props.mode === "inline") {
|
|
2774
4595
|
return renderContent();
|
|
2775
4596
|
}
|
|
2776
|
-
return
|
|
4597
|
+
return h16(Teleport3, { to: "body" }, renderContent());
|
|
2777
4598
|
};
|
|
2778
4599
|
}
|
|
2779
4600
|
});
|
|
@@ -2791,11 +4612,11 @@ function mountDialRoot(el, value) {
|
|
|
2791
4612
|
if (typeof window === "undefined") return;
|
|
2792
4613
|
const host = document.createElement("div");
|
|
2793
4614
|
el.appendChild(host);
|
|
2794
|
-
const props =
|
|
2795
|
-
const RootHost =
|
|
4615
|
+
const props = shallowRef3(normalizeDirectiveValue(value));
|
|
4616
|
+
const RootHost = defineComponent17({
|
|
2796
4617
|
name: "DialKitDirectiveHost",
|
|
2797
4618
|
setup() {
|
|
2798
|
-
return () =>
|
|
4619
|
+
return () => h17(DialRoot, props.value);
|
|
2799
4620
|
}
|
|
2800
4621
|
});
|
|
2801
4622
|
const app = createApp(RootHost);
|
|
@@ -2826,8 +4647,1129 @@ var vDialKit = {
|
|
|
2826
4647
|
}
|
|
2827
4648
|
};
|
|
2828
4649
|
|
|
4650
|
+
// src/vue/components/Timeline/DialTimeline.ts
|
|
4651
|
+
import {
|
|
4652
|
+
Teleport as Teleport4,
|
|
4653
|
+
computed as computed7,
|
|
4654
|
+
defineComponent as defineComponent19,
|
|
4655
|
+
h as h19,
|
|
4656
|
+
nextTick as nextTick4,
|
|
4657
|
+
onMounted as onMounted13,
|
|
4658
|
+
onUnmounted as onUnmounted12,
|
|
4659
|
+
ref as ref15,
|
|
4660
|
+
watch as watch8
|
|
4661
|
+
} from "vue";
|
|
4662
|
+
|
|
4663
|
+
// src/vue/components/ControlRenderer.ts
|
|
4664
|
+
import { Fragment as Fragment2, defineComponent as defineComponent18, h as h18 } from "vue";
|
|
4665
|
+
import { inject as inject2 } from "vue";
|
|
4666
|
+
var ControlRenderer = defineComponent18({
|
|
4667
|
+
name: "DialKitControlRenderer",
|
|
4668
|
+
props: {
|
|
4669
|
+
panelId: { type: String, required: true },
|
|
4670
|
+
controls: { type: Array, required: true },
|
|
4671
|
+
values: { type: Object, required: true },
|
|
4672
|
+
transitionDuration: Object
|
|
4673
|
+
},
|
|
4674
|
+
setup(props) {
|
|
4675
|
+
const shortcut = inject2(ShortcutKey, void 0);
|
|
4676
|
+
const renderControl = (control) => {
|
|
4677
|
+
const value = props.values[control.path];
|
|
4678
|
+
switch (control.type) {
|
|
4679
|
+
case "slider":
|
|
4680
|
+
return h18(Slider, {
|
|
4681
|
+
key: control.path,
|
|
4682
|
+
label: control.label,
|
|
4683
|
+
value,
|
|
4684
|
+
min: control.min,
|
|
4685
|
+
max: control.max,
|
|
4686
|
+
step: control.step,
|
|
4687
|
+
shortcut: control.shortcut,
|
|
4688
|
+
shortcutActive: shortcut?.activePanelId.value === props.panelId && shortcut.activePath.value === control.path,
|
|
4689
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4690
|
+
});
|
|
4691
|
+
case "toggle":
|
|
4692
|
+
return h18(Toggle, {
|
|
4693
|
+
key: control.path,
|
|
4694
|
+
label: control.label,
|
|
4695
|
+
checked: value,
|
|
4696
|
+
shortcut: control.shortcut,
|
|
4697
|
+
shortcutActive: shortcut?.activePanelId.value === props.panelId && shortcut.activePath.value === control.path,
|
|
4698
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4699
|
+
});
|
|
4700
|
+
case "spring":
|
|
4701
|
+
return h18(SpringControl, {
|
|
4702
|
+
key: control.path,
|
|
4703
|
+
panelId: props.panelId,
|
|
4704
|
+
path: control.path,
|
|
4705
|
+
label: control.label,
|
|
4706
|
+
spring: value,
|
|
4707
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4708
|
+
});
|
|
4709
|
+
case "transition":
|
|
4710
|
+
return h18(TransitionControl, {
|
|
4711
|
+
key: control.path,
|
|
4712
|
+
panelId: props.panelId,
|
|
4713
|
+
path: control.path,
|
|
4714
|
+
label: control.label,
|
|
4715
|
+
value,
|
|
4716
|
+
durationControl: props.transitionDuration,
|
|
4717
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4718
|
+
});
|
|
4719
|
+
case "folder":
|
|
4720
|
+
return h18(Folder, {
|
|
4721
|
+
key: control.path,
|
|
4722
|
+
title: control.label,
|
|
4723
|
+
defaultOpen: control.defaultOpen ?? true
|
|
4724
|
+
}, { default: () => (control.children ?? []).map(renderControl) });
|
|
4725
|
+
case "text":
|
|
4726
|
+
return h18(TextControl, {
|
|
4727
|
+
key: control.path,
|
|
4728
|
+
label: control.label,
|
|
4729
|
+
value,
|
|
4730
|
+
placeholder: control.placeholder,
|
|
4731
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4732
|
+
});
|
|
4733
|
+
case "select":
|
|
4734
|
+
return h18(SelectControl, {
|
|
4735
|
+
key: control.path,
|
|
4736
|
+
label: control.label,
|
|
4737
|
+
value,
|
|
4738
|
+
options: control.options ?? [],
|
|
4739
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4740
|
+
});
|
|
4741
|
+
case "color":
|
|
4742
|
+
return h18(ColorControl, {
|
|
4743
|
+
key: control.path,
|
|
4744
|
+
label: control.label,
|
|
4745
|
+
value,
|
|
4746
|
+
onChange: (next) => DialStore.updateValue(props.panelId, control.path, next)
|
|
4747
|
+
});
|
|
4748
|
+
case "action":
|
|
4749
|
+
return h18("button", {
|
|
4750
|
+
key: control.path,
|
|
4751
|
+
class: "dialkit-button",
|
|
4752
|
+
onClick: () => DialStore.triggerAction(props.panelId, control.path)
|
|
4753
|
+
}, control.label);
|
|
4754
|
+
default:
|
|
4755
|
+
return null;
|
|
4756
|
+
}
|
|
4757
|
+
};
|
|
4758
|
+
return () => h18(Fragment2, null, props.controls.map(renderControl));
|
|
4759
|
+
}
|
|
4760
|
+
});
|
|
4761
|
+
|
|
4762
|
+
// src/vue/components/Timeline/DialTimeline.ts
|
|
4763
|
+
var DRAG_THRESHOLD_PX = 3;
|
|
4764
|
+
var MAJOR_TICK_TARGET_PX = 140;
|
|
4765
|
+
var MILLISECOND_STEP = 1e-3;
|
|
4766
|
+
var SECOND_TICK_STEPS = [
|
|
4767
|
+
1e-3,
|
|
4768
|
+
2e-3,
|
|
4769
|
+
5e-3,
|
|
4770
|
+
0.01,
|
|
4771
|
+
0.02,
|
|
4772
|
+
0.05,
|
|
4773
|
+
0.1,
|
|
4774
|
+
0.2,
|
|
4775
|
+
0.5,
|
|
4776
|
+
1,
|
|
4777
|
+
2,
|
|
4778
|
+
5,
|
|
4779
|
+
10,
|
|
4780
|
+
15,
|
|
4781
|
+
30,
|
|
4782
|
+
60,
|
|
4783
|
+
120,
|
|
4784
|
+
300,
|
|
4785
|
+
600
|
|
4786
|
+
];
|
|
4787
|
+
var MIN_TIMELINE_MAX_ZOOM = 8;
|
|
4788
|
+
var PLAYHEAD_FLAG_WIDTH = 52;
|
|
4789
|
+
var POPOVER_WIDTH = 280;
|
|
4790
|
+
var ZOOM_DRAG_DISTANCE = 180;
|
|
4791
|
+
var DialTimeline = defineComponent19({
|
|
4792
|
+
name: "DialKitTimeline",
|
|
4793
|
+
props: {
|
|
4794
|
+
theme: { type: String, default: "system" },
|
|
4795
|
+
defaultVisible: { type: Boolean, default: true },
|
|
4796
|
+
visible: {
|
|
4797
|
+
type: Boolean,
|
|
4798
|
+
default: void 0
|
|
4799
|
+
},
|
|
4800
|
+
onVisibilityChange: Function,
|
|
4801
|
+
defaultOpen: { type: Boolean, default: true },
|
|
4802
|
+
productionEnabled: { type: Boolean, default: isDevDefault }
|
|
4803
|
+
},
|
|
4804
|
+
setup(props) {
|
|
4805
|
+
const timelines = ref15(TimelineStore.getTimelines());
|
|
4806
|
+
const dockVisible = ref15(TimelineUiStore.getVisible());
|
|
4807
|
+
const mounted = ref15(false);
|
|
4808
|
+
const controllerId = /* @__PURE__ */ Symbol("dialkit-timeline-visibility");
|
|
4809
|
+
let unsubscribeTimelines;
|
|
4810
|
+
let unsubscribeVisibility;
|
|
4811
|
+
let unregisterController;
|
|
4812
|
+
onMounted13(() => {
|
|
4813
|
+
mounted.value = true;
|
|
4814
|
+
unsubscribeVisibility = TimelineUiStore.subscribe(() => {
|
|
4815
|
+
dockVisible.value = TimelineUiStore.getVisible();
|
|
4816
|
+
});
|
|
4817
|
+
unregisterController = TimelineUiStore.registerController(controllerId, {
|
|
4818
|
+
visible: props.visible,
|
|
4819
|
+
defaultVisible: props.defaultVisible,
|
|
4820
|
+
onVisibilityChange: props.onVisibilityChange
|
|
4821
|
+
});
|
|
4822
|
+
dockVisible.value = TimelineUiStore.getVisible();
|
|
4823
|
+
unsubscribeTimelines = TimelineStore.subscribeGlobal(() => {
|
|
4824
|
+
timelines.value = TimelineStore.getTimelines();
|
|
4825
|
+
});
|
|
4826
|
+
});
|
|
4827
|
+
watch8(() => [props.visible, props.defaultVisible, props.onVisibilityChange], () => {
|
|
4828
|
+
TimelineUiStore.updateController(controllerId, {
|
|
4829
|
+
visible: props.visible,
|
|
4830
|
+
defaultVisible: props.defaultVisible,
|
|
4831
|
+
onVisibilityChange: props.onVisibilityChange
|
|
4832
|
+
});
|
|
4833
|
+
});
|
|
4834
|
+
onUnmounted12(() => {
|
|
4835
|
+
unregisterController?.();
|
|
4836
|
+
unsubscribeTimelines?.();
|
|
4837
|
+
unsubscribeVisibility?.();
|
|
4838
|
+
});
|
|
4839
|
+
return () => {
|
|
4840
|
+
if (!props.productionEnabled || !mounted.value || timelines.value.length === 0) return null;
|
|
4841
|
+
return h19(Teleport4, { to: "body" }, [
|
|
4842
|
+
h19("div", {
|
|
4843
|
+
class: "dialkit-root dialkit-timeline",
|
|
4844
|
+
"data-theme": props.theme,
|
|
4845
|
+
hidden: !dockVisible.value
|
|
4846
|
+
}, [
|
|
4847
|
+
h19("div", { class: "dialkit-timeline-dock" }, timelines.value.map((meta) => h19(TimelineSection, {
|
|
4848
|
+
key: meta.id,
|
|
4849
|
+
meta,
|
|
4850
|
+
defaultOpen: props.defaultOpen,
|
|
4851
|
+
theme: props.theme,
|
|
4852
|
+
dockVisible: dockVisible.value
|
|
4853
|
+
})))
|
|
4854
|
+
])
|
|
4855
|
+
]);
|
|
4856
|
+
};
|
|
4857
|
+
}
|
|
4858
|
+
});
|
|
4859
|
+
var PlayPauseButton = defineComponent19({
|
|
4860
|
+
props: { id: { type: String, required: true } },
|
|
4861
|
+
setup(props) {
|
|
4862
|
+
const playing = ref15(TimelineStore.getTransport(props.id).playing);
|
|
4863
|
+
let unsubscribe;
|
|
4864
|
+
onMounted13(() => {
|
|
4865
|
+
unsubscribe = TimelineStore.subscribe(props.id, () => {
|
|
4866
|
+
playing.value = TimelineStore.getTransport(props.id).playing;
|
|
4867
|
+
});
|
|
4868
|
+
});
|
|
4869
|
+
onUnmounted12(() => unsubscribe?.());
|
|
4870
|
+
return () => {
|
|
4871
|
+
const label = playing.value ? "Pause" : "Play";
|
|
4872
|
+
const icon = playing.value ? h19(
|
|
4873
|
+
"svg",
|
|
4874
|
+
{ viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", style: iconStyle },
|
|
4875
|
+
ICON_PAUSE.map((path) => h19("path", { d: path, fill: "currentColor" }))
|
|
4876
|
+
) : h19("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", style: iconStyle }, [
|
|
4877
|
+
h19("path", { d: ICON_PLAY, fill: "currentColor" })
|
|
4878
|
+
]);
|
|
4879
|
+
return h19("button", {
|
|
4880
|
+
class: "dialkit-toolbar-add",
|
|
4881
|
+
title: label,
|
|
4882
|
+
"aria-label": label,
|
|
4883
|
+
onClick: () => playing.value ? TimelineStore.pause(props.id) : TimelineStore.play(props.id)
|
|
4884
|
+
}, [h19("span", { style: { position: "relative", width: "16px", height: "16px" } }, [icon])]);
|
|
4885
|
+
};
|
|
4886
|
+
}
|
|
4887
|
+
});
|
|
4888
|
+
var iconStyle = {
|
|
4889
|
+
position: "absolute",
|
|
4890
|
+
inset: 0,
|
|
4891
|
+
width: "16px",
|
|
4892
|
+
height: "16px",
|
|
4893
|
+
color: "var(--dial-text-label)"
|
|
4894
|
+
};
|
|
4895
|
+
var TimelineOverview = defineComponent19({
|
|
4896
|
+
props: {
|
|
4897
|
+
id: { type: String, required: true },
|
|
4898
|
+
duration: { type: Number, required: true },
|
|
4899
|
+
viewStart: { type: Number, required: true },
|
|
4900
|
+
viewEnd: { type: Number, required: true },
|
|
4901
|
+
onNavigate: { type: Function, required: true }
|
|
4902
|
+
},
|
|
4903
|
+
setup(props) {
|
|
4904
|
+
const time = ref15(TimelineStore.getTransport(props.id).time);
|
|
4905
|
+
let scrub = null;
|
|
4906
|
+
let unsubscribe;
|
|
4907
|
+
onMounted13(() => {
|
|
4908
|
+
unsubscribe = TimelineStore.subscribe(props.id, () => {
|
|
4909
|
+
time.value = TimelineStore.getTransport(props.id).time;
|
|
4910
|
+
});
|
|
4911
|
+
});
|
|
4912
|
+
onUnmounted12(() => unsubscribe?.());
|
|
4913
|
+
const seek = (clientX) => {
|
|
4914
|
+
if (!scrub || scrub.rect.width <= 0 || props.duration <= 0) return;
|
|
4915
|
+
const next = clamp((clientX - scrub.rect.left) / scrub.rect.width * props.duration, 0, props.duration);
|
|
4916
|
+
TimelineStore.seek(props.id, next);
|
|
4917
|
+
props.onNavigate(next);
|
|
4918
|
+
};
|
|
4919
|
+
const finish = () => {
|
|
4920
|
+
if (scrub?.wasPlaying) TimelineStore.play(props.id);
|
|
4921
|
+
scrub = null;
|
|
4922
|
+
};
|
|
4923
|
+
return () => {
|
|
4924
|
+
const viewportWidth = props.duration > 0 ? (props.viewEnd - props.viewStart) / props.duration * 100 : 100;
|
|
4925
|
+
const playhead = props.duration > 0 ? time.value / props.duration * 100 : 0;
|
|
4926
|
+
return h19("div", {
|
|
4927
|
+
class: "dialkit-timeline-overview",
|
|
4928
|
+
title: "Drag to scrub the full timeline",
|
|
4929
|
+
onPointerdown: (event) => {
|
|
4930
|
+
event.preventDefault();
|
|
4931
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
4932
|
+
scrub = {
|
|
4933
|
+
wasPlaying: TimelineStore.getTransport(props.id).playing,
|
|
4934
|
+
rect: event.currentTarget.getBoundingClientRect()
|
|
4935
|
+
};
|
|
4936
|
+
TimelineStore.pause(props.id);
|
|
4937
|
+
seek(event.clientX);
|
|
4938
|
+
},
|
|
4939
|
+
onPointermove: (event) => scrub && seek(event.clientX),
|
|
4940
|
+
onPointerup: finish,
|
|
4941
|
+
onPointercancel: finish,
|
|
4942
|
+
onLostpointercapture: finish
|
|
4943
|
+
}, [
|
|
4944
|
+
h19("div", {
|
|
4945
|
+
class: "dialkit-timeline-overview-viewport",
|
|
4946
|
+
"data-zoomed": viewportWidth < 99.999 || void 0,
|
|
4947
|
+
style: { left: `${props.duration > 0 ? props.viewStart / props.duration * 100 : 0}%`, width: `${viewportWidth}%` }
|
|
4948
|
+
}),
|
|
4949
|
+
h19("div", { class: "dialkit-timeline-overview-progress", style: { width: `${playhead}%` } }),
|
|
4950
|
+
h19("div", { class: "dialkit-timeline-overview-playhead", style: { left: `${playhead}%` } })
|
|
4951
|
+
]);
|
|
4952
|
+
};
|
|
4953
|
+
}
|
|
4954
|
+
});
|
|
4955
|
+
var TimelinePlayheadFlag = defineComponent19({
|
|
4956
|
+
props: {
|
|
4957
|
+
id: { type: String, required: true },
|
|
4958
|
+
duration: { type: Number, required: true },
|
|
4959
|
+
pxPerSecond: { type: Number, required: true },
|
|
4960
|
+
viewStart: { type: Number, required: true },
|
|
4961
|
+
viewEnd: { type: Number, required: true },
|
|
4962
|
+
laneWidth: { type: Number, required: true },
|
|
4963
|
+
ruler: Object,
|
|
4964
|
+
headerClearStart: { type: Number, required: true },
|
|
4965
|
+
headerClearEnd: { type: Number, required: true },
|
|
4966
|
+
onResetView: { type: Function, required: true }
|
|
4967
|
+
},
|
|
4968
|
+
setup(props) {
|
|
4969
|
+
const time = ref15(TimelineStore.getTransport(props.id).time);
|
|
4970
|
+
let unsubscribe;
|
|
4971
|
+
let scrub = null;
|
|
4972
|
+
let cleanup = null;
|
|
4973
|
+
onMounted13(() => {
|
|
4974
|
+
unsubscribe = TimelineStore.subscribe(props.id, () => {
|
|
4975
|
+
time.value = TimelineStore.getTransport(props.id).time;
|
|
4976
|
+
});
|
|
4977
|
+
});
|
|
4978
|
+
onUnmounted12(() => {
|
|
4979
|
+
unsubscribe?.();
|
|
4980
|
+
cleanup?.();
|
|
4981
|
+
});
|
|
4982
|
+
const seek = (clientX) => {
|
|
4983
|
+
if (!scrub || scrub.rect.width <= 0) return;
|
|
4984
|
+
TimelineStore.seek(props.id, clamp(
|
|
4985
|
+
scrub.viewStart + (clientX - scrub.rect.left) / scrub.rect.width * (scrub.viewEnd - scrub.viewStart),
|
|
4986
|
+
scrub.viewStart,
|
|
4987
|
+
scrub.viewEnd
|
|
4988
|
+
));
|
|
4989
|
+
};
|
|
4990
|
+
return () => {
|
|
4991
|
+
if (time.value < props.viewStart || time.value > props.viewEnd || props.laneWidth <= 0) return null;
|
|
4992
|
+
const x = clamp((time.value - props.viewStart) * props.pxPerSecond, 0, props.laneWidth);
|
|
4993
|
+
const left = x - PLAYHEAD_FLAG_WIDTH / 2;
|
|
4994
|
+
const placement = left >= props.headerClearStart && left + PLAYHEAD_FLAG_WIDTH <= props.headerClearEnd ? "raised" : "lowered";
|
|
4995
|
+
return h19("div", {
|
|
4996
|
+
class: "dialkit-timeline-playhead-control",
|
|
4997
|
+
"data-edge": "center",
|
|
4998
|
+
"data-placement": placement,
|
|
4999
|
+
style: { left: `calc(var(--dial-timeline-label-w) + ${x}px)` },
|
|
5000
|
+
role: "slider",
|
|
5001
|
+
"aria-label": "Timeline current time",
|
|
5002
|
+
"aria-valuemin": 0,
|
|
5003
|
+
"aria-valuemax": props.duration,
|
|
5004
|
+
"aria-valuenow": time.value,
|
|
5005
|
+
title: "Drag to scrub the timeline",
|
|
5006
|
+
onPointerdown: (event) => {
|
|
5007
|
+
const rect = props.ruler?.getBoundingClientRect();
|
|
5008
|
+
if (!rect) return;
|
|
5009
|
+
event.preventDefault();
|
|
5010
|
+
event.stopPropagation();
|
|
5011
|
+
cleanup?.();
|
|
5012
|
+
const reset = event.shiftKey;
|
|
5013
|
+
scrub = {
|
|
5014
|
+
wasPlaying: TimelineStore.getTransport(props.id).playing,
|
|
5015
|
+
rect,
|
|
5016
|
+
viewStart: reset ? 0 : props.viewStart,
|
|
5017
|
+
viewEnd: reset ? props.duration : props.viewEnd
|
|
5018
|
+
};
|
|
5019
|
+
if (reset) props.onResetView();
|
|
5020
|
+
TimelineStore.pause(props.id);
|
|
5021
|
+
seek(event.clientX);
|
|
5022
|
+
const move = (next) => {
|
|
5023
|
+
next.preventDefault();
|
|
5024
|
+
seek(next.clientX);
|
|
5025
|
+
};
|
|
5026
|
+
const finish = () => {
|
|
5027
|
+
window.removeEventListener("pointermove", move);
|
|
5028
|
+
window.removeEventListener("pointerup", finish);
|
|
5029
|
+
window.removeEventListener("pointercancel", finish);
|
|
5030
|
+
if (scrub?.wasPlaying) TimelineStore.play(props.id);
|
|
5031
|
+
scrub = null;
|
|
5032
|
+
cleanup = null;
|
|
5033
|
+
};
|
|
5034
|
+
window.addEventListener("pointermove", move, { passive: false });
|
|
5035
|
+
window.addEventListener("pointerup", finish);
|
|
5036
|
+
window.addEventListener("pointercancel", finish);
|
|
5037
|
+
cleanup = finish;
|
|
5038
|
+
}
|
|
5039
|
+
}, [
|
|
5040
|
+
h19("div", { class: "dialkit-timeline-playhead-flag" }, time.value.toFixed(2)),
|
|
5041
|
+
h19("div", { class: "dialkit-timeline-playhead-stem" })
|
|
5042
|
+
]);
|
|
5043
|
+
};
|
|
5044
|
+
}
|
|
5045
|
+
});
|
|
5046
|
+
function clampViewStart(start, duration, visibleDuration) {
|
|
5047
|
+
return clamp(start, 0, Math.max(0, duration - visibleDuration));
|
|
5048
|
+
}
|
|
5049
|
+
function formatRulerSeconds(time, step) {
|
|
5050
|
+
if (step >= 1 && Number.isInteger(time)) return formatClock(time);
|
|
5051
|
+
const decimals = Math.min(3, Math.max(1, Math.ceil(-Math.log10(step))));
|
|
5052
|
+
return `${time.toFixed(decimals)}s`;
|
|
5053
|
+
}
|
|
5054
|
+
var TimelineSection = defineComponent19({
|
|
5055
|
+
props: {
|
|
5056
|
+
meta: { type: Object, required: true },
|
|
5057
|
+
defaultOpen: { type: Boolean, required: true },
|
|
5058
|
+
theme: { type: String, required: true },
|
|
5059
|
+
dockVisible: { type: Boolean, required: true }
|
|
5060
|
+
},
|
|
5061
|
+
setup(props) {
|
|
5062
|
+
const open = ref15(props.defaultOpen);
|
|
5063
|
+
const copied = ref15(false);
|
|
5064
|
+
const popover = ref15(null);
|
|
5065
|
+
const collapsedGroups = ref15(/* @__PURE__ */ new Set());
|
|
5066
|
+
const expandedTracks = ref15(/* @__PURE__ */ new Set());
|
|
5067
|
+
const zoom = ref15(1);
|
|
5068
|
+
const viewStart = ref15(0);
|
|
5069
|
+
const values = ref15(DialStore.getValues(props.meta.id));
|
|
5070
|
+
const presets = ref15(DialStore.getPresets(props.meta.id));
|
|
5071
|
+
const activePresetId = ref15(DialStore.getActivePresetId(props.meta.id));
|
|
5072
|
+
const laneAreaRef = ref15(null);
|
|
5073
|
+
const titleRef = ref15(null);
|
|
5074
|
+
const actionsRef = ref15(null);
|
|
5075
|
+
const laneWidth = ref15(0);
|
|
5076
|
+
const flagClearRange = ref15({ start: 0, end: 0 });
|
|
5077
|
+
let unsubscribeValues;
|
|
5078
|
+
let resizeObserver;
|
|
5079
|
+
const measure = () => {
|
|
5080
|
+
if (!laneAreaRef.value || !titleRef.value || !actionsRef.value) return;
|
|
5081
|
+
const ruler = laneAreaRef.value.getBoundingClientRect();
|
|
5082
|
+
const title = titleRef.value.getBoundingClientRect();
|
|
5083
|
+
const actions = actionsRef.value.getBoundingClientRect();
|
|
5084
|
+
laneWidth.value = ruler.width;
|
|
5085
|
+
flagClearRange.value = {
|
|
5086
|
+
start: Math.round(title.right + 10 - ruler.left),
|
|
5087
|
+
end: Math.round(actions.left - 10 - ruler.left)
|
|
5088
|
+
};
|
|
5089
|
+
};
|
|
5090
|
+
const connectMeasure = async () => {
|
|
5091
|
+
resizeObserver?.disconnect();
|
|
5092
|
+
if (!open.value) return;
|
|
5093
|
+
await nextTick4();
|
|
5094
|
+
if (!laneAreaRef.value || !titleRef.value || !actionsRef.value) return;
|
|
5095
|
+
measure();
|
|
5096
|
+
resizeObserver = new ResizeObserver(measure);
|
|
5097
|
+
resizeObserver.observe(laneAreaRef.value);
|
|
5098
|
+
resizeObserver.observe(titleRef.value);
|
|
5099
|
+
resizeObserver.observe(actionsRef.value);
|
|
5100
|
+
};
|
|
5101
|
+
onMounted13(() => {
|
|
5102
|
+
unsubscribeValues = DialStore.subscribe(props.meta.id, () => {
|
|
5103
|
+
values.value = DialStore.getValues(props.meta.id);
|
|
5104
|
+
presets.value = DialStore.getPresets(props.meta.id);
|
|
5105
|
+
activePresetId.value = DialStore.getActivePresetId(props.meta.id);
|
|
5106
|
+
});
|
|
5107
|
+
void connectMeasure();
|
|
5108
|
+
});
|
|
5109
|
+
onUnmounted12(() => {
|
|
5110
|
+
unsubscribeValues?.();
|
|
5111
|
+
resizeObserver?.disconnect();
|
|
5112
|
+
});
|
|
5113
|
+
watch8(open, connectMeasure);
|
|
5114
|
+
watch8(() => props.dockVisible, (visible) => {
|
|
5115
|
+
if (!visible) popover.value = null;
|
|
5116
|
+
});
|
|
5117
|
+
const visibleDuration = computed7(() => props.meta.duration > 0 ? props.meta.duration / zoom.value : props.meta.duration);
|
|
5118
|
+
const safeViewStart = computed7(() => clampViewStart(viewStart.value, props.meta.duration, visibleDuration.value));
|
|
5119
|
+
const viewEnd = computed7(() => safeViewStart.value + visibleDuration.value);
|
|
5120
|
+
const pxPerSecond = computed7(() => visibleDuration.value > 0 && laneWidth.value > 0 ? laneWidth.value / visibleDuration.value : 0);
|
|
5121
|
+
const maxZoom = computed7(() => Math.max(
|
|
5122
|
+
MIN_TIMELINE_MAX_ZOOM,
|
|
5123
|
+
laneWidth.value > 0 && props.meta.duration > 0 ? MAJOR_TICK_TARGET_PX * props.meta.duration / (MILLISECOND_STEP * 10 * laneWidth.value) : MIN_TIMELINE_MAX_ZOOM
|
|
5124
|
+
));
|
|
5125
|
+
watch8(maxZoom, (next) => {
|
|
5126
|
+
zoom.value = clamp(zoom.value, 1, next);
|
|
5127
|
+
}, { immediate: true });
|
|
5128
|
+
watch8([() => props.meta.duration, zoom], () => {
|
|
5129
|
+
viewStart.value = clampViewStart(viewStart.value, props.meta.duration, props.meta.duration / zoom.value);
|
|
5130
|
+
});
|
|
5131
|
+
const centerViewAt = (time) => {
|
|
5132
|
+
if (zoom.value <= 1 || props.meta.duration <= 0) return;
|
|
5133
|
+
const duration = props.meta.duration / zoom.value;
|
|
5134
|
+
viewStart.value = clampViewStart(time - duration / 2, props.meta.duration, duration);
|
|
5135
|
+
};
|
|
5136
|
+
const resetView = () => {
|
|
5137
|
+
zoom.value = 1;
|
|
5138
|
+
viewStart.value = 0;
|
|
5139
|
+
};
|
|
5140
|
+
let zoomDrag = null;
|
|
5141
|
+
let rulerScrub = null;
|
|
5142
|
+
let trackScrub = null;
|
|
5143
|
+
const seekRuler = (clientX) => {
|
|
5144
|
+
if (!rulerScrub || rulerScrub.rect.width <= 0) return;
|
|
5145
|
+
TimelineStore.seek(props.meta.id, clamp(
|
|
5146
|
+
rulerScrub.viewStart + (clientX - rulerScrub.rect.left) / rulerScrub.rect.width * rulerScrub.visibleDuration,
|
|
5147
|
+
rulerScrub.viewStart,
|
|
5148
|
+
rulerScrub.viewStart + rulerScrub.visibleDuration
|
|
5149
|
+
));
|
|
5150
|
+
};
|
|
5151
|
+
const seekTrack = (clientX) => {
|
|
5152
|
+
if (!trackScrub || trackScrub.rect.width <= 0) return;
|
|
5153
|
+
TimelineStore.seek(props.meta.id, clamp(
|
|
5154
|
+
trackScrub.viewStart + (clientX - trackScrub.rect.left) / trackScrub.rect.width * trackScrub.visibleDuration,
|
|
5155
|
+
trackScrub.viewStart,
|
|
5156
|
+
trackScrub.viewStart + trackScrub.visibleDuration
|
|
5157
|
+
));
|
|
5158
|
+
};
|
|
5159
|
+
const finishRuler = () => {
|
|
5160
|
+
if (rulerScrub?.wasPlaying) TimelineStore.play(props.meta.id);
|
|
5161
|
+
rulerScrub = null;
|
|
5162
|
+
zoomDrag = null;
|
|
5163
|
+
};
|
|
5164
|
+
const finishTrack = () => {
|
|
5165
|
+
if (trackScrub?.wasPlaying) TimelineStore.play(props.meta.id);
|
|
5166
|
+
trackScrub = null;
|
|
5167
|
+
};
|
|
5168
|
+
const handleCopy = () => {
|
|
5169
|
+
const normalized = normalizeTimelineValuesForCopy(DialStore.getValues(props.meta.id), props.meta.clips);
|
|
5170
|
+
void navigator.clipboard.writeText(buildCopyInstruction("useDialTimeline", props.meta.name, normalized));
|
|
5171
|
+
copied.value = true;
|
|
5172
|
+
window.setTimeout(() => {
|
|
5173
|
+
copied.value = false;
|
|
5174
|
+
}, 1500);
|
|
5175
|
+
};
|
|
5176
|
+
const handleAddPreset = () => DialStore.savePreset(props.meta.id, `Version ${presets.value.length + 2}`);
|
|
5177
|
+
const closePopover = () => {
|
|
5178
|
+
popover.value = null;
|
|
5179
|
+
};
|
|
5180
|
+
const openClipPopover = (clip, rect, stepKey) => {
|
|
5181
|
+
const target = stepKey ? `${clip.key}.${stepKey}` : clip.key;
|
|
5182
|
+
if (getClipControls(props.meta.id, target, stepKey ? void 0 : clipPopoverExclusions(clip)).length === 0) return;
|
|
5183
|
+
popover.value = popover.value?.clip.key === clip.key && popover.value.stepKey === stepKey ? null : {
|
|
5184
|
+
clip,
|
|
5185
|
+
stepKey,
|
|
5186
|
+
anchor: { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom, width: rect.width, height: rect.height }
|
|
5187
|
+
};
|
|
5188
|
+
};
|
|
5189
|
+
const toggleSet = (state, key) => {
|
|
5190
|
+
const next = new Set(state.value);
|
|
5191
|
+
if (next.has(key)) next.delete(key);
|
|
5192
|
+
else next.add(key);
|
|
5193
|
+
state.value = next;
|
|
5194
|
+
};
|
|
5195
|
+
const toggleTracks = (key) => toggleSet(expandedTracks, key);
|
|
5196
|
+
const toggleGroup = (key) => toggleSet(collapsedGroups, key);
|
|
5197
|
+
const handleBarClick = (clip, rect, stepKey) => {
|
|
5198
|
+
if (!stepKey && clip.tracks?.length) toggleTracks(clip.key);
|
|
5199
|
+
else openClipPopover(clip, rect, stepKey);
|
|
5200
|
+
};
|
|
5201
|
+
const ticks = computed7(() => {
|
|
5202
|
+
const raw = pxPerSecond.value > 0 ? MAJOR_TICK_TARGET_PX / pxPerSecond.value : 1;
|
|
5203
|
+
const adaptive = SECOND_TICK_STEPS.find((step) => step >= raw) ?? SECOND_TICK_STEPS[SECOND_TICK_STEPS.length - 1];
|
|
5204
|
+
const majorStep = zoom.value < 1.5 && props.meta.duration >= 1 ? Math.max(1, adaptive) : adaptive;
|
|
5205
|
+
const fineStep = majorStep / 10;
|
|
5206
|
+
const major = [];
|
|
5207
|
+
const medium = [];
|
|
5208
|
+
const fine = [];
|
|
5209
|
+
for (let time = Math.ceil((safeViewStart.value - 1e-6) / majorStep) * majorStep; time <= viewEnd.value + 1e-6; time += majorStep) {
|
|
5210
|
+
major.push(Number(time.toFixed(4)));
|
|
5211
|
+
}
|
|
5212
|
+
const first = Math.ceil((safeViewStart.value - 1e-6) / fineStep);
|
|
5213
|
+
const last = Math.floor((viewEnd.value + 1e-6) / fineStep);
|
|
5214
|
+
for (let index = first; index <= last; index++) {
|
|
5215
|
+
if (index % 10 === 0) continue;
|
|
5216
|
+
const tick = Number((index * fineStep).toFixed(6));
|
|
5217
|
+
if (index % 5 === 0) medium.push(tick);
|
|
5218
|
+
else fine.push(tick);
|
|
5219
|
+
}
|
|
5220
|
+
return { major, medium, fine, majorStep };
|
|
5221
|
+
});
|
|
5222
|
+
const renderRows = () => {
|
|
5223
|
+
const rows = [];
|
|
5224
|
+
let lastGroup;
|
|
5225
|
+
for (const clip of props.meta.clips) {
|
|
5226
|
+
if (clip.group !== lastGroup) {
|
|
5227
|
+
lastGroup = clip.group;
|
|
5228
|
+
if (clip.group) {
|
|
5229
|
+
const group = clip.group;
|
|
5230
|
+
const collapsed = collapsedGroups.value.has(group);
|
|
5231
|
+
rows.push(h19("div", { key: `group:${group}`, class: "dialkit-timeline-row dialkit-timeline-group-row" }, [
|
|
5232
|
+
h19("div", { class: "dialkit-timeline-label" }, [
|
|
5233
|
+
h19("button", {
|
|
5234
|
+
class: "dialkit-timeline-group-toggle",
|
|
5235
|
+
"data-open": !collapsed,
|
|
5236
|
+
title: collapsed ? "Expand layer" : "Collapse layer",
|
|
5237
|
+
onClick: () => toggleGroup(group)
|
|
5238
|
+
}, [chevronIcon()]),
|
|
5239
|
+
h19("span", formatLabel(group))
|
|
5240
|
+
]),
|
|
5241
|
+
h19("div", { class: "dialkit-timeline-lane" })
|
|
5242
|
+
]));
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
if (clip.group && collapsedGroups.value.has(clip.group)) continue;
|
|
5246
|
+
const isProps = Boolean(clip.tracks?.length);
|
|
5247
|
+
const tracksOpen = isProps && expandedTracks.value.has(clip.key);
|
|
5248
|
+
const stat = computeClipStaticFromValues(values.value, clip, props.meta.duration);
|
|
5249
|
+
const selected = popover.value?.clip.key === clip.key;
|
|
5250
|
+
rows.push(h19("div", { key: clip.key, class: "dialkit-timeline-row", "data-grouped": clip.group ? "" : void 0 }, [
|
|
5251
|
+
h19("div", { class: "dialkit-timeline-label" }, [
|
|
5252
|
+
isProps ? h19("button", {
|
|
5253
|
+
class: "dialkit-timeline-group-toggle",
|
|
5254
|
+
"data-open": tracksOpen,
|
|
5255
|
+
title: tracksOpen ? "Collapse properties" : "Expand properties",
|
|
5256
|
+
onClick: (event) => {
|
|
5257
|
+
event.stopPropagation();
|
|
5258
|
+
toggleTracks(clip.key);
|
|
5259
|
+
}
|
|
5260
|
+
}, [chevronIcon()]) : null,
|
|
5261
|
+
clip.label
|
|
5262
|
+
]),
|
|
5263
|
+
h19("div", { class: "dialkit-timeline-lane" }, [h19(TimelineClip, {
|
|
5264
|
+
timelineId: props.meta.id,
|
|
5265
|
+
clip,
|
|
5266
|
+
at: stat.at,
|
|
5267
|
+
duration: stat.duration,
|
|
5268
|
+
loop: stat.loop,
|
|
5269
|
+
steps: clip.stepKeys?.length ? stat.tracks[0]?.steps : void 0,
|
|
5270
|
+
fixedDuration: isProps ? true : stat.isPhysics,
|
|
5271
|
+
composite: isProps,
|
|
5272
|
+
pxPerSecond: pxPerSecond.value,
|
|
5273
|
+
viewStart: safeViewStart.value,
|
|
5274
|
+
timelineDuration: props.meta.duration,
|
|
5275
|
+
selected,
|
|
5276
|
+
selectedStepKey: selected ? popover.value?.stepKey : void 0,
|
|
5277
|
+
onClick: handleBarClick,
|
|
5278
|
+
onDrag: closePopover
|
|
5279
|
+
})])
|
|
5280
|
+
]));
|
|
5281
|
+
if (!tracksOpen) continue;
|
|
5282
|
+
for (const trackRef of clip.tracks ?? []) {
|
|
5283
|
+
const track = stat.tracks.find((candidate) => candidate.prop === trackRef.prop);
|
|
5284
|
+
if (!track) continue;
|
|
5285
|
+
const trackKey = `${clip.key}.${trackRef.prop}`;
|
|
5286
|
+
const trackMeta = {
|
|
5287
|
+
key: trackKey,
|
|
5288
|
+
label: `${clip.label} \xB7 ${formatLabel(trackRef.prop)}`,
|
|
5289
|
+
color: clip.color,
|
|
5290
|
+
loop: clip.loop,
|
|
5291
|
+
group: clip.group,
|
|
5292
|
+
stepKeys: trackRef.stepKeys
|
|
5293
|
+
};
|
|
5294
|
+
const trackSelected = popover.value?.clip.key === trackKey;
|
|
5295
|
+
rows.push(h19("div", { key: trackKey, class: "dialkit-timeline-row dialkit-timeline-track-row", "data-grouped": clip.group ? "" : void 0 }, [
|
|
5296
|
+
h19("div", { class: "dialkit-timeline-label" }, formatLabel(trackRef.prop)),
|
|
5297
|
+
h19("div", { class: "dialkit-timeline-lane" }, [h19(TimelineClip, {
|
|
5298
|
+
timelineId: props.meta.id,
|
|
5299
|
+
clip: trackMeta,
|
|
5300
|
+
at: stat.at + track.delay,
|
|
5301
|
+
duration: track.duration,
|
|
5302
|
+
loop: stat.loop,
|
|
5303
|
+
steps: trackRef.stepKeys?.length ? track.steps : void 0,
|
|
5304
|
+
fixedDuration: !trackRef.stepKeys?.length && track.steps[0]?.isPhysics === true,
|
|
5305
|
+
baseAt: stat.at,
|
|
5306
|
+
delayMode: true,
|
|
5307
|
+
pxPerSecond: pxPerSecond.value,
|
|
5308
|
+
viewStart: safeViewStart.value,
|
|
5309
|
+
timelineDuration: props.meta.duration,
|
|
5310
|
+
selected: trackSelected,
|
|
5311
|
+
selectedStepKey: trackSelected ? popover.value?.stepKey : void 0,
|
|
5312
|
+
onClick: openClipPopover,
|
|
5313
|
+
onDrag: closePopover
|
|
5314
|
+
})])
|
|
5315
|
+
]));
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
return rows;
|
|
5319
|
+
};
|
|
5320
|
+
return () => h19("div", { class: "dialkit-timeline-section" }, [
|
|
5321
|
+
h19("div", { class: "dialkit-timeline-header", "data-open": open.value || void 0 }, [
|
|
5322
|
+
h19("div", { class: "dialkit-timeline-identity" }, [
|
|
5323
|
+
h19("span", { ref: titleRef, class: "dialkit-timeline-title" }, props.meta.name)
|
|
5324
|
+
]),
|
|
5325
|
+
!open.value ? h19(TimelineOverview, {
|
|
5326
|
+
id: props.meta.id,
|
|
5327
|
+
duration: props.meta.duration,
|
|
5328
|
+
viewStart: safeViewStart.value,
|
|
5329
|
+
viewEnd: viewEnd.value,
|
|
5330
|
+
onNavigate: centerViewAt
|
|
5331
|
+
}) : null,
|
|
5332
|
+
h19("div", { ref: actionsRef, class: "dialkit-timeline-actions" }, [
|
|
5333
|
+
h19(PlayPauseButton, { id: props.meta.id }),
|
|
5334
|
+
h19("button", { class: "dialkit-toolbar-add", title: "Add timeline version", "aria-label": "Add timeline version", onClick: handleAddPreset }, [
|
|
5335
|
+
h19(
|
|
5336
|
+
"svg",
|
|
5337
|
+
{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2.5", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" },
|
|
5338
|
+
ICON_ADD_PRESET.map((path) => h19("path", { d: path }))
|
|
5339
|
+
)
|
|
5340
|
+
]),
|
|
5341
|
+
h19(PresetManager, { panelId: props.meta.id, presets: presets.value, activePresetId: activePresetId.value }),
|
|
5342
|
+
h19("button", {
|
|
5343
|
+
class: "dialkit-toolbar-add",
|
|
5344
|
+
title: "Copy parameters",
|
|
5345
|
+
"aria-label": copied.value ? "Copied parameters" : "Copy parameters",
|
|
5346
|
+
onClick: handleCopy
|
|
5347
|
+
}, [h19("span", { style: { position: "relative", width: "16px", height: "16px" } }, [
|
|
5348
|
+
copied.value ? h19("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", style: iconStyle }, [h19("path", { d: ICON_CHECK })]) : h19("svg", { viewBox: "0 0 24 24", fill: "none", style: iconStyle }, [
|
|
5349
|
+
h19("path", { d: ICON_CLIPBOARD.board, stroke: "currentColor", "stroke-width": "2", "stroke-linejoin": "round" }),
|
|
5350
|
+
h19("path", { d: ICON_CLIPBOARD.sparkle, fill: "currentColor" }),
|
|
5351
|
+
h19("path", { d: ICON_CLIPBOARD.body, stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" })
|
|
5352
|
+
])
|
|
5353
|
+
])]),
|
|
5354
|
+
h19("button", {
|
|
5355
|
+
class: "dialkit-timeline-chevron",
|
|
5356
|
+
"data-open": open.value,
|
|
5357
|
+
"aria-expanded": open.value,
|
|
5358
|
+
title: open.value ? "Collapse timeline" : "Expand timeline",
|
|
5359
|
+
onClick: () => {
|
|
5360
|
+
open.value = !open.value;
|
|
5361
|
+
}
|
|
5362
|
+
}, [chevronIcon()])
|
|
5363
|
+
])
|
|
5364
|
+
]),
|
|
5365
|
+
open.value ? h19("div", {
|
|
5366
|
+
class: "dialkit-timeline-body",
|
|
5367
|
+
onPointerdown: (event) => {
|
|
5368
|
+
const target = event.target;
|
|
5369
|
+
if (target.closest(".dialkit-timeline-label, button")) return;
|
|
5370
|
+
if (!event.shiftKey && target.closest(".dialkit-timeline-clip")) return;
|
|
5371
|
+
const rect = laneAreaRef.value?.getBoundingClientRect();
|
|
5372
|
+
if (!rect) return;
|
|
5373
|
+
event.preventDefault();
|
|
5374
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
5375
|
+
const reset = event.shiftKey;
|
|
5376
|
+
trackScrub = {
|
|
5377
|
+
wasPlaying: TimelineStore.getTransport(props.meta.id).playing,
|
|
5378
|
+
rect,
|
|
5379
|
+
viewStart: reset ? 0 : safeViewStart.value,
|
|
5380
|
+
visibleDuration: reset ? props.meta.duration : visibleDuration.value
|
|
5381
|
+
};
|
|
5382
|
+
if (reset) resetView();
|
|
5383
|
+
popover.value = null;
|
|
5384
|
+
TimelineStore.pause(props.meta.id);
|
|
5385
|
+
seekTrack(event.clientX);
|
|
5386
|
+
},
|
|
5387
|
+
onPointermove: (event) => trackScrub && seekTrack(event.clientX),
|
|
5388
|
+
onPointerup: finishTrack,
|
|
5389
|
+
onPointercancel: finishTrack,
|
|
5390
|
+
onLostpointercapture: finishTrack
|
|
5391
|
+
}, [h19("div", { class: "dialkit-timeline-grid" }, [
|
|
5392
|
+
h19("div", { class: "dialkit-timeline-row dialkit-timeline-ruler-row" }, [
|
|
5393
|
+
h19("div", { class: "dialkit-timeline-label" }),
|
|
5394
|
+
h19("div", {
|
|
5395
|
+
ref: laneAreaRef,
|
|
5396
|
+
class: "dialkit-timeline-ruler",
|
|
5397
|
+
title: "Drag to seek \xB7 Option-drag to zoom \xB7 Shift-drag to reset zoom",
|
|
5398
|
+
onPointerdown: (event) => {
|
|
5399
|
+
event.preventDefault();
|
|
5400
|
+
event.stopPropagation();
|
|
5401
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
5402
|
+
if (rect.width <= 0) return;
|
|
5403
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
5404
|
+
if (!event.altKey) {
|
|
5405
|
+
const reset = event.shiftKey;
|
|
5406
|
+
rulerScrub = {
|
|
5407
|
+
wasPlaying: TimelineStore.getTransport(props.meta.id).playing,
|
|
5408
|
+
rect,
|
|
5409
|
+
viewStart: reset ? 0 : safeViewStart.value,
|
|
5410
|
+
visibleDuration: reset ? props.meta.duration : visibleDuration.value
|
|
5411
|
+
};
|
|
5412
|
+
if (reset) resetView();
|
|
5413
|
+
TimelineStore.pause(props.meta.id);
|
|
5414
|
+
seekRuler(event.clientX);
|
|
5415
|
+
return;
|
|
5416
|
+
}
|
|
5417
|
+
const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
|
|
5418
|
+
zoomDrag = {
|
|
5419
|
+
pointerX: event.clientX,
|
|
5420
|
+
zoom: zoom.value,
|
|
5421
|
+
anchorRatio: ratio,
|
|
5422
|
+
anchorTime: safeViewStart.value + ratio * visibleDuration.value,
|
|
5423
|
+
moved: false
|
|
5424
|
+
};
|
|
5425
|
+
},
|
|
5426
|
+
onPointermove: (event) => {
|
|
5427
|
+
if (rulerScrub) {
|
|
5428
|
+
seekRuler(event.clientX);
|
|
5429
|
+
return;
|
|
5430
|
+
}
|
|
5431
|
+
if (!zoomDrag || props.meta.duration <= 0) return;
|
|
5432
|
+
const dx = event.clientX - zoomDrag.pointerX;
|
|
5433
|
+
if (!zoomDrag.moved && Math.abs(dx) <= DRAG_THRESHOLD_PX) return;
|
|
5434
|
+
zoomDrag.moved = true;
|
|
5435
|
+
const nextZoom = clamp(zoomDrag.zoom * Math.exp(dx / ZOOM_DRAG_DISTANCE), 1, maxZoom.value);
|
|
5436
|
+
const duration = props.meta.duration / nextZoom;
|
|
5437
|
+
zoom.value = nextZoom;
|
|
5438
|
+
viewStart.value = clampViewStart(zoomDrag.anchorTime - zoomDrag.anchorRatio * duration, props.meta.duration, duration);
|
|
5439
|
+
},
|
|
5440
|
+
onPointerup: finishRuler,
|
|
5441
|
+
onPointercancel: finishRuler,
|
|
5442
|
+
onLostpointercapture: finishRuler
|
|
5443
|
+
}, [
|
|
5444
|
+
...ticks.value.fine.map((time) => h19("div", { key: `fine:${time}`, class: "dialkit-timeline-tick dialkit-timeline-tick-fine", style: { left: `${(time - safeViewStart.value) * pxPerSecond.value}px` } })),
|
|
5445
|
+
...ticks.value.medium.map((time) => h19("div", { key: `medium:${time}`, class: "dialkit-timeline-tick dialkit-timeline-tick-medium", style: { left: `${(time - safeViewStart.value) * pxPerSecond.value}px` } })),
|
|
5446
|
+
...ticks.value.major.map((time) => h19("div", { key: time, class: "dialkit-timeline-tick", style: { left: `${(time - safeViewStart.value) * pxPerSecond.value}px` } }, [
|
|
5447
|
+
h19("span", { class: "dialkit-timeline-tick-label" }, formatRulerSeconds(time, ticks.value.majorStep))
|
|
5448
|
+
]))
|
|
5449
|
+
])
|
|
5450
|
+
]),
|
|
5451
|
+
...renderRows(),
|
|
5452
|
+
pxPerSecond.value > 0 ? h19(TimelinePlayheadFlag, {
|
|
5453
|
+
id: props.meta.id,
|
|
5454
|
+
duration: props.meta.duration,
|
|
5455
|
+
pxPerSecond: pxPerSecond.value,
|
|
5456
|
+
viewStart: safeViewStart.value,
|
|
5457
|
+
viewEnd: viewEnd.value,
|
|
5458
|
+
laneWidth: laneWidth.value,
|
|
5459
|
+
ruler: laneAreaRef.value ?? void 0,
|
|
5460
|
+
headerClearStart: flagClearRange.value.start,
|
|
5461
|
+
headerClearEnd: flagClearRange.value.end,
|
|
5462
|
+
onResetView: resetView
|
|
5463
|
+
}) : null
|
|
5464
|
+
])]) : null,
|
|
5465
|
+
popover.value ? h19(ClipPopover, {
|
|
5466
|
+
panelId: props.meta.id,
|
|
5467
|
+
popover: popover.value,
|
|
5468
|
+
values: values.value,
|
|
5469
|
+
theme: props.theme,
|
|
5470
|
+
onClose: closePopover
|
|
5471
|
+
}) : null
|
|
5472
|
+
]);
|
|
5473
|
+
}
|
|
5474
|
+
});
|
|
5475
|
+
function chevronIcon() {
|
|
5476
|
+
return h19("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2.5", "stroke-linecap": "round", "stroke-linejoin": "round" }, [
|
|
5477
|
+
h19("path", { d: ICON_CHEVRON })
|
|
5478
|
+
]);
|
|
5479
|
+
}
|
|
5480
|
+
var ClipPopover = defineComponent19({
|
|
5481
|
+
props: {
|
|
5482
|
+
panelId: { type: String, required: true },
|
|
5483
|
+
popover: { type: Object, required: true },
|
|
5484
|
+
values: { type: Object, required: true },
|
|
5485
|
+
theme: { type: String, required: true },
|
|
5486
|
+
onClose: { type: Function, required: true }
|
|
5487
|
+
},
|
|
5488
|
+
setup(props) {
|
|
5489
|
+
const element = ref15(null);
|
|
5490
|
+
const naturalHeight = ref15(0);
|
|
5491
|
+
const viewport = ref15(readViewport());
|
|
5492
|
+
let observer;
|
|
5493
|
+
const measure = () => {
|
|
5494
|
+
if (element.value) naturalHeight.value = element.value.scrollHeight + 2;
|
|
5495
|
+
};
|
|
5496
|
+
const updateViewport = () => {
|
|
5497
|
+
viewport.value = readViewport();
|
|
5498
|
+
};
|
|
5499
|
+
const outside = (event) => {
|
|
5500
|
+
const target = event.target;
|
|
5501
|
+
if (element.value?.contains(target) || target.closest?.(".dialkit-timeline-clip") || target.closest?.(".dialkit-timeline-label")) return;
|
|
5502
|
+
props.onClose();
|
|
5503
|
+
};
|
|
5504
|
+
const keydown = (event) => {
|
|
5505
|
+
if (event.key === "Escape") props.onClose();
|
|
5506
|
+
};
|
|
5507
|
+
onMounted13(() => {
|
|
5508
|
+
measure();
|
|
5509
|
+
observer = new ResizeObserver(measure);
|
|
5510
|
+
if (element.value) observer.observe(element.value.querySelector(".dialkit-timeline-popover-body") ?? element.value);
|
|
5511
|
+
window.addEventListener("resize", updateViewport);
|
|
5512
|
+
window.visualViewport?.addEventListener("resize", updateViewport);
|
|
5513
|
+
window.visualViewport?.addEventListener("scroll", updateViewport);
|
|
5514
|
+
document.addEventListener("pointerdown", outside, true);
|
|
5515
|
+
document.addEventListener("keydown", keydown);
|
|
5516
|
+
});
|
|
5517
|
+
onUnmounted12(() => {
|
|
5518
|
+
observer?.disconnect();
|
|
5519
|
+
window.removeEventListener("resize", updateViewport);
|
|
5520
|
+
window.visualViewport?.removeEventListener("resize", updateViewport);
|
|
5521
|
+
window.visualViewport?.removeEventListener("scroll", updateViewport);
|
|
5522
|
+
document.removeEventListener("pointerdown", outside, true);
|
|
5523
|
+
document.removeEventListener("keydown", keydown);
|
|
5524
|
+
});
|
|
5525
|
+
return () => {
|
|
5526
|
+
const { clip, stepKey } = props.popover;
|
|
5527
|
+
let controls;
|
|
5528
|
+
let title;
|
|
5529
|
+
if (stepKey) {
|
|
5530
|
+
controls = getClipControls(props.panelId, `${clip.key}.${stepKey}`);
|
|
5531
|
+
if (stepKey === clip.stepKeys?.[0]) {
|
|
5532
|
+
const from = getControlAt(props.panelId, `${clip.key}.from`);
|
|
5533
|
+
if (from) {
|
|
5534
|
+
const index = controls.findIndex((control) => control.path === `${clip.key}.${stepKey}.to`);
|
|
5535
|
+
controls = index >= 0 ? [...controls.slice(0, index), from, ...controls.slice(index)] : [...controls, from];
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
title = `${clip.label} \xB7 ${formatStepLabel(stepKey)}`;
|
|
5539
|
+
} else {
|
|
5540
|
+
controls = getClipControls(props.panelId, clip.key, clipPopoverExclusions(clip));
|
|
5541
|
+
title = clip.label;
|
|
5542
|
+
}
|
|
5543
|
+
if (controls.length === 0) return null;
|
|
5544
|
+
const target = stepKey ? `${clip.key}.${stepKey}` : clip.key;
|
|
5545
|
+
const durationMeta = getControlAt(props.panelId, `${target}.duration`);
|
|
5546
|
+
const durationValue = durationMeta ? props.values[durationMeta.path] : void 0;
|
|
5547
|
+
const transitionDuration = durationMeta?.type === "slider" && typeof durationValue === "number" ? {
|
|
5548
|
+
value: durationValue,
|
|
5549
|
+
onChange: (next) => DialStore.updateValue(props.panelId, durationMeta.path, next),
|
|
5550
|
+
min: Math.max(TIMELINE_MIN_CLIP_DURATION, durationMeta.min ?? 0),
|
|
5551
|
+
max: durationMeta.max,
|
|
5552
|
+
step: durationMeta.step
|
|
5553
|
+
} : void 0;
|
|
5554
|
+
const current = viewport.value;
|
|
5555
|
+
const right = current.offsetLeft + current.width;
|
|
5556
|
+
const bottom = current.offsetTop + current.height;
|
|
5557
|
+
const width = Math.min(POPOVER_WIDTH, Math.max(220, current.width - 24));
|
|
5558
|
+
const left = clamp(props.popover.anchor.left + props.popover.anchor.width / 2 - width / 2, current.offsetLeft + 12, Math.max(current.offsetLeft + 12, right - width - 12));
|
|
5559
|
+
const above = Math.max(0, props.popover.anchor.top - current.offsetTop - 22);
|
|
5560
|
+
const below = Math.max(0, bottom - props.popover.anchor.bottom - 22);
|
|
5561
|
+
const placeAbove = naturalHeight.value === 0 ? above >= below : naturalHeight.value <= above || naturalHeight.value > below && above >= below;
|
|
5562
|
+
const availableHeight = placeAbove ? above : below;
|
|
5563
|
+
const renderedHeight = Math.min(naturalHeight.value || availableHeight, availableHeight);
|
|
5564
|
+
const rawTop = placeAbove ? props.popover.anchor.top - 10 - renderedHeight : props.popover.anchor.bottom + 10;
|
|
5565
|
+
const top = clamp(rawTop, current.offsetTop + 12, Math.max(current.offsetTop + 12, bottom - renderedHeight - 12));
|
|
5566
|
+
return h19(Teleport4, { to: "body" }, [h19("div", { class: "dialkit-root", "data-theme": props.theme }, [
|
|
5567
|
+
h19("div", {
|
|
5568
|
+
ref: element,
|
|
5569
|
+
class: "dialkit-timeline-popover",
|
|
5570
|
+
"data-placement": placeAbove ? "above" : "below",
|
|
5571
|
+
style: { left: `${left}px`, top: `${top}px`, width: `${width}px`, maxHeight: `${availableHeight}px`, visibility: naturalHeight.value > 0 ? "visible" : "hidden" },
|
|
5572
|
+
role: "dialog",
|
|
5573
|
+
"aria-label": `Edit ${title}`
|
|
5574
|
+
}, [
|
|
5575
|
+
h19("div", { class: "dialkit-timeline-popover-header" }, [
|
|
5576
|
+
h19("span", { class: "dialkit-timeline-popover-title" }, title),
|
|
5577
|
+
h19("button", { class: "dialkit-timeline-popover-close", title: "Close editor", "aria-label": "Close editor", onClick: props.onClose }, [
|
|
5578
|
+
h19("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }, [h19("path", { d: "M6 6L18 18M18 6L6 18" })])
|
|
5579
|
+
])
|
|
5580
|
+
]),
|
|
5581
|
+
h19("div", { class: "dialkit-timeline-popover-body" }, [h19(ControlRenderer, {
|
|
5582
|
+
panelId: props.panelId,
|
|
5583
|
+
controls,
|
|
5584
|
+
values: timelinePopoverDisplayValues(props.values, clip.key, clip.stepKeys, stepKey),
|
|
5585
|
+
transitionDuration
|
|
5586
|
+
})])
|
|
5587
|
+
])
|
|
5588
|
+
])]);
|
|
5589
|
+
};
|
|
5590
|
+
}
|
|
5591
|
+
});
|
|
5592
|
+
function readViewport() {
|
|
5593
|
+
return {
|
|
5594
|
+
width: window.visualViewport?.width ?? window.innerWidth,
|
|
5595
|
+
height: window.visualViewport?.height ?? window.innerHeight,
|
|
5596
|
+
offsetLeft: window.visualViewport?.offsetLeft ?? 0,
|
|
5597
|
+
offsetTop: window.visualViewport?.offsetTop ?? 0
|
|
5598
|
+
};
|
|
5599
|
+
}
|
|
5600
|
+
function clipPopoverExclusions(clip) {
|
|
5601
|
+
return /* @__PURE__ */ new Set([...clip.stepKeys ?? [], ...clip.tracks?.map((track) => track.prop) ?? []]);
|
|
5602
|
+
}
|
|
5603
|
+
function getClipControls(panelId, path, exclusions) {
|
|
5604
|
+
const panel = DialStore.getPanel(panelId);
|
|
5605
|
+
const folder = panel ? findControl(panel.controls, path) : null;
|
|
5606
|
+
if (!folder?.children) return [];
|
|
5607
|
+
return folder.children.filter((control) => {
|
|
5608
|
+
const key = control.path.slice(path.length + 1);
|
|
5609
|
+
return key !== "at" && key !== "duration" && !exclusions?.has(key);
|
|
5610
|
+
});
|
|
5611
|
+
}
|
|
5612
|
+
function getControlAt(panelId, path) {
|
|
5613
|
+
const panel = DialStore.getPanel(panelId);
|
|
5614
|
+
return panel ? findControl(panel.controls, path) : null;
|
|
5615
|
+
}
|
|
5616
|
+
var TimelineClip = defineComponent19({
|
|
5617
|
+
props: {
|
|
5618
|
+
timelineId: { type: String, required: true },
|
|
5619
|
+
clip: { type: Object, required: true },
|
|
5620
|
+
at: { type: Number, required: true },
|
|
5621
|
+
duration: { type: Number, required: true },
|
|
5622
|
+
loop: { type: String, required: true },
|
|
5623
|
+
steps: Array,
|
|
5624
|
+
fixedDuration: { type: Boolean, required: true },
|
|
5625
|
+
composite: Boolean,
|
|
5626
|
+
baseAt: { type: Number, default: 0 },
|
|
5627
|
+
delayMode: Boolean,
|
|
5628
|
+
pxPerSecond: { type: Number, required: true },
|
|
5629
|
+
viewStart: { type: Number, required: true },
|
|
5630
|
+
timelineDuration: { type: Number, required: true },
|
|
5631
|
+
selected: { type: Boolean, required: true },
|
|
5632
|
+
selectedStepKey: String,
|
|
5633
|
+
onClick: { type: Function, required: true },
|
|
5634
|
+
onDrag: { type: Function, required: true }
|
|
5635
|
+
},
|
|
5636
|
+
setup(props) {
|
|
5637
|
+
const dragging = ref15(false);
|
|
5638
|
+
let drag = null;
|
|
5639
|
+
const finish = (event) => {
|
|
5640
|
+
const previous = drag;
|
|
5641
|
+
drag = null;
|
|
5642
|
+
dragging.value = false;
|
|
5643
|
+
if (previous && !previous.moved && event) {
|
|
5644
|
+
const anchor = previous.clickEl ?? event.currentTarget;
|
|
5645
|
+
props.onClick(props.clip, anchor.getBoundingClientRect(), previous.clickEl?.dataset.step);
|
|
5646
|
+
}
|
|
5647
|
+
};
|
|
5648
|
+
return () => {
|
|
5649
|
+
const width = Math.max(props.duration * props.pxPerSecond, 14);
|
|
5650
|
+
const isSteps = Boolean(props.steps?.length);
|
|
5651
|
+
const looping = props.loop === "repeat" && props.duration > 0;
|
|
5652
|
+
const resizable = props.duration > 0 && !props.fixedDuration && !props.composite;
|
|
5653
|
+
const durationText = `${props.fixedDuration && !props.composite ? "~" : ""}${formatSeconds(props.duration)}`;
|
|
5654
|
+
const ghosts = [];
|
|
5655
|
+
if (looping) {
|
|
5656
|
+
const first = Math.max(1, Math.floor((props.viewStart - props.at) / props.duration));
|
|
5657
|
+
for (let offset = 0; offset < 256; offset++) {
|
|
5658
|
+
const index = first + offset;
|
|
5659
|
+
const start = props.at + props.duration * index;
|
|
5660
|
+
if (start >= props.timelineDuration - 1e-6) break;
|
|
5661
|
+
const duration = Math.min(props.duration, props.timelineDuration - start);
|
|
5662
|
+
ghosts.push(h19("div", {
|
|
5663
|
+
key: `ghost:${index}`,
|
|
5664
|
+
class: "dialkit-timeline-clip-ghost",
|
|
5665
|
+
"data-steps": isSteps || void 0,
|
|
5666
|
+
"aria-hidden": "true",
|
|
5667
|
+
style: { left: `${(start - props.viewStart) * props.pxPerSecond + 1}px`, width: `${Math.max(1, duration * props.pxPerSecond - 2)}px`, background: props.clip.color }
|
|
5668
|
+
}, props.steps?.map((step) => h19("span", { class: "dialkit-timeline-clip-ghost-segment", style: { width: `${step.duration * props.pxPerSecond}px` } }))));
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
let cumulative = 0;
|
|
5672
|
+
const boundaries = props.steps?.map((step) => cumulative += step.duration) ?? [];
|
|
5673
|
+
const children = [];
|
|
5674
|
+
if (props.composite) {
|
|
5675
|
+
if (width > 56) children.push(h19("span", { class: "dialkit-timeline-clip-duration" }, durationText));
|
|
5676
|
+
} else if (isSteps) {
|
|
5677
|
+
for (const step of props.steps ?? []) {
|
|
5678
|
+
const segmentWidth = step.duration * props.pxPerSecond;
|
|
5679
|
+
children.push(h19("div", {
|
|
5680
|
+
key: step.key ?? "step",
|
|
5681
|
+
class: "dialkit-timeline-clip-segment",
|
|
5682
|
+
"data-step": step.key,
|
|
5683
|
+
"data-selected": props.selectedStepKey === step.key || void 0,
|
|
5684
|
+
style: { width: `${segmentWidth}px` }
|
|
5685
|
+
}, segmentWidth > 52 ? [h19("span", { class: "dialkit-timeline-clip-duration" }, formatSeconds(step.duration))] : []));
|
|
5686
|
+
}
|
|
5687
|
+
(props.steps ?? []).forEach((step, index) => {
|
|
5688
|
+
if (!step.isPhysics) children.push(h19("div", { key: `boundary:${step.key}`, class: "dialkit-timeline-clip-handle", "data-boundary": index, style: { left: `${boundaries[index] * props.pxPerSecond - 4}px` } }));
|
|
5689
|
+
});
|
|
5690
|
+
if (!props.steps?.[0]?.isPhysics) children.push(h19("div", { class: "dialkit-timeline-clip-handle", "data-edge": "start" }));
|
|
5691
|
+
} else {
|
|
5692
|
+
if (resizable) children.push(h19("div", { class: "dialkit-timeline-clip-handle", "data-edge": "start" }));
|
|
5693
|
+
if (width > 56) children.push(h19("span", { class: "dialkit-timeline-clip-duration" }, durationText));
|
|
5694
|
+
if (resizable) children.push(h19("div", { class: "dialkit-timeline-clip-handle", "data-edge": "end" }));
|
|
5695
|
+
}
|
|
5696
|
+
const title = props.composite ? `${props.clip.label} \u2014 composite of its property tracks${looping ? " \xB7 repeats through timeline" : ""} \xB7 click to expand` : `${props.clip.label} \u2014 ${formatSeconds(props.at)} for ${durationText}${props.fixedDuration ? " (duration set by spring physics)" : ""}${looping ? " \xB7 repeats through timeline" : ""}${props.delayMode ? " \xB7 drag to phase-shift" : ""}`;
|
|
5697
|
+
return [...ghosts, h19("div", {
|
|
5698
|
+
class: "dialkit-timeline-clip",
|
|
5699
|
+
"data-steps": isSteps || void 0,
|
|
5700
|
+
"data-composite": props.composite || void 0,
|
|
5701
|
+
"data-selected": props.selected || void 0,
|
|
5702
|
+
"data-dragging": dragging.value || void 0,
|
|
5703
|
+
style: { left: `${(props.at - props.viewStart) * props.pxPerSecond}px`, width: `${width}px`, background: props.composite ? `${props.clip.color}80` : props.clip.color },
|
|
5704
|
+
title,
|
|
5705
|
+
onPointerdown: (event) => {
|
|
5706
|
+
if (event.shiftKey) return;
|
|
5707
|
+
event.stopPropagation();
|
|
5708
|
+
const target = event.target;
|
|
5709
|
+
let mode = "move";
|
|
5710
|
+
let boundaryIndex;
|
|
5711
|
+
if (target.dataset.boundary !== void 0) {
|
|
5712
|
+
mode = "boundary";
|
|
5713
|
+
boundaryIndex = Number(target.dataset.boundary);
|
|
5714
|
+
} else if (!props.fixedDuration) {
|
|
5715
|
+
const edge = target.dataset.edge;
|
|
5716
|
+
if (edge) mode = edge;
|
|
5717
|
+
}
|
|
5718
|
+
drag = {
|
|
5719
|
+
mode,
|
|
5720
|
+
boundaryIndex,
|
|
5721
|
+
pointerX: event.clientX,
|
|
5722
|
+
at: props.at,
|
|
5723
|
+
duration: props.duration,
|
|
5724
|
+
stepDurations: props.steps?.map((step) => step.duration),
|
|
5725
|
+
clickEl: target.closest?.("[data-step]"),
|
|
5726
|
+
moved: false
|
|
5727
|
+
};
|
|
5728
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
5729
|
+
},
|
|
5730
|
+
onPointermove: (event) => {
|
|
5731
|
+
if (!drag || props.pxPerSecond <= 0) return;
|
|
5732
|
+
const dx = event.clientX - drag.pointerX;
|
|
5733
|
+
if (!drag.moved) {
|
|
5734
|
+
if (Math.abs(dx) <= DRAG_THRESHOLD_PX) return;
|
|
5735
|
+
drag.moved = true;
|
|
5736
|
+
dragging.value = true;
|
|
5737
|
+
props.onDrag();
|
|
5738
|
+
}
|
|
5739
|
+
const dt = dx / props.pxPerSecond;
|
|
5740
|
+
if (drag.mode === "boundary" && props.steps && drag.stepDurations) {
|
|
5741
|
+
const index = drag.boundaryIndex ?? 0;
|
|
5742
|
+
const others = drag.stepDurations.reduce((sum, duration, stepIndex) => stepIndex === index ? sum : sum + duration, 0);
|
|
5743
|
+
DialStore.updateValue(props.timelineId, `${props.clip.key}.${props.steps[index].key ?? ""}.duration`, clampStepResize(drag.stepDurations[index] + dt, drag.at, others, props.timelineDuration));
|
|
5744
|
+
} else if (drag.mode === "move") {
|
|
5745
|
+
if (props.delayMode) DialStore.updateValue(props.timelineId, `${props.clip.key}.delay`, clampTrackDelay(drag.at + dt - props.baseAt, props.baseAt, drag.duration, props.timelineDuration));
|
|
5746
|
+
else DialStore.updateValue(props.timelineId, `${props.clip.key}.at`, clampClipMove(drag.at + dt, drag.duration, props.timelineDuration));
|
|
5747
|
+
} else if (drag.mode === "end") {
|
|
5748
|
+
DialStore.updateValue(props.timelineId, `${props.clip.key}.duration`, clampClipResizeEnd(drag.duration + dt, drag.at, props.timelineDuration));
|
|
5749
|
+
} else if (props.steps && drag.stepDurations) {
|
|
5750
|
+
const next = clampClipResizeStart(Math.max(drag.at + dt, Math.max(props.baseAt, 0)), drag.at, drag.stepDurations[0]);
|
|
5751
|
+
DialStore.updateValues(props.timelineId, {
|
|
5752
|
+
[props.delayMode ? `${props.clip.key}.delay` : `${props.clip.key}.at`]: props.delayMode ? Math.max(0, next.at - props.baseAt) : next.at,
|
|
5753
|
+
[`${props.clip.key}.${props.steps[0].key ?? ""}.duration`]: next.duration
|
|
5754
|
+
});
|
|
5755
|
+
} else {
|
|
5756
|
+
const next = clampClipResizeStart(Math.max(drag.at + dt, Math.max(props.baseAt, 0)), drag.at, drag.duration);
|
|
5757
|
+
DialStore.updateValues(props.timelineId, {
|
|
5758
|
+
[props.delayMode ? `${props.clip.key}.delay` : `${props.clip.key}.at`]: props.delayMode ? Math.max(0, next.at - props.baseAt) : next.at,
|
|
5759
|
+
[`${props.clip.key}.duration`]: next.duration
|
|
5760
|
+
});
|
|
5761
|
+
}
|
|
5762
|
+
},
|
|
5763
|
+
onPointerup: finish,
|
|
5764
|
+
onPointercancel: () => finish(),
|
|
5765
|
+
onLostpointercapture: () => finish()
|
|
5766
|
+
}, children), looping ? h19("span", { class: "dialkit-timeline-loop-infinity", "aria-hidden": "true", title: "Repeats indefinitely" }, "\u221E") : null];
|
|
5767
|
+
};
|
|
5768
|
+
}
|
|
5769
|
+
});
|
|
5770
|
+
|
|
2829
5771
|
// src/vue/components/ShortcutsMenu.ts
|
|
2830
|
-
import { defineComponent as
|
|
5772
|
+
import { defineComponent as defineComponent20, h as h20, onUnmounted as onUnmounted13, ref as ref16, Teleport as Teleport5 } from "vue";
|
|
2831
5773
|
function formatShortcutKey(sc) {
|
|
2832
5774
|
if (!sc.key) return "\u2014";
|
|
2833
5775
|
const mod = sc.modifier === "alt" ? "\u2325" : sc.modifier === "shift" ? "\u21E7" : sc.modifier === "meta" ? "\u2318" : "";
|
|
@@ -2846,7 +5788,7 @@ function formatInteraction(sc) {
|
|
|
2846
5788
|
return "scroll";
|
|
2847
5789
|
}
|
|
2848
5790
|
}
|
|
2849
|
-
var ShortcutsMenu =
|
|
5791
|
+
var ShortcutsMenu = defineComponent20({
|
|
2850
5792
|
name: "DialKitShortcutsMenu",
|
|
2851
5793
|
props: {
|
|
2852
5794
|
panelId: {
|
|
@@ -2855,10 +5797,10 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2855
5797
|
}
|
|
2856
5798
|
},
|
|
2857
5799
|
setup(props) {
|
|
2858
|
-
const isOpen =
|
|
2859
|
-
const triggerRef =
|
|
2860
|
-
const dropdownRef =
|
|
2861
|
-
const pos =
|
|
5800
|
+
const isOpen = ref16(false);
|
|
5801
|
+
const triggerRef = ref16(null);
|
|
5802
|
+
const dropdownRef = ref16(null);
|
|
5803
|
+
const pos = ref16({ top: 0, right: 0 });
|
|
2862
5804
|
const open = () => {
|
|
2863
5805
|
const rect = triggerRef.value?.getBoundingClientRect();
|
|
2864
5806
|
if (rect) {
|
|
@@ -2888,7 +5830,7 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2888
5830
|
mousedownHandler = null;
|
|
2889
5831
|
}
|
|
2890
5832
|
};
|
|
2891
|
-
|
|
5833
|
+
onUnmounted13(() => {
|
|
2892
5834
|
removeOutsideClickListener();
|
|
2893
5835
|
});
|
|
2894
5836
|
return () => {
|
|
@@ -2917,13 +5859,13 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2917
5859
|
removeOutsideClickListener();
|
|
2918
5860
|
}
|
|
2919
5861
|
return [
|
|
2920
|
-
|
|
5862
|
+
h20("button", {
|
|
2921
5863
|
ref: triggerRef,
|
|
2922
5864
|
class: "dialkit-shortcuts-trigger",
|
|
2923
5865
|
onClick: toggle,
|
|
2924
5866
|
title: "Keyboard shortcuts"
|
|
2925
5867
|
}, [
|
|
2926
|
-
|
|
5868
|
+
h20("svg", {
|
|
2927
5869
|
viewBox: "0 0 24 24",
|
|
2928
5870
|
fill: "none",
|
|
2929
5871
|
stroke: "currentColor",
|
|
@@ -2931,16 +5873,16 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2931
5873
|
"stroke-linecap": "round",
|
|
2932
5874
|
"stroke-linejoin": "round"
|
|
2933
5875
|
}, [
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
5876
|
+
h20("rect", { x: "2", y: "6", width: "20", height: "12", rx: "2" }),
|
|
5877
|
+
h20("path", { d: "M6 10H6.01" }),
|
|
5878
|
+
h20("path", { d: "M10 10H10.01" }),
|
|
5879
|
+
h20("path", { d: "M14 10H14.01" }),
|
|
5880
|
+
h20("path", { d: "M18 10H18.01" }),
|
|
5881
|
+
h20("path", { d: "M8 14H16" })
|
|
2940
5882
|
])
|
|
2941
5883
|
]),
|
|
2942
|
-
isOpen.value ?
|
|
2943
|
-
|
|
5884
|
+
isOpen.value ? h20(Teleport5, { to: "body" }, [
|
|
5885
|
+
h20("div", {
|
|
2944
5886
|
ref: dropdownRef,
|
|
2945
5887
|
class: "dialkit-root dialkit-shortcuts-dropdown",
|
|
2946
5888
|
style: {
|
|
@@ -2949,19 +5891,19 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2949
5891
|
right: `${pos.value.right}px`
|
|
2950
5892
|
}
|
|
2951
5893
|
}, [
|
|
2952
|
-
|
|
2953
|
-
|
|
5894
|
+
h20("div", { class: "dialkit-shortcuts-title" }, "Keyboard Shortcuts"),
|
|
5895
|
+
h20(
|
|
2954
5896
|
"div",
|
|
2955
5897
|
{ class: "dialkit-shortcuts-list" },
|
|
2956
5898
|
rows.map(
|
|
2957
|
-
(row) =>
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
5899
|
+
(row) => h20("div", { key: row.path, class: "dialkit-shortcuts-row" }, [
|
|
5900
|
+
h20("span", { class: "dialkit-shortcuts-row-key" }, formatShortcutKey(row.shortcut)),
|
|
5901
|
+
h20("span", { class: "dialkit-shortcuts-row-label" }, row.label),
|
|
5902
|
+
h20("span", { class: "dialkit-shortcuts-row-mode" }, formatInteraction(row.shortcut))
|
|
2961
5903
|
])
|
|
2962
5904
|
)
|
|
2963
5905
|
),
|
|
2964
|
-
|
|
5906
|
+
h20("div", { class: "dialkit-shortcuts-hint" }, "See pill badges on controls for keys")
|
|
2965
5907
|
])
|
|
2966
5908
|
]) : null
|
|
2967
5909
|
];
|
|
@@ -2970,8 +5912,8 @@ var ShortcutsMenu = defineComponent17({
|
|
|
2970
5912
|
});
|
|
2971
5913
|
|
|
2972
5914
|
// src/vue/components/ButtonGroup.ts
|
|
2973
|
-
import { defineComponent as
|
|
2974
|
-
var ButtonGroup =
|
|
5915
|
+
import { defineComponent as defineComponent21, h as h21 } from "vue";
|
|
5916
|
+
var ButtonGroup = defineComponent21({
|
|
2975
5917
|
name: "DialKitButtonGroup",
|
|
2976
5918
|
props: {
|
|
2977
5919
|
buttons: {
|
|
@@ -2980,11 +5922,11 @@ var ButtonGroup = defineComponent18({
|
|
|
2980
5922
|
}
|
|
2981
5923
|
},
|
|
2982
5924
|
setup(props) {
|
|
2983
|
-
return () =>
|
|
5925
|
+
return () => h21(
|
|
2984
5926
|
"div",
|
|
2985
5927
|
{ class: "dialkit-button-group" },
|
|
2986
5928
|
props.buttons.map(
|
|
2987
|
-
(button) =>
|
|
5929
|
+
(button) => h21("button", { class: "dialkit-button", onClick: button.onClick }, button.label)
|
|
2988
5930
|
)
|
|
2989
5931
|
);
|
|
2990
5932
|
}
|
|
@@ -2992,8 +5934,10 @@ var ButtonGroup = defineComponent18({
|
|
|
2992
5934
|
export {
|
|
2993
5935
|
ButtonGroup,
|
|
2994
5936
|
ColorControl,
|
|
5937
|
+
ControlRenderer,
|
|
2995
5938
|
DialRoot,
|
|
2996
5939
|
DialStore,
|
|
5940
|
+
DialTimeline,
|
|
2997
5941
|
EasingVisualization,
|
|
2998
5942
|
Folder,
|
|
2999
5943
|
PresetManager,
|
|
@@ -3008,6 +5952,8 @@ export {
|
|
|
3008
5952
|
Toggle,
|
|
3009
5953
|
TransitionControl,
|
|
3010
5954
|
useDialKit,
|
|
5955
|
+
useDialKitController,
|
|
5956
|
+
useDialTimeline,
|
|
3011
5957
|
useShortcutContext,
|
|
3012
5958
|
vDialKit
|
|
3013
5959
|
};
|