dialkit 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +223 -4
- 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 +19 -0
- package/dist/icons.js +39 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.cjs +697 -307
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -5
- package/dist/index.d.ts +42 -5
- package/dist/index.js +720 -331
- 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/shortcut-utils.d.ts +39 -0
- package/dist/shortcut-utils.js +100 -0
- package/dist/shortcut-utils.js.map +1 -0
- package/dist/solid/index.cjs +661 -204
- package/dist/solid/index.cjs.map +1 -1
- package/dist/solid/index.d.cts +40 -3
- package/dist/solid/index.d.ts +40 -3
- package/dist/solid/index.js +646 -190
- package/dist/solid/index.js.map +1 -1
- package/dist/store/index.cjs +272 -41
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +30 -3
- package/dist/store/index.d.ts +30 -3
- package/dist/store/index.js +269 -40
- package/dist/store/index.js.map +1 -1
- package/dist/styles.css +105 -7
- package/dist/svelte/components/DialRoot.svelte +176 -11
- 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 +98 -71
- package/dist/svelte/components/Panel.svelte.d.ts +2 -0
- package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
- package/dist/svelte/components/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/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/index.d.ts +3 -3
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -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 +105 -7
- package/dist/vue/index.cjs +573 -132
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +53 -6
- package/dist/vue/index.d.ts +53 -6
- package/dist/vue/index.js +573 -133
- package/dist/vue/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type PanelDragOffset = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
type PanelDragStart = {
|
|
6
|
+
pointerX: number;
|
|
7
|
+
pointerY: number;
|
|
8
|
+
elX: number;
|
|
9
|
+
elY: number;
|
|
10
|
+
};
|
|
11
|
+
type PanelDragOriginX = 'left' | 'right';
|
|
12
|
+
declare function getPanelDragHandle(target: EventTarget | null, panel: HTMLElement | null): HTMLElement | null;
|
|
13
|
+
declare function getPanelDragStart(pointerX: number, pointerY: number, panel: HTMLElement): PanelDragStart;
|
|
14
|
+
declare function getPanelDragOffset(start: PanelDragStart, pointerX: number, pointerY: number): PanelDragOffset;
|
|
15
|
+
declare function hasPanelDragMoved(start: PanelDragStart, pointerX: number, pointerY: number): boolean;
|
|
16
|
+
declare function getPanelOriginX(position: string, offset: PanelDragOffset | null, viewportWidth?: number | undefined): PanelDragOriginX;
|
|
17
|
+
declare function blockPanelDragClick(handle: HTMLElement): void;
|
|
18
|
+
|
|
19
|
+
export { type PanelDragOffset, type PanelDragOriginX, type PanelDragStart, blockPanelDragClick, getPanelDragHandle, getPanelDragOffset, getPanelDragStart, getPanelOriginX, hasPanelDragMoved };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/panel-drag.ts
|
|
2
|
+
var PANEL_DRAG_THRESHOLD = 8;
|
|
3
|
+
var COLLAPSED_PANEL_SIZE = 42;
|
|
4
|
+
var DRAG_EXCLUSION_SELECTOR = [
|
|
5
|
+
".dialkit-panel-icon",
|
|
6
|
+
".dialkit-panel-toolbar",
|
|
7
|
+
"button",
|
|
8
|
+
"input",
|
|
9
|
+
"select",
|
|
10
|
+
"textarea",
|
|
11
|
+
"a",
|
|
12
|
+
'[role="button"]',
|
|
13
|
+
'[contenteditable="true"]'
|
|
14
|
+
].join(",");
|
|
15
|
+
function getPanelDragHandle(target, panel) {
|
|
16
|
+
if (!(target instanceof Element) || !panel) return null;
|
|
17
|
+
const inner = target.closest(".dialkit-panel-inner");
|
|
18
|
+
if (!inner || !panel.contains(inner)) return null;
|
|
19
|
+
if (inner.getAttribute("data-collapsed") === "true") {
|
|
20
|
+
return inner;
|
|
21
|
+
}
|
|
22
|
+
const header = target.closest(".dialkit-panel-header");
|
|
23
|
+
if (!header || !inner.contains(header)) return null;
|
|
24
|
+
if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;
|
|
25
|
+
return header;
|
|
26
|
+
}
|
|
27
|
+
function getPanelDragStart(pointerX, pointerY, panel) {
|
|
28
|
+
const rect = panel.getBoundingClientRect();
|
|
29
|
+
return {
|
|
30
|
+
pointerX,
|
|
31
|
+
pointerY,
|
|
32
|
+
elX: rect.left,
|
|
33
|
+
elY: rect.top
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getPanelDragOffset(start, pointerX, pointerY) {
|
|
37
|
+
return {
|
|
38
|
+
x: start.elX + pointerX - start.pointerX,
|
|
39
|
+
y: start.elY + pointerY - start.pointerY
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function hasPanelDragMoved(start, pointerX, pointerY) {
|
|
43
|
+
const dx = pointerX - start.pointerX;
|
|
44
|
+
const dy = pointerY - start.pointerY;
|
|
45
|
+
return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;
|
|
46
|
+
}
|
|
47
|
+
function getPanelOriginX(position, offset, viewportWidth = typeof window !== "undefined" ? window.innerWidth : void 0) {
|
|
48
|
+
if (offset && viewportWidth) {
|
|
49
|
+
return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? "left" : "right";
|
|
50
|
+
}
|
|
51
|
+
return position.endsWith("left") ? "left" : "right";
|
|
52
|
+
}
|
|
53
|
+
function blockPanelDragClick(handle) {
|
|
54
|
+
const blocker = (event) => {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
event.stopImmediatePropagation();
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
};
|
|
59
|
+
handle.addEventListener("click", blocker, { capture: true, once: true });
|
|
60
|
+
window.setTimeout(() => {
|
|
61
|
+
handle.removeEventListener("click", blocker, true);
|
|
62
|
+
}, 0);
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
blockPanelDragClick,
|
|
66
|
+
getPanelDragHandle,
|
|
67
|
+
getPanelDragOffset,
|
|
68
|
+
getPanelDragStart,
|
|
69
|
+
getPanelOriginX,
|
|
70
|
+
hasPanelDragMoved
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=panel-drag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/panel-drag.ts"],"sourcesContent":["export type PanelDragOffset = {\n x: number;\n y: number;\n};\n\nexport type PanelDragStart = {\n pointerX: number;\n pointerY: number;\n elX: number;\n elY: number;\n};\n\nexport type PanelDragOriginX = 'left' | 'right';\n\nconst PANEL_DRAG_THRESHOLD = 8;\nconst COLLAPSED_PANEL_SIZE = 42;\n\nconst DRAG_EXCLUSION_SELECTOR = [\n '.dialkit-panel-icon',\n '.dialkit-panel-toolbar',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'a',\n '[role=\"button\"]',\n '[contenteditable=\"true\"]',\n].join(',');\n\nexport function getPanelDragHandle(target: EventTarget | null, panel: HTMLElement | null): HTMLElement | null {\n if (!(target instanceof Element) || !panel) return null;\n\n const inner = target.closest<HTMLElement>('.dialkit-panel-inner');\n if (!inner || !panel.contains(inner)) return null;\n\n if (inner.getAttribute('data-collapsed') === 'true') {\n return inner;\n }\n\n const header = target.closest<HTMLElement>('.dialkit-panel-header');\n if (!header || !inner.contains(header)) return null;\n\n if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;\n\n return header;\n}\n\nexport function getPanelDragStart(pointerX: number, pointerY: number, panel: HTMLElement): PanelDragStart {\n const rect = panel.getBoundingClientRect();\n return {\n pointerX,\n pointerY,\n elX: rect.left,\n elY: rect.top,\n };\n}\n\nexport function getPanelDragOffset(start: PanelDragStart, pointerX: number, pointerY: number): PanelDragOffset {\n return {\n x: start.elX + pointerX - start.pointerX,\n y: start.elY + pointerY - start.pointerY,\n };\n}\n\nexport function hasPanelDragMoved(start: PanelDragStart, pointerX: number, pointerY: number): boolean {\n const dx = pointerX - start.pointerX;\n const dy = pointerY - start.pointerY;\n return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;\n}\n\nexport function getPanelOriginX(\n position: string,\n offset: PanelDragOffset | null,\n viewportWidth = typeof window !== 'undefined' ? window.innerWidth : undefined\n): PanelDragOriginX {\n if (offset && viewportWidth) {\n return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? 'left' : 'right';\n }\n\n return position.endsWith('left') ? 'left' : 'right';\n}\n\nexport function blockPanelDragClick(handle: HTMLElement) {\n const blocker = (event: Event) => {\n event.preventDefault();\n event.stopImmediatePropagation();\n event.stopPropagation();\n };\n\n handle.addEventListener('click', blocker, { capture: true, once: true });\n window.setTimeout(() => {\n handle.removeEventListener('click', blocker, true);\n }, 0);\n}\n"],"mappings":";AAcA,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;AAE7B,IAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEH,SAAS,mBAAmB,QAA4B,OAA+C;AAC5G,MAAI,EAAE,kBAAkB,YAAY,CAAC,MAAO,QAAO;AAEnD,QAAM,QAAQ,OAAO,QAAqB,sBAAsB;AAChE,MAAI,CAAC,SAAS,CAAC,MAAM,SAAS,KAAK,EAAG,QAAO;AAE7C,MAAI,MAAM,aAAa,gBAAgB,MAAM,QAAQ;AACnD,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,QAAqB,uBAAuB;AAClE,MAAI,CAAC,UAAU,CAAC,MAAM,SAAS,MAAM,EAAG,QAAO;AAE/C,MAAI,OAAO,QAAQ,uBAAuB,EAAG,QAAO;AAEpD,SAAO;AACT;AAEO,SAAS,kBAAkB,UAAkB,UAAkB,OAAoC;AACxG,QAAM,OAAO,MAAM,sBAAsB;AACzC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK,KAAK;AAAA,IACV,KAAK,KAAK;AAAA,EACZ;AACF;AAEO,SAAS,mBAAmB,OAAuB,UAAkB,UAAmC;AAC7G,SAAO;AAAA,IACL,GAAG,MAAM,MAAM,WAAW,MAAM;AAAA,IAChC,GAAG,MAAM,MAAM,WAAW,MAAM;AAAA,EAClC;AACF;AAEO,SAAS,kBAAkB,OAAuB,UAAkB,UAA2B;AACpG,QAAM,KAAK,WAAW,MAAM;AAC5B,QAAM,KAAK,WAAW,MAAM;AAC5B,SAAO,KAAK,MAAM,IAAI,EAAE,KAAK;AAC/B;AAEO,SAAS,gBACd,UACA,QACA,gBAAgB,OAAO,WAAW,cAAc,OAAO,aAAa,QAClD;AAClB,MAAI,UAAU,eAAe;AAC3B,WAAO,OAAO,IAAI,uBAAuB,IAAI,gBAAgB,IAAI,SAAS;AAAA,EAC5E;AAEA,SAAO,SAAS,SAAS,MAAM,IAAI,SAAS;AAC9C;AAEO,SAAS,oBAAoB,QAAqB;AACvD,QAAM,UAAU,CAAC,UAAiB;AAChC,UAAM,eAAe;AACrB,UAAM,yBAAyB;AAC/B,UAAM,gBAAgB;AAAA,EACxB;AAEA,SAAO,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,MAAM,KAAK,CAAC;AACvE,SAAO,WAAW,MAAM;AACtB,WAAO,oBAAoB,SAAS,SAAS,IAAI;AAAA,EACnD,GAAG,CAAC;AACN;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type ShortcutMode = 'fine' | 'normal' | 'coarse';
|
|
2
|
+
type ShortcutInteraction = 'scroll' | 'drag' | 'move' | 'scroll-only';
|
|
3
|
+
type ShortcutConfig = {
|
|
4
|
+
key?: string;
|
|
5
|
+
modifier?: 'alt' | 'shift' | 'meta';
|
|
6
|
+
mode?: ShortcutMode;
|
|
7
|
+
interaction?: ShortcutInteraction;
|
|
8
|
+
};
|
|
9
|
+
type ControlMeta = {
|
|
10
|
+
type: 'slider' | 'toggle' | 'spring' | 'transition' | 'folder' | 'action' | 'select' | 'color' | 'text';
|
|
11
|
+
path: string;
|
|
12
|
+
label: string;
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
step?: number;
|
|
16
|
+
children?: ControlMeta[];
|
|
17
|
+
defaultOpen?: boolean;
|
|
18
|
+
options?: (string | {
|
|
19
|
+
value: string;
|
|
20
|
+
label: string;
|
|
21
|
+
})[];
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
shortcut?: ShortcutConfig;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
declare function decimalsForStep(step: number): number;
|
|
27
|
+
declare function roundValue(val: number, step: number): number;
|
|
28
|
+
declare function getEffectiveStep(control: ControlMeta, shortcut: ShortcutConfig): number;
|
|
29
|
+
declare function applySliderDelta(panelId: string, path: string, control: ControlMeta, effectiveStep: number, direction: number): void;
|
|
30
|
+
declare function snapToDecile(rawValue: number, min: number, max: number): number;
|
|
31
|
+
declare function isInputFocused(): boolean;
|
|
32
|
+
declare function getActiveModifier(e: KeyboardEvent | WheelEvent | MouseEvent): 'alt' | 'shift' | 'meta' | undefined;
|
|
33
|
+
declare function findControl(controls: ControlMeta[], path: string): ControlMeta | null;
|
|
34
|
+
declare const DRAG_SENSITIVITY = 4;
|
|
35
|
+
declare function formatInteractionLabel(interaction: string): string;
|
|
36
|
+
declare function formatSliderShortcut(sc: ShortcutConfig): string;
|
|
37
|
+
declare function formatToggleShortcut(sc: ShortcutConfig): string;
|
|
38
|
+
|
|
39
|
+
export { DRAG_SENSITIVITY, applySliderDelta, decimalsForStep, findControl, formatInteractionLabel, formatSliderShortcut, formatToggleShortcut, getActiveModifier, getEffectiveStep, isInputFocused, roundValue, snapToDecile };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/shortcut-utils.ts
|
|
2
|
+
import { DialStore } from "dialkit/store";
|
|
3
|
+
function decimalsForStep(step) {
|
|
4
|
+
const s = step.toString();
|
|
5
|
+
const dot = s.indexOf(".");
|
|
6
|
+
return dot === -1 ? 0 : s.length - dot - 1;
|
|
7
|
+
}
|
|
8
|
+
function roundValue(val, step) {
|
|
9
|
+
const raw = Math.round(val / step) * step;
|
|
10
|
+
return parseFloat(raw.toFixed(decimalsForStep(step)));
|
|
11
|
+
}
|
|
12
|
+
function getEffectiveStep(control, shortcut) {
|
|
13
|
+
const min = control.min ?? 0;
|
|
14
|
+
const max = control.max ?? 1;
|
|
15
|
+
const range = max - min;
|
|
16
|
+
const mode = shortcut.mode ?? "normal";
|
|
17
|
+
return mode === "fine" ? range * 0.01 : mode === "coarse" ? range * 0.1 : control.step ?? 1;
|
|
18
|
+
}
|
|
19
|
+
function applySliderDelta(panelId, path, control, effectiveStep, direction) {
|
|
20
|
+
const currentValue = DialStore.getValue(panelId, path);
|
|
21
|
+
const min = control.min ?? 0;
|
|
22
|
+
const max = control.max ?? 1;
|
|
23
|
+
const newValue = Math.max(min, Math.min(max, currentValue + direction * effectiveStep));
|
|
24
|
+
DialStore.updateValue(panelId, path, roundValue(newValue, effectiveStep));
|
|
25
|
+
}
|
|
26
|
+
function snapToDecile(rawValue, min, max) {
|
|
27
|
+
const normalized = (rawValue - min) / (max - min);
|
|
28
|
+
const nearest = Math.round(normalized * 10) / 10;
|
|
29
|
+
if (Math.abs(normalized - nearest) <= 0.03125) {
|
|
30
|
+
return min + nearest * (max - min);
|
|
31
|
+
}
|
|
32
|
+
return rawValue;
|
|
33
|
+
}
|
|
34
|
+
function isInputFocused() {
|
|
35
|
+
const el = document.activeElement;
|
|
36
|
+
if (!el) return false;
|
|
37
|
+
const tag = el.tagName;
|
|
38
|
+
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
39
|
+
if (el.contentEditable === "true") return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function getActiveModifier(e) {
|
|
43
|
+
if (e.altKey) return "alt";
|
|
44
|
+
if (e.shiftKey) return "shift";
|
|
45
|
+
if (e.metaKey) return "meta";
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
function findControl(controls, path) {
|
|
49
|
+
for (const control of controls) {
|
|
50
|
+
if (control.path === path) return control;
|
|
51
|
+
if (control.type === "folder" && control.children) {
|
|
52
|
+
const found = findControl(control.children, path);
|
|
53
|
+
if (found) return found;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
var DRAG_SENSITIVITY = 4;
|
|
59
|
+
function formatInteractionLabel(interaction) {
|
|
60
|
+
switch (interaction) {
|
|
61
|
+
case "drag":
|
|
62
|
+
return "Drag";
|
|
63
|
+
case "move":
|
|
64
|
+
return "Move";
|
|
65
|
+
case "scroll-only":
|
|
66
|
+
return "Scroll";
|
|
67
|
+
default:
|
|
68
|
+
return "Scroll";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function formatSliderShortcut(sc) {
|
|
72
|
+
const interaction = sc.interaction ?? "scroll";
|
|
73
|
+
const actionLabel = formatInteractionLabel(interaction);
|
|
74
|
+
if (!sc.key) return actionLabel;
|
|
75
|
+
const mod = formatModifier(sc.modifier);
|
|
76
|
+
return `${mod}${sc.key.toUpperCase()}+${actionLabel}`;
|
|
77
|
+
}
|
|
78
|
+
function formatToggleShortcut(sc) {
|
|
79
|
+
if (!sc.key) return "Press";
|
|
80
|
+
const mod = formatModifier(sc.modifier);
|
|
81
|
+
return `${mod}${sc.key.toUpperCase()}`;
|
|
82
|
+
}
|
|
83
|
+
function formatModifier(modifier) {
|
|
84
|
+
return modifier === "alt" ? "\u2325" : modifier === "shift" ? "\u21E7" : modifier === "meta" ? "\u2318" : "";
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
DRAG_SENSITIVITY,
|
|
88
|
+
applySliderDelta,
|
|
89
|
+
decimalsForStep,
|
|
90
|
+
findControl,
|
|
91
|
+
formatInteractionLabel,
|
|
92
|
+
formatSliderShortcut,
|
|
93
|
+
formatToggleShortcut,
|
|
94
|
+
getActiveModifier,
|
|
95
|
+
getEffectiveStep,
|
|
96
|
+
isInputFocused,
|
|
97
|
+
roundValue,
|
|
98
|
+
snapToDecile
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=shortcut-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shortcut-utils.ts"],"sourcesContent":["// Shared shortcut utilities — single source of truth across all framework adapters.\n\nimport { DialStore } from './store/DialStore';\nimport type { ControlMeta, ShortcutConfig } from './store/DialStore';\n\n// ── Math helpers ──\n\nexport function decimalsForStep(step: number): number {\n const s = step.toString();\n const dot = s.indexOf('.');\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\nexport function roundValue(val: number, step: number): number {\n const raw = Math.round(val / step) * step;\n return parseFloat(raw.toFixed(decimalsForStep(step)));\n}\n\nexport function getEffectiveStep(control: ControlMeta, shortcut: ShortcutConfig): number {\n const min = control.min ?? 0;\n const max = control.max ?? 1;\n const range = max - min;\n const mode = shortcut.mode ?? 'normal';\n return mode === 'fine' ? range * 0.01\n : mode === 'coarse' ? range * 0.1\n : control.step ?? 1;\n}\n\nexport function applySliderDelta(\n panelId: string,\n path: string,\n control: ControlMeta,\n effectiveStep: number,\n direction: number\n): void {\n const currentValue = DialStore.getValue(panelId, path) as number;\n const min = control.min ?? 0;\n const max = control.max ?? 1;\n const newValue = Math.max(min, Math.min(max, currentValue + direction * effectiveStep));\n DialStore.updateValue(panelId, path, roundValue(newValue, effectiveStep));\n}\n\nexport function snapToDecile(rawValue: number, min: number, max: number): number {\n const normalized = (rawValue - min) / (max - min);\n const nearest = Math.round(normalized * 10) / 10;\n if (Math.abs(normalized - nearest) <= 0.03125) {\n return min + nearest * (max - min);\n }\n return rawValue;\n}\n\n// ── DOM helpers ──\n\nexport function isInputFocused(): boolean {\n const el = document.activeElement;\n if (!el) return false;\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA') return true;\n if ((el as HTMLElement).contentEditable === 'true') return true;\n return false;\n}\n\nexport function getActiveModifier(e: KeyboardEvent | WheelEvent | MouseEvent): 'alt' | 'shift' | 'meta' | undefined {\n if (e.altKey) return 'alt';\n if (e.shiftKey) return 'shift';\n if (e.metaKey) return 'meta';\n return undefined;\n}\n\nexport function findControl(controls: ControlMeta[], path: string): ControlMeta | null {\n for (const control of controls) {\n if (control.path === path) return control;\n if (control.type === 'folder' && control.children) {\n const found = findControl(control.children, path);\n if (found) return found;\n }\n }\n return null;\n}\n\nexport const DRAG_SENSITIVITY = 4;\n\n// ── Formatting helpers ──\n\nexport function formatInteractionLabel(interaction: string): string {\n switch (interaction) {\n case 'drag': return 'Drag';\n case 'move': return 'Move';\n case 'scroll-only': return 'Scroll';\n default: return 'Scroll';\n }\n}\n\nexport function formatSliderShortcut(sc: ShortcutConfig): string {\n const interaction = sc.interaction ?? 'scroll';\n const actionLabel = formatInteractionLabel(interaction);\n if (!sc.key) return actionLabel;\n const mod = formatModifier(sc.modifier);\n return `${mod}${sc.key.toUpperCase()}+${actionLabel}`;\n}\n\nexport function formatToggleShortcut(sc: ShortcutConfig): string {\n if (!sc.key) return 'Press';\n const mod = formatModifier(sc.modifier);\n return `${mod}${sc.key.toUpperCase()}`;\n}\n\nfunction formatModifier(modifier?: string): string {\n return modifier === 'alt' ? '\\u2325'\n : modifier === 'shift' ? '\\u21E7'\n : modifier === 'meta' ? '\\u2318'\n : '';\n}\n"],"mappings":";AAEA,SAAS,iBAAiB;AAKnB,SAAS,gBAAgB,MAAsB;AACpD,QAAM,IAAI,KAAK,SAAS;AACxB,QAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,SAAO,QAAQ,KAAK,IAAI,EAAE,SAAS,MAAM;AAC3C;AAEO,SAAS,WAAW,KAAa,MAAsB;AAC5D,QAAM,MAAM,KAAK,MAAM,MAAM,IAAI,IAAI;AACrC,SAAO,WAAW,IAAI,QAAQ,gBAAgB,IAAI,CAAC,CAAC;AACtD;AAEO,SAAS,iBAAiB,SAAsB,UAAkC;AACvF,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,QAAQ,MAAM;AACpB,QAAM,OAAO,SAAS,QAAQ;AAC9B,SAAO,SAAS,SAAS,QAAQ,OAC7B,SAAS,WAAW,QAAQ,MAC5B,QAAQ,QAAQ;AACtB;AAEO,SAAS,iBACd,SACA,MACA,SACA,eACA,WACM;AACN,QAAM,eAAe,UAAU,SAAS,SAAS,IAAI;AACrD,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,WAAW,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,eAAe,YAAY,aAAa,CAAC;AACtF,YAAU,YAAY,SAAS,MAAM,WAAW,UAAU,aAAa,CAAC;AAC1E;AAEO,SAAS,aAAa,UAAkB,KAAa,KAAqB;AAC/E,QAAM,cAAc,WAAW,QAAQ,MAAM;AAC7C,QAAM,UAAU,KAAK,MAAM,aAAa,EAAE,IAAI;AAC9C,MAAI,KAAK,IAAI,aAAa,OAAO,KAAK,SAAS;AAC7C,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC;AACA,SAAO;AACT;AAIO,SAAS,iBAA0B;AACxC,QAAM,KAAK,SAAS;AACpB,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,MAAM,GAAG;AACf,MAAI,QAAQ,WAAW,QAAQ,WAAY,QAAO;AAClD,MAAK,GAAmB,oBAAoB,OAAQ,QAAO;AAC3D,SAAO;AACT;AAEO,SAAS,kBAAkB,GAAkF;AAClH,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,SAAU,QAAO;AACvB,MAAI,EAAE,QAAS,QAAO;AACtB,SAAO;AACT;AAEO,SAAS,YAAY,UAAyB,MAAkC;AACrF,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,KAAM,QAAO;AAClC,QAAI,QAAQ,SAAS,YAAY,QAAQ,UAAU;AACjD,YAAM,QAAQ,YAAY,QAAQ,UAAU,IAAI;AAChD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,mBAAmB;AAIzB,SAAS,uBAAuB,aAA6B;AAClE,UAAQ,aAAa;AAAA,IACnB,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAe,aAAO;AAAA,IAC3B;AAAS,aAAO;AAAA,EAClB;AACF;AAEO,SAAS,qBAAqB,IAA4B;AAC/D,QAAM,cAAc,GAAG,eAAe;AACtC,QAAM,cAAc,uBAAuB,WAAW;AACtD,MAAI,CAAC,GAAG,IAAK,QAAO;AACpB,QAAM,MAAM,eAAe,GAAG,QAAQ;AACtC,SAAO,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,CAAC,IAAI,WAAW;AACrD;AAEO,SAAS,qBAAqB,IAA4B;AAC/D,MAAI,CAAC,GAAG,IAAK,QAAO;AACpB,QAAM,MAAM,eAAe,GAAG,QAAQ;AACtC,SAAO,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,CAAC;AACtC;AAEA,SAAS,eAAe,UAA2B;AACjD,SAAO,aAAa,QAAQ,WACxB,aAAa,UAAU,WACvB,aAAa,SAAS,WACtB;AACN;","names":[]}
|