@rohal12/spindle 0.26.2 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pkg/format.js +1 -1
- package/package.json +1 -1
- package/src/components/PassageDialog.tsx +10 -6
- package/src/components/TriggerDialogHost.tsx +24 -11
- package/src/components/macros/Dialog.tsx +6 -1
- package/src/components/macros/MenubarButtons.tsx +2 -0
- package/src/story-api.ts +39 -1
- package/src/triggers.ts +40 -3
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ interface PassageDialogProps {
|
|
|
12
12
|
fallbackMarkup?: string;
|
|
13
13
|
panelClass?: string;
|
|
14
14
|
onClose: () => void;
|
|
15
|
+
showCloseButton?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function PassageDialog({
|
|
@@ -19,6 +20,7 @@ export function PassageDialog({
|
|
|
19
20
|
fallbackMarkup,
|
|
20
21
|
panelClass,
|
|
21
22
|
onClose,
|
|
23
|
+
showCloseButton = true,
|
|
22
24
|
}: PassageDialogProps) {
|
|
23
25
|
const storyData = useStoryStore((s) => s.storyData);
|
|
24
26
|
|
|
@@ -55,12 +57,14 @@ export function PassageDialog({
|
|
|
55
57
|
onClick={handleBackdrop}
|
|
56
58
|
>
|
|
57
59
|
<div class={cls}>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
{showCloseButton && (
|
|
61
|
+
<button
|
|
62
|
+
class="dialog-close"
|
|
63
|
+
onClick={onClose}
|
|
64
|
+
>
|
|
65
|
+
✕
|
|
66
|
+
</button>
|
|
67
|
+
)}
|
|
64
68
|
<div class="dialog-body">{content}</div>
|
|
65
69
|
</div>
|
|
66
70
|
</div>
|
|
@@ -1,19 +1,38 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from 'preact/hooks';
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
2
2
|
import { PassageDialog } from './PassageDialog';
|
|
3
3
|
import {
|
|
4
4
|
subscribeTriggerDialogs,
|
|
5
5
|
shiftDialogQueue,
|
|
6
6
|
dialogQueueLength,
|
|
7
|
+
registerDialogHost,
|
|
7
8
|
} from '../triggers';
|
|
9
|
+
import type { QueuedDialog } from '../triggers';
|
|
8
10
|
|
|
9
11
|
export function TriggerDialogHost() {
|
|
10
|
-
const [current, setCurrent] = useState<
|
|
12
|
+
const [current, setCurrent] = useState<QueuedDialog | null>(null);
|
|
13
|
+
const currentRef = useRef(current);
|
|
14
|
+
currentRef.current = current;
|
|
11
15
|
|
|
12
16
|
const advance = useCallback(() => {
|
|
13
17
|
const next = shiftDialogQueue();
|
|
14
18
|
setCurrent(next ?? null);
|
|
15
19
|
}, []);
|
|
16
20
|
|
|
21
|
+
const handleClose = useCallback(() => {
|
|
22
|
+
setCurrent(null);
|
|
23
|
+
// Show next queued dialog after a tick
|
|
24
|
+
if (dialogQueueLength() > 0) {
|
|
25
|
+
requestAnimationFrame(advance);
|
|
26
|
+
}
|
|
27
|
+
}, [advance]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
return registerDialogHost({
|
|
31
|
+
close: handleClose,
|
|
32
|
+
isOpen: () => currentRef.current !== null,
|
|
33
|
+
});
|
|
34
|
+
}, [handleClose]);
|
|
35
|
+
|
|
17
36
|
useEffect(() => {
|
|
18
37
|
return subscribeTriggerDialogs(() => {
|
|
19
38
|
setCurrent((prev) => {
|
|
@@ -23,20 +42,14 @@ export function TriggerDialogHost() {
|
|
|
23
42
|
});
|
|
24
43
|
}, []);
|
|
25
44
|
|
|
26
|
-
const handleClose = useCallback(() => {
|
|
27
|
-
setCurrent(null);
|
|
28
|
-
// Show next queued dialog after a tick
|
|
29
|
-
if (dialogQueueLength() > 0) {
|
|
30
|
-
requestAnimationFrame(advance);
|
|
31
|
-
}
|
|
32
|
-
}, [advance]);
|
|
33
|
-
|
|
34
45
|
if (!current) return null;
|
|
35
46
|
|
|
36
47
|
return (
|
|
37
48
|
<PassageDialog
|
|
38
|
-
passageName={current}
|
|
49
|
+
passageName={current.passageName}
|
|
50
|
+
panelClass={current.panelClass}
|
|
39
51
|
onClose={handleClose}
|
|
52
|
+
showCloseButton={current.showCloseButton}
|
|
40
53
|
/>
|
|
41
54
|
);
|
|
42
55
|
}
|
|
@@ -3,11 +3,15 @@ import { PassageDialog } from '../PassageDialog';
|
|
|
3
3
|
|
|
4
4
|
defineMacro({
|
|
5
5
|
name: 'dialog',
|
|
6
|
+
block: true,
|
|
6
7
|
interpolate: true,
|
|
7
8
|
render({ rawArgs, children = [] }, ctx) {
|
|
8
9
|
const [open, setOpen] = ctx.hooks.useState(false);
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
+
const noclose = /\bnoclose\s*$/.test(rawArgs);
|
|
12
|
+
const labelRaw = rawArgs.replace(/\bnoclose\s*$/, '').trim();
|
|
13
|
+
const label =
|
|
14
|
+
ctx.resolve?.(labelRaw.replace(/^["']|["']$/g, '')) ?? labelRaw;
|
|
11
15
|
const passageName = ctx
|
|
12
16
|
.collectText(children)
|
|
13
17
|
.trim()
|
|
@@ -26,6 +30,7 @@ defineMacro({
|
|
|
26
30
|
<PassageDialog
|
|
27
31
|
passageName={passageName}
|
|
28
32
|
onClose={() => setOpen(false)}
|
|
33
|
+
showCloseButton={!noclose}
|
|
29
34
|
/>
|
|
30
35
|
)}
|
|
31
36
|
</>
|
|
@@ -18,6 +18,7 @@ interface MenubarActionConfig {
|
|
|
18
18
|
passageName: string;
|
|
19
19
|
fallbackMarkup?: string;
|
|
20
20
|
panelClass?: string;
|
|
21
|
+
showCloseButton?: boolean;
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -72,6 +73,7 @@ export function defineMenubarAction(config: MenubarActionConfig) {
|
|
|
72
73
|
fallbackMarkup={config.dialog.fallbackMarkup}
|
|
73
74
|
panelClass={config.dialog.panelClass}
|
|
74
75
|
onClose={() => setDialogOpen(false)}
|
|
76
|
+
showCloseButton={config.dialog.showCloseButton}
|
|
75
77
|
/>
|
|
76
78
|
)}
|
|
77
79
|
</>
|
package/src/story-api.ts
CHANGED
|
@@ -21,7 +21,14 @@ import {
|
|
|
21
21
|
randomInt,
|
|
22
22
|
snapshotPRNG,
|
|
23
23
|
} from './prng';
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
addTrigger,
|
|
26
|
+
removeTrigger,
|
|
27
|
+
pushDialog,
|
|
28
|
+
clearDialogQueue,
|
|
29
|
+
closeCurrentDialog,
|
|
30
|
+
isDialogShowing,
|
|
31
|
+
} from './triggers';
|
|
25
32
|
import type { WatchOptions } from './triggers';
|
|
26
33
|
import type { TransitionConfig } from './transition';
|
|
27
34
|
|
|
@@ -73,6 +80,13 @@ export interface StoryAPI {
|
|
|
73
80
|
callbackOrOptions: (() => void) | WatchOptions,
|
|
74
81
|
): () => void;
|
|
75
82
|
unwatch(name: string): void;
|
|
83
|
+
openDialog(
|
|
84
|
+
passageName: string,
|
|
85
|
+
options?: { panelClass?: string; showCloseButton?: boolean },
|
|
86
|
+
): void;
|
|
87
|
+
closeDialog(): void;
|
|
88
|
+
closeAllDialogs(): void;
|
|
89
|
+
isDialogOpen(): boolean;
|
|
76
90
|
setNobr(enabled: boolean): void;
|
|
77
91
|
setCSS(enabled: boolean): void;
|
|
78
92
|
setTransition(config: TransitionConfig | null): void;
|
|
@@ -286,6 +300,30 @@ function createStoryAPI(): StoryAPI {
|
|
|
286
300
|
removeTrigger(name);
|
|
287
301
|
},
|
|
288
302
|
|
|
303
|
+
openDialog(
|
|
304
|
+
passageName: string,
|
|
305
|
+
options?: { panelClass?: string; showCloseButton?: boolean },
|
|
306
|
+
): void {
|
|
307
|
+
pushDialog({
|
|
308
|
+
passageName,
|
|
309
|
+
panelClass: options?.panelClass,
|
|
310
|
+
showCloseButton: options?.showCloseButton,
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
closeDialog(): void {
|
|
315
|
+
closeCurrentDialog();
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
closeAllDialogs(): void {
|
|
319
|
+
clearDialogQueue();
|
|
320
|
+
closeCurrentDialog();
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
isDialogOpen(): boolean {
|
|
324
|
+
return isDialogShowing();
|
|
325
|
+
},
|
|
326
|
+
|
|
289
327
|
setNobr(enabled: boolean): void {
|
|
290
328
|
useStoryStore.setState({ nobr: enabled });
|
|
291
329
|
},
|
package/src/triggers.ts
CHANGED
|
@@ -10,6 +10,12 @@ export interface WatchOptions {
|
|
|
10
10
|
priority?: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export interface QueuedDialog {
|
|
14
|
+
passageName: string;
|
|
15
|
+
panelClass?: string;
|
|
16
|
+
showCloseButton?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
interface Trigger {
|
|
14
20
|
id: number;
|
|
15
21
|
name?: string;
|
|
@@ -25,9 +31,15 @@ let triggers: Trigger[] = [];
|
|
|
25
31
|
let checking = false;
|
|
26
32
|
|
|
27
33
|
type DialogCallback = () => void;
|
|
28
|
-
let dialogQueue:
|
|
34
|
+
let dialogQueue: QueuedDialog[] = [];
|
|
29
35
|
let dialogNotify: DialogCallback | null = null;
|
|
30
36
|
|
|
37
|
+
interface DialogHostCallbacks {
|
|
38
|
+
close: () => void;
|
|
39
|
+
isOpen: () => boolean;
|
|
40
|
+
}
|
|
41
|
+
let dialogHostCallbacks: DialogHostCallbacks | null = null;
|
|
42
|
+
|
|
31
43
|
function evalCondition(condition: string): boolean {
|
|
32
44
|
const state = useStoryStore.getState();
|
|
33
45
|
try {
|
|
@@ -84,7 +96,7 @@ function fireTrigger(trigger: Trigger): void {
|
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
if (options.dialog) {
|
|
87
|
-
dialogQueue.push(options.dialog);
|
|
99
|
+
dialogQueue.push({ passageName: options.dialog });
|
|
88
100
|
dialogNotify?.();
|
|
89
101
|
}
|
|
90
102
|
|
|
@@ -141,6 +153,7 @@ export function resetTriggers(): void {
|
|
|
141
153
|
triggers = [];
|
|
142
154
|
dialogQueue = [];
|
|
143
155
|
nextId = 0;
|
|
156
|
+
dialogHostCallbacks?.close();
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
export function subscribeTriggerDialogs(cb: () => void): () => void {
|
|
@@ -152,10 +165,34 @@ export function subscribeTriggerDialogs(cb: () => void): () => void {
|
|
|
152
165
|
};
|
|
153
166
|
}
|
|
154
167
|
|
|
155
|
-
export function shiftDialogQueue():
|
|
168
|
+
export function shiftDialogQueue(): QueuedDialog | undefined {
|
|
156
169
|
return dialogQueue.shift();
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
export function dialogQueueLength(): number {
|
|
160
173
|
return dialogQueue.length;
|
|
161
174
|
}
|
|
175
|
+
|
|
176
|
+
export function pushDialog(item: QueuedDialog): void {
|
|
177
|
+
dialogQueue.push(item);
|
|
178
|
+
dialogNotify?.();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function clearDialogQueue(): void {
|
|
182
|
+
dialogQueue = [];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function registerDialogHost(callbacks: DialogHostCallbacks): () => void {
|
|
186
|
+
dialogHostCallbacks = callbacks;
|
|
187
|
+
return () => {
|
|
188
|
+
if (dialogHostCallbacks === callbacks) dialogHostCallbacks = null;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function closeCurrentDialog(): void {
|
|
193
|
+
dialogHostCallbacks?.close();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isDialogShowing(): boolean {
|
|
197
|
+
return dialogHostCallbacks?.isOpen() ?? false;
|
|
198
|
+
}
|