wyreframe 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +110 -1
- package/src/index.ts +6 -0
- package/src/renderer/Renderer.gen.tsx +21 -1
- package/src/renderer/Renderer.mjs +79 -27
- package/src/renderer/Renderer.res +119 -16
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,12 @@ export interface RenderOptions {
|
|
|
118
118
|
* @param toScene - The scene ID navigating to
|
|
119
119
|
*/
|
|
120
120
|
onSceneChange?: OnSceneChangeCallback;
|
|
121
|
+
/**
|
|
122
|
+
* Override the device type for all scenes.
|
|
123
|
+
* When provided, this overrides the device type defined in scene definitions.
|
|
124
|
+
* Useful for previewing wireframes in different device contexts without modifying the source.
|
|
125
|
+
*/
|
|
126
|
+
device?: DeviceType;
|
|
121
127
|
}
|
|
122
128
|
/** Render result */
|
|
123
129
|
export interface RenderResult {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0EAA0E;AAC1E,yFAAyF;AACzF,yCAAyC;AACzC,OAAO,KAAK,MAAM,MAAM,0BAA0B,CAAC;AACnD,yCAAyC;AACzC,OAAO,KAAK,QAAQ,MAAM,8BAA8B,CAAC;AACzD,yCAAyC;AACzC,OAAO,KAAK,KAAK,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { describe, test, expect, vi } from 'vitest';
|
|
9
9
|
import { parse, render, createUI } from './index';
|
|
10
|
-
import type { AST, ParseResult, OnSceneChangeCallback } from './index';
|
|
10
|
+
import type { AST, ParseResult, OnSceneChangeCallback, DeviceType, RenderOptions } from './index';
|
|
11
11
|
|
|
12
12
|
describe('render() input validation (Issue #1)', () => {
|
|
13
13
|
const validWireframe = `
|
|
@@ -250,3 +250,112 @@ describe('onSceneChange callback (Issue #2)', () => {
|
|
|
250
250
|
}
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
|
+
|
|
254
|
+
describe('device option override (Issue #11)', () => {
|
|
255
|
+
const desktopWireframe = `
|
|
256
|
+
@scene: test
|
|
257
|
+
@device: desktop
|
|
258
|
+
|
|
259
|
+
+---------------+
|
|
260
|
+
| Desktop Scene |
|
|
261
|
+
+---------------+
|
|
262
|
+
`;
|
|
263
|
+
|
|
264
|
+
test('DeviceType type is exported', () => {
|
|
265
|
+
// Verify the type is exported by using it
|
|
266
|
+
const device: DeviceType = 'mobile';
|
|
267
|
+
expect(typeof device).toBe('string');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('RenderOptions includes device field in type definition', () => {
|
|
271
|
+
// Verify the device option is part of RenderOptions type
|
|
272
|
+
const options: RenderOptions = {
|
|
273
|
+
device: 'mobile',
|
|
274
|
+
};
|
|
275
|
+
expect(options.device).toBe('mobile');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('RenderOptions accepts all valid device types', () => {
|
|
279
|
+
const deviceTypes: DeviceType[] = [
|
|
280
|
+
'desktop',
|
|
281
|
+
'laptop',
|
|
282
|
+
'tablet',
|
|
283
|
+
'tablet-landscape',
|
|
284
|
+
'mobile',
|
|
285
|
+
'mobile-landscape',
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
deviceTypes.forEach((deviceType) => {
|
|
289
|
+
const options: RenderOptions = { device: deviceType };
|
|
290
|
+
expect(options.device).toBe(deviceType);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Note: DOM-dependent tests are skipped in Node.js environment
|
|
295
|
+
test.skip('render accepts device option without throwing (requires DOM)', () => {
|
|
296
|
+
const result = parse(desktopWireframe);
|
|
297
|
+
expect(result.success).toBe(true);
|
|
298
|
+
|
|
299
|
+
if (result.success) {
|
|
300
|
+
// This should not throw - just verify the API accepts the option
|
|
301
|
+
expect(() => {
|
|
302
|
+
render(result.ast, { device: 'mobile' });
|
|
303
|
+
}).not.toThrow();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test.skip('createUI accepts device option without throwing (requires DOM)', () => {
|
|
308
|
+
// Verify the API accepts the option
|
|
309
|
+
expect(() => {
|
|
310
|
+
createUI(desktopWireframe, { device: 'tablet' });
|
|
311
|
+
}).not.toThrow();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test.skip('device option overrides scene-defined device class (requires DOM)', () => {
|
|
315
|
+
const result = parse(desktopWireframe);
|
|
316
|
+
expect(result.success).toBe(true);
|
|
317
|
+
|
|
318
|
+
if (result.success) {
|
|
319
|
+
// Render with mobile override
|
|
320
|
+
const { root } = render(result.ast, { device: 'mobile' });
|
|
321
|
+
|
|
322
|
+
// Should have mobile class, not desktop
|
|
323
|
+
expect(root.classList.contains('wf-device-mobile')).toBe(true);
|
|
324
|
+
expect(root.classList.contains('wf-device-desktop')).toBe(false);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test.skip('device option applies correct class for all device types (requires DOM)', () => {
|
|
329
|
+
const result = parse(desktopWireframe);
|
|
330
|
+
expect(result.success).toBe(true);
|
|
331
|
+
|
|
332
|
+
if (result.success) {
|
|
333
|
+
const deviceClassMap: Record<DeviceType, string> = {
|
|
334
|
+
desktop: 'wf-device-desktop',
|
|
335
|
+
laptop: 'wf-device-laptop',
|
|
336
|
+
tablet: 'wf-device-tablet',
|
|
337
|
+
'tablet-landscape': 'wf-device-tablet-landscape',
|
|
338
|
+
mobile: 'wf-device-mobile',
|
|
339
|
+
'mobile-landscape': 'wf-device-mobile-landscape',
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
Object.entries(deviceClassMap).forEach(([deviceType, expectedClass]) => {
|
|
343
|
+
const { root } = render(result.ast, { device: deviceType as DeviceType });
|
|
344
|
+
expect(root.classList.contains(expectedClass)).toBe(true);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test.skip('renders with scene device when no override provided (requires DOM)', () => {
|
|
350
|
+
const result = parse(desktopWireframe);
|
|
351
|
+
expect(result.success).toBe(true);
|
|
352
|
+
|
|
353
|
+
if (result.success) {
|
|
354
|
+
// Render without device override
|
|
355
|
+
const { root } = render(result.ast);
|
|
356
|
+
|
|
357
|
+
// Should use the scene-defined desktop device
|
|
358
|
+
expect(root.classList.contains('wf-device-desktop')).toBe(true);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -168,6 +168,12 @@ export interface RenderOptions {
|
|
|
168
168
|
* @param toScene - The scene ID navigating to
|
|
169
169
|
*/
|
|
170
170
|
onSceneChange?: OnSceneChangeCallback;
|
|
171
|
+
/**
|
|
172
|
+
* Override the device type for all scenes.
|
|
173
|
+
* When provided, this overrides the device type defined in scene definitions.
|
|
174
|
+
* Useful for previewing wireframes in different device contexts without modifying the source.
|
|
175
|
+
*/
|
|
176
|
+
device?: DeviceType;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
/** Render result */
|
|
@@ -7,6 +7,8 @@ import * as RendererJS from './Renderer.mjs';
|
|
|
7
7
|
|
|
8
8
|
import type {ast as Types_ast} from '../../src/parser/Core/Types.gen';
|
|
9
9
|
|
|
10
|
+
import type {deviceType as Types_deviceType} from '../../src/parser/Core/Types.gen';
|
|
11
|
+
|
|
10
12
|
import type {t as ErrorTypes_t} from '../../src/parser/Errors/ErrorTypes.gen';
|
|
11
13
|
|
|
12
14
|
export abstract class DomBindings_element { protected opaque!: any }; /* simulate opaque types */
|
|
@@ -17,13 +19,31 @@ export abstract class DomBindings_element { protected opaque!: any }; /* simulat
|
|
|
17
19
|
* @param toScene The scene ID navigating to */
|
|
18
20
|
export type onSceneChangeCallback = (_1:(undefined | string), _2:string) => void;
|
|
19
21
|
|
|
22
|
+
/** * Dead end click info type.
|
|
23
|
+
* Contains information about the clicked element that has no navigation target. */
|
|
24
|
+
export type deadEndClickInfo = {
|
|
25
|
+
readonly sceneId: string;
|
|
26
|
+
readonly elementId: string;
|
|
27
|
+
readonly elementText: string;
|
|
28
|
+
readonly elementType:
|
|
29
|
+
"link"
|
|
30
|
+
| "button"
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** * Dead end click callback type.
|
|
34
|
+
* Called when a button or link without navigation target is clicked.
|
|
35
|
+
* @param info Information about the clicked element and current scene */
|
|
36
|
+
export type onDeadEndClickCallback = (_1:deadEndClickInfo) => void;
|
|
37
|
+
|
|
20
38
|
/** * Configuration for the rendering process. */
|
|
21
39
|
export type renderOptions = {
|
|
22
40
|
readonly theme: (undefined | string);
|
|
23
41
|
readonly interactive: boolean;
|
|
24
42
|
readonly injectStyles: boolean;
|
|
25
43
|
readonly containerClass: (undefined | string);
|
|
26
|
-
readonly onSceneChange: (undefined | onSceneChangeCallback)
|
|
44
|
+
readonly onSceneChange: (undefined | onSceneChangeCallback);
|
|
45
|
+
readonly onDeadEndClick: (undefined | onDeadEndClickCallback);
|
|
46
|
+
readonly device: (undefined | Types_deviceType)
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
/** * Scene management interface returned by render function. */
|
|
@@ -13,7 +13,9 @@ let defaultOptions = {
|
|
|
13
13
|
interactive: true,
|
|
14
14
|
injectStyles: true,
|
|
15
15
|
containerClass: undefined,
|
|
16
|
-
onSceneChange: undefined
|
|
16
|
+
onSceneChange: undefined,
|
|
17
|
+
onDeadEndClick: undefined,
|
|
18
|
+
device: undefined
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
let defaultStyles = `
|
|
@@ -133,7 +135,24 @@ function deviceTypeToClass(device) {
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
function
|
|
138
|
+
function isNavigationAction(action) {
|
|
139
|
+
if (typeof action !== "object") {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
switch (action.TAG) {
|
|
143
|
+
case "Validate" :
|
|
144
|
+
case "Call" :
|
|
145
|
+
return false;
|
|
146
|
+
default:
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function hasNavigationAction(actions) {
|
|
152
|
+
return actions.some(isNavigationAction);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function renderElement(_elem, onAction, onDeadEnd) {
|
|
137
156
|
while (true) {
|
|
138
157
|
let elem = _elem;
|
|
139
158
|
if (isInputOnlyBox(elem)) {
|
|
@@ -155,7 +174,7 @@ function renderElement(_elem, onAction) {
|
|
|
155
174
|
div.dataset["name"] = name;
|
|
156
175
|
}
|
|
157
176
|
elem.children.forEach(child => {
|
|
158
|
-
let el = renderElement(child, onAction);
|
|
177
|
+
let el = renderElement(child, onAction, onDeadEnd);
|
|
159
178
|
if (el !== undefined) {
|
|
160
179
|
div.appendChild(Primitive_option.valFromOption(el));
|
|
161
180
|
return;
|
|
@@ -164,18 +183,25 @@ function renderElement(_elem, onAction) {
|
|
|
164
183
|
return Primitive_option.some(div);
|
|
165
184
|
case "Button" :
|
|
166
185
|
let actions = elem.actions;
|
|
186
|
+
let text = elem.text;
|
|
187
|
+
let id = elem.id;
|
|
167
188
|
let btn = document.createElement("button");
|
|
168
189
|
btn.className = "wf-button";
|
|
169
|
-
btn.id =
|
|
170
|
-
btn.textContent =
|
|
190
|
+
btn.id = id;
|
|
191
|
+
btn.textContent = text;
|
|
171
192
|
applyAlignment(btn, elem.align);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
193
|
+
let hasNavigation = actions.some(isNavigationAction);
|
|
194
|
+
if (hasNavigation) {
|
|
195
|
+
if (onAction !== undefined) {
|
|
196
|
+
btn.addEventListener("click", _event => {
|
|
197
|
+
let action = actions[0];
|
|
198
|
+
if (action !== undefined) {
|
|
199
|
+
return onAction(action);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
} else if (onDeadEnd !== undefined) {
|
|
204
|
+
btn.addEventListener("click", _event => onDeadEnd(id, text, "button"));
|
|
179
205
|
}
|
|
180
206
|
return Primitive_option.some(btn);
|
|
181
207
|
case "Input" :
|
|
@@ -189,19 +215,29 @@ function renderElement(_elem, onAction) {
|
|
|
189
215
|
return Primitive_option.some(input$1);
|
|
190
216
|
case "Link" :
|
|
191
217
|
let actions$1 = elem.actions;
|
|
218
|
+
let text$1 = elem.text;
|
|
219
|
+
let id$1 = elem.id;
|
|
192
220
|
let link = document.createElement("a");
|
|
193
221
|
link.className = "wf-link";
|
|
194
|
-
link.id =
|
|
222
|
+
link.id = id$1;
|
|
195
223
|
link.href = "#";
|
|
196
|
-
link.textContent =
|
|
224
|
+
link.textContent = text$1;
|
|
197
225
|
applyAlignment(link, elem.align);
|
|
198
|
-
|
|
226
|
+
let hasNavigation$1 = actions$1.some(isNavigationAction);
|
|
227
|
+
if (hasNavigation$1) {
|
|
228
|
+
if (onAction !== undefined) {
|
|
229
|
+
link.addEventListener("click", event => {
|
|
230
|
+
event.preventDefault();
|
|
231
|
+
let action = actions$1[0];
|
|
232
|
+
if (action !== undefined) {
|
|
233
|
+
return onAction(action);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
} else if (onDeadEnd !== undefined) {
|
|
199
238
|
link.addEventListener("click", event => {
|
|
200
239
|
event.preventDefault();
|
|
201
|
-
|
|
202
|
-
if (action !== undefined) {
|
|
203
|
-
return onAction(action);
|
|
204
|
-
}
|
|
240
|
+
onDeadEnd(id$1, text$1, "link");
|
|
205
241
|
});
|
|
206
242
|
}
|
|
207
243
|
return Primitive_option.some(link);
|
|
@@ -238,7 +274,7 @@ function renderElement(_elem, onAction) {
|
|
|
238
274
|
row.className = "wf-row";
|
|
239
275
|
applyAlignment(row, elem.align);
|
|
240
276
|
elem.children.forEach(child => {
|
|
241
|
-
let el = renderElement(child, onAction);
|
|
277
|
+
let el = renderElement(child, onAction, onDeadEnd);
|
|
242
278
|
if (el !== undefined) {
|
|
243
279
|
row.appendChild(Primitive_option.valFromOption(el));
|
|
244
280
|
return;
|
|
@@ -254,7 +290,7 @@ function renderElement(_elem, onAction) {
|
|
|
254
290
|
let contentEl = document.createElement("div");
|
|
255
291
|
contentEl.className = "wf-section-content";
|
|
256
292
|
elem.children.forEach(child => {
|
|
257
|
-
let el = renderElement(child, onAction);
|
|
293
|
+
let el = renderElement(child, onAction, onDeadEnd);
|
|
258
294
|
if (el !== undefined) {
|
|
259
295
|
contentEl.appendChild(Primitive_option.valFromOption(el));
|
|
260
296
|
return;
|
|
@@ -267,12 +303,12 @@ function renderElement(_elem, onAction) {
|
|
|
267
303
|
};
|
|
268
304
|
}
|
|
269
305
|
|
|
270
|
-
function renderScene(scene, onAction) {
|
|
306
|
+
function renderScene(scene, onAction, onDeadEnd) {
|
|
271
307
|
let sceneEl = document.createElement("div");
|
|
272
308
|
sceneEl.className = "wf-scene";
|
|
273
309
|
sceneEl.dataset["scene"] = scene.id;
|
|
274
310
|
scene.elements.forEach(elem => {
|
|
275
|
-
let el = renderElement(elem, onAction);
|
|
311
|
+
let el = renderElement(elem, onAction, onDeadEnd);
|
|
276
312
|
if (el !== undefined) {
|
|
277
313
|
sceneEl.appendChild(Primitive_option.valFromOption(el));
|
|
278
314
|
return;
|
|
@@ -410,9 +446,16 @@ function render(ast, options) {
|
|
|
410
446
|
if (cls !== undefined) {
|
|
411
447
|
app.classList.add(cls);
|
|
412
448
|
}
|
|
413
|
-
let
|
|
414
|
-
|
|
415
|
-
|
|
449
|
+
let device = opts.device;
|
|
450
|
+
let deviceType;
|
|
451
|
+
if (device !== undefined) {
|
|
452
|
+
deviceType = device;
|
|
453
|
+
} else {
|
|
454
|
+
let firstScene = ast.scenes[0];
|
|
455
|
+
deviceType = firstScene !== undefined ? firstScene.device : undefined;
|
|
456
|
+
}
|
|
457
|
+
if (deviceType !== undefined) {
|
|
458
|
+
let deviceClass = deviceTypeToClass(deviceType);
|
|
416
459
|
app.classList.add(deviceClass);
|
|
417
460
|
}
|
|
418
461
|
let gotoRef = {
|
|
@@ -457,7 +500,14 @@ function render(ast, options) {
|
|
|
457
500
|
};
|
|
458
501
|
let sceneMap = new Map();
|
|
459
502
|
ast.scenes.forEach(scene => {
|
|
460
|
-
let
|
|
503
|
+
let callback = opts.onDeadEndClick;
|
|
504
|
+
let handleDeadEnd = callback !== undefined ? (elementId, elementText, elementType) => callback({
|
|
505
|
+
sceneId: scene.id,
|
|
506
|
+
elementId: elementId,
|
|
507
|
+
elementText: elementText,
|
|
508
|
+
elementType: elementType
|
|
509
|
+
}) : undefined;
|
|
510
|
+
let sceneEl = renderScene(scene, handleAction, handleDeadEnd);
|
|
461
511
|
app.appendChild(sceneEl);
|
|
462
512
|
sceneMap.set(scene.id, sceneEl);
|
|
463
513
|
});
|
|
@@ -530,6 +580,8 @@ export {
|
|
|
530
580
|
alignmentToClass,
|
|
531
581
|
applyAlignment,
|
|
532
582
|
deviceTypeToClass,
|
|
583
|
+
isNavigationAction,
|
|
584
|
+
hasNavigationAction,
|
|
533
585
|
renderElement,
|
|
534
586
|
renderScene,
|
|
535
587
|
createSceneManager,
|
|
@@ -56,6 +56,24 @@ module DomBindings = {
|
|
|
56
56
|
*/
|
|
57
57
|
type onSceneChangeCallback = (option<string>, string) => unit
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Dead end click info type.
|
|
61
|
+
* Contains information about the clicked element that has no navigation target.
|
|
62
|
+
*/
|
|
63
|
+
type deadEndClickInfo = {
|
|
64
|
+
sceneId: string,
|
|
65
|
+
elementId: string,
|
|
66
|
+
elementText: string,
|
|
67
|
+
elementType: [#button | #link],
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Dead end click callback type.
|
|
72
|
+
* Called when a button or link without navigation target is clicked.
|
|
73
|
+
* @param info Information about the clicked element and current scene
|
|
74
|
+
*/
|
|
75
|
+
type onDeadEndClickCallback = deadEndClickInfo => unit
|
|
76
|
+
|
|
59
77
|
/**
|
|
60
78
|
* Configuration for the rendering process.
|
|
61
79
|
*/
|
|
@@ -65,6 +83,8 @@ type renderOptions = {
|
|
|
65
83
|
injectStyles: bool,
|
|
66
84
|
containerClass: option<string>,
|
|
67
85
|
onSceneChange: option<onSceneChangeCallback>,
|
|
86
|
+
onDeadEndClick: option<onDeadEndClickCallback>,
|
|
87
|
+
device: option<deviceType>,
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
/**
|
|
@@ -76,6 +96,8 @@ let defaultOptions: renderOptions = {
|
|
|
76
96
|
injectStyles: true,
|
|
77
97
|
containerClass: None,
|
|
78
98
|
onSceneChange: None,
|
|
99
|
+
onDeadEndClick: None,
|
|
100
|
+
device: None,
|
|
79
101
|
}
|
|
80
102
|
|
|
81
103
|
// ============================================================================
|
|
@@ -228,17 +250,44 @@ let deviceTypeToClass = (device: deviceType): string => {
|
|
|
228
250
|
*/
|
|
229
251
|
type actionHandler = interactionAction => unit
|
|
230
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Dead end handler function type - called when an element without navigation is clicked.
|
|
255
|
+
* Receives element ID, text, and type.
|
|
256
|
+
*/
|
|
257
|
+
type deadEndHandler = (string, string, [#button | #link]) => unit
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check if an action is a navigation action (Goto, Back, Forward).
|
|
261
|
+
*/
|
|
262
|
+
let isNavigationAction = (action: interactionAction): bool => {
|
|
263
|
+
switch action {
|
|
264
|
+
| Goto(_) | Back | Forward => true
|
|
265
|
+
| Validate(_) | Call(_) => false
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Check if actions array has any navigation actions.
|
|
271
|
+
*/
|
|
272
|
+
let hasNavigationAction = (actions: array<interactionAction>): bool => {
|
|
273
|
+
actions->Array.some(isNavigationAction)
|
|
274
|
+
}
|
|
275
|
+
|
|
231
276
|
// ============================================================================
|
|
232
277
|
// Element Rendering
|
|
233
278
|
// ============================================================================
|
|
234
279
|
|
|
235
|
-
let rec renderElement = (
|
|
280
|
+
let rec renderElement = (
|
|
281
|
+
elem: element,
|
|
282
|
+
~onAction: option<actionHandler>=?,
|
|
283
|
+
~onDeadEnd: option<deadEndHandler>=?,
|
|
284
|
+
): option<DomBindings.element> => {
|
|
236
285
|
// Handle input-only boxes by rendering children directly in a wrapper
|
|
237
286
|
if isInputOnlyBox(elem) {
|
|
238
287
|
let inputs = getInputsFromBox(elem)
|
|
239
288
|
// If only one input, render it directly
|
|
240
289
|
switch inputs->Array.get(0) {
|
|
241
|
-
| Some(input) => renderElement(input, ~onAction?)
|
|
290
|
+
| Some(input) => renderElement(input, ~onAction?, ~onDeadEnd?)
|
|
242
291
|
| None => None
|
|
243
292
|
}
|
|
244
293
|
} else {
|
|
@@ -256,7 +305,7 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
256
305
|
}
|
|
257
306
|
|
|
258
307
|
children->Array.forEach(child => {
|
|
259
|
-
switch renderElement(child, ~onAction?) {
|
|
308
|
+
switch renderElement(child, ~onAction?, ~onDeadEnd?) {
|
|
260
309
|
| Some(el) => div->DomBindings.appendChild(el)
|
|
261
310
|
| None => ()
|
|
262
311
|
}
|
|
@@ -272,8 +321,11 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
272
321
|
btn->DomBindings.setTextContent(text)
|
|
273
322
|
applyAlignment(btn, align)
|
|
274
323
|
|
|
275
|
-
//
|
|
276
|
-
|
|
324
|
+
// Check if button has navigation actions
|
|
325
|
+
let hasNavigation = hasNavigationAction(actions)
|
|
326
|
+
|
|
327
|
+
if hasNavigation {
|
|
328
|
+
// Attach click handler for navigation actions
|
|
277
329
|
switch onAction {
|
|
278
330
|
| Some(handler) => {
|
|
279
331
|
btn->DomBindings.addEventListener("click", _event => {
|
|
@@ -286,6 +338,16 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
286
338
|
}
|
|
287
339
|
| None => ()
|
|
288
340
|
}
|
|
341
|
+
} else {
|
|
342
|
+
// No navigation - call dead end handler
|
|
343
|
+
switch onDeadEnd {
|
|
344
|
+
| Some(handler) => {
|
|
345
|
+
btn->DomBindings.addEventListener("click", _event => {
|
|
346
|
+
handler(id, text, #button)
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
| None => ()
|
|
350
|
+
}
|
|
289
351
|
}
|
|
290
352
|
|
|
291
353
|
Some(btn)
|
|
@@ -310,8 +372,11 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
310
372
|
link->DomBindings.setTextContent(text)
|
|
311
373
|
applyAlignment(link, align)
|
|
312
374
|
|
|
313
|
-
//
|
|
314
|
-
|
|
375
|
+
// Check if link has navigation actions
|
|
376
|
+
let hasNavigation = hasNavigationAction(actions)
|
|
377
|
+
|
|
378
|
+
if hasNavigation {
|
|
379
|
+
// Attach click handler for navigation actions
|
|
315
380
|
switch onAction {
|
|
316
381
|
| Some(handler) => {
|
|
317
382
|
link->DomBindings.addEventListener("click", event => {
|
|
@@ -325,6 +390,17 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
325
390
|
}
|
|
326
391
|
| None => ()
|
|
327
392
|
}
|
|
393
|
+
} else {
|
|
394
|
+
// No navigation - call dead end handler
|
|
395
|
+
switch onDeadEnd {
|
|
396
|
+
| Some(handler) => {
|
|
397
|
+
link->DomBindings.addEventListener("click", event => {
|
|
398
|
+
DomBindings.preventDefault(event)
|
|
399
|
+
handler(id, text, #link)
|
|
400
|
+
})
|
|
401
|
+
}
|
|
402
|
+
| None => ()
|
|
403
|
+
}
|
|
328
404
|
}
|
|
329
405
|
|
|
330
406
|
Some(link)
|
|
@@ -374,7 +450,7 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
374
450
|
applyAlignment(row, align)
|
|
375
451
|
|
|
376
452
|
children->Array.forEach(child => {
|
|
377
|
-
switch renderElement(child, ~onAction?) {
|
|
453
|
+
switch renderElement(child, ~onAction?, ~onDeadEnd?) {
|
|
378
454
|
| Some(el) => row->DomBindings.appendChild(el)
|
|
379
455
|
| None => ()
|
|
380
456
|
}
|
|
@@ -395,7 +471,7 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
395
471
|
contentEl->DomBindings.setClassName("wf-section-content")
|
|
396
472
|
|
|
397
473
|
children->Array.forEach(child => {
|
|
398
|
-
switch renderElement(child, ~onAction?) {
|
|
474
|
+
switch renderElement(child, ~onAction?, ~onDeadEnd?) {
|
|
399
475
|
| Some(el) => contentEl->DomBindings.appendChild(el)
|
|
400
476
|
| None => ()
|
|
401
477
|
}
|
|
@@ -409,13 +485,17 @@ let rec renderElement = (elem: element, ~onAction: option<actionHandler>=?): opt
|
|
|
409
485
|
}
|
|
410
486
|
}
|
|
411
487
|
|
|
412
|
-
let renderScene = (
|
|
488
|
+
let renderScene = (
|
|
489
|
+
scene: scene,
|
|
490
|
+
~onAction: option<actionHandler>=?,
|
|
491
|
+
~onDeadEnd: option<deadEndHandler>=?,
|
|
492
|
+
): DomBindings.element => {
|
|
413
493
|
let sceneEl = DomBindings.document->DomBindings.createElement("div")
|
|
414
494
|
sceneEl->DomBindings.setClassName("wf-scene")
|
|
415
495
|
sceneEl->DomBindings.dataset->DomBindings.setDataAttr("scene", scene.id)
|
|
416
496
|
|
|
417
497
|
scene.elements->Array.forEach(elem => {
|
|
418
|
-
switch renderElement(elem, ~onAction?) {
|
|
498
|
+
switch renderElement(elem, ~onAction?, ~onDeadEnd?) {
|
|
419
499
|
| Some(el) => sceneEl->DomBindings.appendChild(el)
|
|
420
500
|
| None => ()
|
|
421
501
|
}
|
|
@@ -610,10 +690,19 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
610
690
|
| None => ()
|
|
611
691
|
}
|
|
612
692
|
|
|
613
|
-
// Apply device class based on first scene's device type
|
|
614
|
-
switch
|
|
615
|
-
| Some(
|
|
616
|
-
|
|
693
|
+
// Apply device class based on options.device override or first scene's device type
|
|
694
|
+
let deviceType = switch opts.device {
|
|
695
|
+
| Some(device) => Some(device)
|
|
696
|
+
| None =>
|
|
697
|
+
switch ast.scenes->Array.get(0) {
|
|
698
|
+
| Some(firstScene) => Some(firstScene.device)
|
|
699
|
+
| None => None
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
switch deviceType {
|
|
704
|
+
| Some(device) => {
|
|
705
|
+
let deviceClass = deviceTypeToClass(device)
|
|
617
706
|
app->DomBindings.classList->DomBindings.add(deviceClass)
|
|
618
707
|
}
|
|
619
708
|
| None => ()
|
|
@@ -659,7 +748,21 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
659
748
|
let sceneMap = Map.make()
|
|
660
749
|
|
|
661
750
|
ast.scenes->Array.forEach(scene => {
|
|
662
|
-
|
|
751
|
+
// Create a scene-specific dead end handler that includes the scene ID
|
|
752
|
+
let handleDeadEnd = switch opts.onDeadEndClick {
|
|
753
|
+
| Some(callback) =>
|
|
754
|
+
Some((elementId: string, elementText: string, elementType: [#button | #link]) => {
|
|
755
|
+
callback({
|
|
756
|
+
sceneId: scene.id,
|
|
757
|
+
elementId,
|
|
758
|
+
elementText,
|
|
759
|
+
elementType,
|
|
760
|
+
})
|
|
761
|
+
})
|
|
762
|
+
| None => None
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
let sceneEl = renderScene(scene, ~onAction=handleAction, ~onDeadEnd=?handleDeadEnd)
|
|
663
766
|
app->DomBindings.appendChild(sceneEl)
|
|
664
767
|
sceneMap->Map.set(scene.id, sceneEl)
|
|
665
768
|
})
|