@cratis/scene.react 3.7.0 → 4.0.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/core/CoreButton.tsx +2 -2
- package/dist/cjs/SceneElementView.js +1 -0
- package/dist/cjs/SceneElementView.js.map +1 -1
- package/dist/cjs/core/CoreButton.js +2 -1
- package/dist/cjs/core/CoreButton.js.map +1 -1
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interactions/InteractionScope.js +65 -0
- package/dist/cjs/interactions/InteractionScope.js.map +1 -0
- package/dist/cjs/interactions/createBrowserDispatcher.js +102 -0
- package/dist/cjs/interactions/createBrowserDispatcher.js.map +1 -0
- package/dist/cjs/interactions/useInteractions.js +122 -0
- package/dist/cjs/interactions/useInteractions.js.map +1 -0
- package/dist/cjs/renderer/InteractiveComponent.js +25 -0
- package/dist/cjs/renderer/InteractiveComponent.js.map +1 -0
- package/dist/cjs/renderer/createReactRenderer.js +3 -1
- package/dist/cjs/renderer/createReactRenderer.js.map +1 -1
- package/dist/esm/SceneElementView.js +1 -0
- package/dist/esm/SceneElementView.js.map +1 -1
- package/dist/esm/core/CoreButton.d.ts +1 -1
- package/dist/esm/core/CoreButton.d.ts.map +1 -1
- package/dist/esm/core/CoreButton.js +2 -1
- package/dist/esm/core/CoreButton.js.map +1 -1
- package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.d.ts +2 -0
- package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.d.ts.map +1 -0
- package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.js +94 -0
- package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interactions/InteractionScope.d.ts +16 -0
- package/dist/esm/interactions/InteractionScope.d.ts.map +1 -0
- package/dist/esm/interactions/InteractionScope.js +61 -0
- package/dist/esm/interactions/InteractionScope.js.map +1 -0
- package/dist/esm/interactions/createBrowserDispatcher.d.ts +17 -0
- package/dist/esm/interactions/createBrowserDispatcher.d.ts.map +1 -0
- package/dist/esm/interactions/createBrowserDispatcher.js +100 -0
- package/dist/esm/interactions/createBrowserDispatcher.js.map +1 -0
- package/dist/esm/interactions/index.d.ts +4 -0
- package/dist/esm/interactions/index.d.ts.map +1 -0
- package/dist/esm/interactions/index.js +4 -0
- package/dist/esm/interactions/index.js.map +1 -0
- package/dist/esm/interactions/useInteractions.d.ts +16 -0
- package/dist/esm/interactions/useInteractions.d.ts.map +1 -0
- package/dist/esm/interactions/useInteractions.js +120 -0
- package/dist/esm/interactions/useInteractions.js.map +1 -0
- package/dist/esm/renderer/ComponentRegistry.d.ts +2 -0
- package/dist/esm/renderer/ComponentRegistry.d.ts.map +1 -1
- package/dist/esm/renderer/InteractiveComponent.d.ts +10 -0
- package/dist/esm/renderer/InteractiveComponent.d.ts.map +1 -0
- package/dist/esm/renderer/InteractiveComponent.js +23 -0
- package/dist/esm/renderer/InteractiveComponent.js.map +1 -0
- package/dist/esm/renderer/createReactRenderer.d.ts.map +1 -1
- package/dist/esm/renderer/createReactRenderer.js +3 -1
- package/dist/esm/renderer/createReactRenderer.js.map +1 -1
- package/dist/esm/renderer/index.d.ts +1 -0
- package/dist/esm/renderer/index.d.ts.map +1 -1
- package/dist/esm/renderer/index.js +1 -0
- package/dist/esm/renderer/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/for_useInteractions/when_clicking_an_element_that_executes_a_command.tsx +141 -0
- package/index.ts +1 -0
- package/interactions/InteractionScope.tsx +74 -0
- package/interactions/createBrowserDispatcher.ts +160 -0
- package/interactions/index.ts +6 -0
- package/interactions/useInteractions.ts +116 -0
- package/package.json +5 -5
- package/renderer/ComponentRegistry.ts +11 -0
- package/renderer/InteractiveComponent.tsx +25 -0
- package/renderer/createReactRenderer.tsx +2 -1
- package/renderer/index.ts +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { InteractionActionKind, InteractionTriggerKind, NotificationLevel } from '@cratis/scene.model';
|
|
5
|
+
import { InteractionScope, useInteractions } from '../interactions';
|
|
6
|
+
const calls = [];
|
|
7
|
+
const dispatcher = {
|
|
8
|
+
async executeCommand(command, args) {
|
|
9
|
+
calls.push(`executeCommand:${command}:${JSON.stringify(args)}`);
|
|
10
|
+
return { isSuccess: true };
|
|
11
|
+
},
|
|
12
|
+
navigate: (screenName) => { calls.push(`navigate:${screenName}`); },
|
|
13
|
+
navigateBack: () => { calls.push('navigateBack'); },
|
|
14
|
+
openDialog: async () => { calls.push('openDialog'); return { isConfirmed: true }; },
|
|
15
|
+
closeDialog: () => { calls.push('closeDialog'); },
|
|
16
|
+
refreshQuery: async (query) => { calls.push(`refreshQuery:${query}`); },
|
|
17
|
+
setState: (target) => { calls.push(`setState:${target}`); },
|
|
18
|
+
notify: (level, message) => { calls.push(`notify:${level}:${message}`); },
|
|
19
|
+
confirm: async () => { calls.push('confirm'); return true; },
|
|
20
|
+
raise: async (trigger) => { calls.push(`raise:${trigger}`); },
|
|
21
|
+
};
|
|
22
|
+
const context = { resolve: binding => `<${binding.path}>` };
|
|
23
|
+
const cancelOnClick = {
|
|
24
|
+
name: 'CancelInvoice',
|
|
25
|
+
bindings: [
|
|
26
|
+
{
|
|
27
|
+
trigger: { kind: InteractionTriggerKind.Click },
|
|
28
|
+
actions: [
|
|
29
|
+
{
|
|
30
|
+
kind: InteractionActionKind.ExecuteCommand,
|
|
31
|
+
command: 'CancelInvoice',
|
|
32
|
+
arguments: [{ name: 'invoiceId', value: { path: 'item.invoiceId' } }],
|
|
33
|
+
onSuccess: [{ kind: InteractionActionKind.RefreshQuery, query: 'Invoices' }],
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
function Button({ behaviors }) {
|
|
40
|
+
const handlers = useInteractions('element', behaviors);
|
|
41
|
+
return _jsx("button", { type: "button", ...handlers, children: "Cancel" });
|
|
42
|
+
}
|
|
43
|
+
function renderInScope(behaviors) {
|
|
44
|
+
return render(_jsx(InteractionScope, { dispatcher: dispatcher, context: context, attachments: [], children: _jsx(Button, { behaviors: behaviors }) }));
|
|
45
|
+
}
|
|
46
|
+
describe('when clicking an element that executes a command', () => {
|
|
47
|
+
beforeEach(() => { calls.length = 0; });
|
|
48
|
+
it('should execute the command the document declared', async () => {
|
|
49
|
+
renderInScope([cancelOnClick]);
|
|
50
|
+
await userEvent.click(screen.getByRole('button'));
|
|
51
|
+
calls.should.eql(['executeCommand:CancelInvoice:{"invoiceId":"<item.invoiceId>"}', 'refreshQuery:Invoices']);
|
|
52
|
+
});
|
|
53
|
+
it('should not make an element interactive when nothing is attached', async () => {
|
|
54
|
+
renderInScope();
|
|
55
|
+
await userEvent.click(screen.getByRole('button'));
|
|
56
|
+
calls.should.have.lengthOf(0);
|
|
57
|
+
});
|
|
58
|
+
it('should attach no handler for a trigger the document does not use', async () => {
|
|
59
|
+
renderInScope([cancelOnClick]);
|
|
60
|
+
await userEvent.dblClick(screen.getByRole('button'));
|
|
61
|
+
calls.filter(call => call.startsWith('executeCommand')).should.have.lengthOf(2);
|
|
62
|
+
});
|
|
63
|
+
it('should run a behavior attached further out as well as its own', async () => {
|
|
64
|
+
render(_jsx(InteractionScope, { dispatcher: dispatcher, context: context, attachments: [{
|
|
65
|
+
level: 'module',
|
|
66
|
+
behaviors: [{
|
|
67
|
+
name: 'ModuleWide',
|
|
68
|
+
bindings: [{
|
|
69
|
+
trigger: { kind: InteractionTriggerKind.Click },
|
|
70
|
+
actions: [{ kind: InteractionActionKind.Notify, level: NotificationLevel.Info, message: { text: 'from the module' } }],
|
|
71
|
+
}],
|
|
72
|
+
}],
|
|
73
|
+
}], children: _jsx(Button, { behaviors: [cancelOnClick] }) }));
|
|
74
|
+
await userEvent.click(screen.getByRole('button'));
|
|
75
|
+
calls.should.eql([
|
|
76
|
+
'notify:Info:from the module',
|
|
77
|
+
'executeCommand:CancelInvoice:{"invoiceId":"<item.invoiceId>"}',
|
|
78
|
+
'refreshQuery:Invoices',
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
it('should not run a binding whose guard is false', async () => {
|
|
82
|
+
render(_jsx(InteractionScope, { dispatcher: dispatcher, context: { resolve: () => false }, attachments: [], children: _jsx(Button, { behaviors: [{
|
|
83
|
+
name: 'Guarded',
|
|
84
|
+
bindings: [{
|
|
85
|
+
trigger: { kind: InteractionTriggerKind.Click },
|
|
86
|
+
condition: { path: 'item.canCancel' },
|
|
87
|
+
actions: [{ kind: InteractionActionKind.ExecuteCommand, command: 'CancelInvoice' }],
|
|
88
|
+
}],
|
|
89
|
+
}] }) }));
|
|
90
|
+
await userEvent.click(screen.getByRole('button'));
|
|
91
|
+
calls.should.have.lengthOf(0);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=when_clicking_an_element_that_executes_a_command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when_clicking_an_element_that_executes_a_command.js","sourceRoot":"","sources":["../../../for_useInteractions/when_clicking_an_element_that_executes_a_command.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,SAAS,MAAM,6BAA6B,CAAC;AAEpD,OAAO,EAAY,qBAAqB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACjH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,KAAK,GAAa,EAAE,CAAC;AAE3B,MAAM,UAAU,GAAG;IACf,KAAK,CAAC,cAAc,CAAC,OAAe,EAAE,IAA6B;QAC/D,KAAK,CAAC,IAAI,CAAC,kBAAkB,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IACD,QAAQ,EAAE,CAAC,UAAkB,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,YAAY,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnF,WAAW,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACjD,YAAY,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,QAAQ,EAAE,CAAC,MAAc,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,EAAE,CAAC,KAAwB,EAAE,OAAe,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACpG,OAAO,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAC5D,KAAK,EAAE,KAAK,EAAE,OAAe,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;CAC7C,CAAC;AAE7B,MAAM,OAAO,GAAuB,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,GAAG,EAAE,CAAC;AAEhF,MAAM,aAAa,GAAa;IAC5B,IAAI,EAAE,eAAe;IACrB,QAAQ,EAAE;QACN;YACI,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAW;YACxD,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,qBAAqB,CAAC,cAAc;oBAC1C,OAAO,EAAE,eAAe;oBACxB,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC;oBACrE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;iBACtE;aACb;SACJ;KACJ;CACJ,CAAC;AAEF,SAAS,MAAM,CAAC,EAAE,SAAS,EAA8B;IACrD,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACvD,OAAO,iBAAQ,IAAI,EAAC,QAAQ,KAAK,QAAQ,uBAAiB,CAAC;AAC/D,CAAC;AAED,SAAS,aAAa,CAAC,SAAsB;IACzC,OAAO,MAAM,CACT,KAAC,gBAAgB,IAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,YACvE,KAAC,MAAM,IAAC,SAAS,EAAE,SAAS,GAAI,GACjB,CACtB,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC9D,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAGxC,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAC9D,aAAa,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QAE/B,MAAM,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAElD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,+DAA+D,EAAE,uBAAuB,CAAC,CAAC,CAAC;IACjH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC7E,aAAa,EAAE,CAAC;QAEhB,MAAM,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAIH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAC9E,aAAa,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QAE/B,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAIrD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,CACF,KAAC,gBAAgB,IACb,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC;oBACV,KAAK,EAAE,QAAQ;oBACf,SAAS,EAAE,CAAC;4BACR,IAAI,EAAE,YAAY;4BAClB,QAAQ,EAAE,CAAC;oCACP,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAW;oCACxD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAW,CAAC;iCAClI,CAAC;yBACL,CAAC;iBACL,CAAC,YACF,KAAC,MAAM,IAAC,SAAS,EAAE,CAAC,aAAa,CAAC,GAAI,GACvB,CACtB,CAAC;QAEF,MAAM,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAElD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;YACb,6BAA6B;YAC7B,+DAA+D;YAC/D,uBAAuB;SAC1B,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,CACF,KAAC,gBAAgB,IACb,UAAU,EAAE,UAAU,EAGtB,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,EACjC,WAAW,EAAE,EAAE,YACf,KAAC,MAAM,IAAC,SAAS,EAAE,CAAC;wBACxB,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,CAAC;gCACP,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAW;gCACxD,SAAS,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;gCACrC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,cAAc,EAAE,OAAO,EAAE,eAAe,EAAW,CAAC;6BAC/F,CAAC;qBACG,CAAC,GAAI,GACS,CACtB,CAAC;QAEF,MAAM,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { SceneElementView } from './SceneElementView.js';
|
|
2
2
|
export { UnresolvedComponent } from './renderer/UnresolvedComponent.js';
|
|
3
3
|
export { createReactRenderer } from './renderer/createReactRenderer.js';
|
|
4
|
+
export { InteractiveComponent } from './renderer/InteractiveComponent.js';
|
|
4
5
|
export { CoreText } from './core/CoreText.js';
|
|
5
6
|
export { CoreButton } from './core/CoreButton.js';
|
|
6
7
|
export { CoreCard } from './core/CoreCard.js';
|
|
@@ -12,4 +13,7 @@ export { extractNavigationItem } from './NavBar/extractNavigationItem.js';
|
|
|
12
13
|
export { componentRegistryKey, mergePackageRegistries, validatePackageBundle } from './packages/ScenePackageBundle.js';
|
|
13
14
|
export { applyThemeTokens, themeTokenPrefix, themeTokenProperty } from './theme/themeTokens.js';
|
|
14
15
|
export { SceneThemeProvider, useSceneTheme } from './theme/SceneThemeProvider.js';
|
|
16
|
+
export { InteractionScope, attachmentFor, useInteractionScope } from './interactions/InteractionScope.js';
|
|
17
|
+
export { useInteractions } from './interactions/useInteractions.js';
|
|
18
|
+
export { createBrowserDispatcher } from './interactions/createBrowserDispatcher.js';
|
|
15
19
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ActionDispatcher, BehaviorAttachment, InteractionContext, InteractionFinding } from '@cratis/scene.engine';
|
|
3
|
+
import { Behavior } from '@cratis/scene.model';
|
|
4
|
+
export interface InteractionScopeValue {
|
|
5
|
+
dispatcher: ActionDispatcher;
|
|
6
|
+
context: InteractionContext;
|
|
7
|
+
attachments: BehaviorAttachment[];
|
|
8
|
+
onFindings?: (findings: InteractionFinding[]) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface InteractionScopeProps extends InteractionScopeValue {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare function InteractionScope({ children, dispatcher, context, attachments, onFindings }: InteractionScopeProps): import("react").JSX.Element;
|
|
14
|
+
export declare function useInteractionScope(): InteractionScopeValue | undefined;
|
|
15
|
+
export declare function attachmentFor(level: string, behaviors?: Behavior[]): BehaviorAttachment[];
|
|
16
|
+
//# sourceMappingURL=InteractionScope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractionScope.d.ts","sourceRoot":"","sources":["../../../interactions/InteractionScope.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAsC,MAAM,OAAO,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACpH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAK/C,MAAM,WAAW,qBAAqB;IAElC,UAAU,EAAE,gBAAgB,CAAC;IAG7B,OAAO,EAAE,kBAAkB,CAAC;IAG5B,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAGlC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAC;CACzD;AAID,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAChE,QAAQ,EAAE,SAAS,CAAC;CACvB;AAUD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,qBAAqB,+BAWjH;AAWD,wBAAgB,mBAAmB,IAAI,qBAAqB,GAAG,SAAS,CAEvE;AASD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAEzF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useContext, useMemo, createContext } from 'react';
|
|
3
|
+
|
|
4
|
+
const Scope = /*#__PURE__*/ createContext(undefined);
|
|
5
|
+
/**
|
|
6
|
+
* Provides the dispatcher, the resolution context and the enclosing attachments to everything beneath it.
|
|
7
|
+
*
|
|
8
|
+
* Attachments nest: a scope inside another scope appends to it rather than replacing it, so an element's
|
|
9
|
+
* enclosing chain is assembled by the tree rather than reconstructed at each leaf. That is what keeps the
|
|
10
|
+
* additive rule true in a renderer - a module's behavior is still in scope at a button six levels down,
|
|
11
|
+
* without the button knowing the module exists.
|
|
12
|
+
*/ function InteractionScope({ children, dispatcher, context, attachments, onFindings }) {
|
|
13
|
+
const enclosing = useContext(Scope);
|
|
14
|
+
const value = useMemo(()=>({
|
|
15
|
+
dispatcher,
|
|
16
|
+
context,
|
|
17
|
+
attachments: [
|
|
18
|
+
...enclosing?.attachments ?? [],
|
|
19
|
+
...attachments
|
|
20
|
+
],
|
|
21
|
+
onFindings: onFindings ?? enclosing?.onFindings
|
|
22
|
+
}), [
|
|
23
|
+
dispatcher,
|
|
24
|
+
context,
|
|
25
|
+
attachments,
|
|
26
|
+
onFindings,
|
|
27
|
+
enclosing
|
|
28
|
+
]);
|
|
29
|
+
return /*#__PURE__*/ jsx(Scope.Provider, {
|
|
30
|
+
value: value,
|
|
31
|
+
children: children
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reads the enclosing interaction scope.
|
|
36
|
+
*
|
|
37
|
+
* @returns The scope, or `undefined` when the tree is rendered without one.
|
|
38
|
+
* @remarks
|
|
39
|
+
* Rendering without a scope is legitimate - a preview surface showing what a screen looks like has nothing to
|
|
40
|
+
* dispatch to - so this answers `undefined` rather than throwing. What must not happen is an element silently
|
|
41
|
+
* appearing interactive when nothing can run; {@link useInteractions} is where that is decided.
|
|
42
|
+
*/ function useInteractionScope() {
|
|
43
|
+
return useContext(Scope);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Builds the attachment list for one element, so a caller does not have to know the shape.
|
|
47
|
+
*
|
|
48
|
+
* @param level What is being attached to, used when reporting.
|
|
49
|
+
* @param behaviors The behaviors attached there.
|
|
50
|
+
* @returns The attachment, or an empty list when there are none.
|
|
51
|
+
*/ function attachmentFor(level, behaviors) {
|
|
52
|
+
return behaviors && behaviors.length > 0 ? [
|
|
53
|
+
{
|
|
54
|
+
level,
|
|
55
|
+
behaviors
|
|
56
|
+
}
|
|
57
|
+
] : [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { InteractionScope, attachmentFor, useInteractionScope };
|
|
61
|
+
//# sourceMappingURL=InteractionScope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractionScope.js","sources":["../../../interactions/InteractionScope.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ReactNode, createContext, useContext, useMemo } from 'react';\nimport { ActionDispatcher, BehaviorAttachment, InteractionContext, InteractionFinding } from '@cratis/scene.engine';\nimport { Behavior } from '@cratis/scene.model';\n\n/**\n * What the elements beneath a scope need in order to run an interaction.\n */\nexport interface InteractionScopeValue {\n /** The seam that performs the effects. */\n dispatcher: ActionDispatcher;\n\n /** How bindings and localization resolve. */\n context: InteractionContext;\n\n /** The attachments enclosing this point, outermost first. */\n attachments: BehaviorAttachment[];\n\n /** Where a finding goes. Absent means nothing is listening, which is itself worth knowing. */\n onFindings?: (findings: InteractionFinding[]) => void;\n}\n\nconst Scope = createContext<InteractionScopeValue | undefined>(undefined);\n\nexport interface InteractionScopeProps extends InteractionScopeValue {\n children: ReactNode;\n}\n\n/**\n * Provides the dispatcher, the resolution context and the enclosing attachments to everything beneath it.\n *\n * Attachments nest: a scope inside another scope appends to it rather than replacing it, so an element's\n * enclosing chain is assembled by the tree rather than reconstructed at each leaf. That is what keeps the\n * additive rule true in a renderer - a module's behavior is still in scope at a button six levels down,\n * without the button knowing the module exists.\n */\nexport function InteractionScope({ children, dispatcher, context, attachments, onFindings }: InteractionScopeProps) {\n const enclosing = useContext(Scope);\n\n const value = useMemo<InteractionScopeValue>(() => ({\n dispatcher,\n context,\n attachments: [...(enclosing?.attachments ?? []), ...attachments],\n onFindings: onFindings ?? enclosing?.onFindings,\n }), [dispatcher, context, attachments, onFindings, enclosing]);\n\n return <Scope.Provider value={value}>{children}</Scope.Provider>;\n}\n\n/**\n * Reads the enclosing interaction scope.\n *\n * @returns The scope, or `undefined` when the tree is rendered without one.\n * @remarks\n * Rendering without a scope is legitimate - a preview surface showing what a screen looks like has nothing to\n * dispatch to - so this answers `undefined` rather than throwing. What must not happen is an element silently\n * appearing interactive when nothing can run; {@link useInteractions} is where that is decided.\n */\nexport function useInteractionScope(): InteractionScopeValue | undefined {\n return useContext(Scope);\n}\n\n/**\n * Builds the attachment list for one element, so a caller does not have to know the shape.\n *\n * @param level What is being attached to, used when reporting.\n * @param behaviors The behaviors attached there.\n * @returns The attachment, or an empty list when there are none.\n */\nexport function attachmentFor(level: string, behaviors?: Behavior[]): BehaviorAttachment[] {\n return behaviors && behaviors.length > 0 ? [{ level, behaviors }] : [];\n}\n"],"names":["Scope","createContext","undefined","InteractionScope","children","dispatcher","context","attachments","onFindings","enclosing","useContext","value","useMemo","_jsx","Provider","useInteractionScope","attachmentFor","level","behaviors","length"],"mappings":";;;AAwBA,MAAMA,sBAAQC,aAAAA,CAAiDC,SAAAA,CAAAA;AAM/D;;;;;;;AAOC,IACM,SAASC,gBAAAA,CAAiB,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,OAAO,EAAEC,WAAW,EAAEC,UAAU,EAAyB,EAAA;AAC9G,IAAA,MAAMC,YAAYC,UAAAA,CAAWV,KAAAA,CAAAA;IAE7B,MAAMW,KAAAA,GAAQC,OAAAA,CAA+B,KAAO;AAChDP,YAAAA,UAAAA;AACAC,YAAAA,OAAAA;YACAC,WAAAA,EAAa;AAAKE,gBAAAA,GAAAA,SAAAA,EAAWF,eAAe,EAAE;AAAMA,gBAAAA,GAAAA;AAAY,aAAA;AAChEC,YAAAA,UAAAA,EAAYA,cAAcC,SAAAA,EAAWD;AACzC,SAAA,CAAA,EAAI;AAACH,QAAAA,UAAAA;AAAYC,QAAAA,OAAAA;AAASC,QAAAA,WAAAA;AAAaC,QAAAA,UAAAA;AAAYC,QAAAA;AAAU,KAAA,CAAA;IAE7D,qBAAOI,GAAA,CAACb,MAAMc,QAAQ,EAAA;QAACH,KAAAA,EAAOA,KAAAA;AAAQP,QAAAA,QAAAA,EAAAA;;AAC1C;AAEA;;;;;;;;AAQC,IACM,SAASW,mBAAAA,GAAAA;AACZ,IAAA,OAAOL,UAAAA,CAAWV,KAAAA,CAAAA;AACtB;AAEA;;;;;;AAMC,IACM,SAASgB,aAAAA,CAAcC,KAAa,EAAEC,SAAsB,EAAA;AAC/D,IAAA,OAAOA,SAAAA,IAAaA,SAAAA,CAAUC,MAAM,GAAG,CAAA,GAAI;AAAC,QAAA;AAAEF,YAAAA,KAAAA;AAAOC,YAAAA;AAAU;AAAE,KAAA,GAAG,EAAE;AAC1E;;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ActionDispatcher, CommandOutcome, DialogOutcome } from '@cratis/scene.engine';
|
|
2
|
+
import { NotificationLevel } from '@cratis/scene.model';
|
|
3
|
+
export interface BrowserDispatcherHost {
|
|
4
|
+
executeCommand?: (command: string, args: Record<string, unknown>) => Promise<CommandOutcome>;
|
|
5
|
+
navigate?: (screen: string, args: Record<string, unknown>) => void;
|
|
6
|
+
navigateBack?: () => void;
|
|
7
|
+
openDialog?: (dialogTemplate: string, args: Record<string, unknown>) => Promise<DialogOutcome>;
|
|
8
|
+
closeDialog?: () => void;
|
|
9
|
+
refreshQuery?: (query: string) => Promise<void>;
|
|
10
|
+
setState?: (target: string, value: unknown) => void;
|
|
11
|
+
notify?: (level: NotificationLevel, message: string) => void;
|
|
12
|
+
confirm?: (message: string) => Promise<boolean>;
|
|
13
|
+
raise?: (trigger: string, args: Record<string, unknown>) => Promise<void>;
|
|
14
|
+
onUnsupported?: (action: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function createBrowserDispatcher(host?: BrowserDispatcherHost): ActionDispatcher;
|
|
17
|
+
//# sourceMappingURL=createBrowserDispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createBrowserDispatcher.d.ts","sourceRoot":"","sources":["../../../interactions/createBrowserDispatcher.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AASxD,MAAM,WAAW,qBAAqB;IAElC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAG7F,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAGnE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAG1B,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAG/F,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAGzB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAGpD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAG7D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAGhD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG1E,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAYD,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,qBAA0B,GAAG,gBAAgB,CAqG1F"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
/**
|
|
4
|
+
* Creates the dispatcher a browser host uses.
|
|
5
|
+
*
|
|
6
|
+
* This is the only place in Scene.React that knows an action has to *happen*. Everything about which actions
|
|
7
|
+
* run and in what order is the engine's, which is why this file is a list of one-line delegations and holds no
|
|
8
|
+
* rules of its own.
|
|
9
|
+
*
|
|
10
|
+
* @param host What the surrounding application can actually do.
|
|
11
|
+
* @returns The {@link ActionDispatcher}.
|
|
12
|
+
*/ function createBrowserDispatcher(host = {}) {
|
|
13
|
+
const unsupported = (action)=>{
|
|
14
|
+
host.onUnsupported?.(action);
|
|
15
|
+
console.warn(`[scene] The host cannot '${action}', so the interaction stopped there.`);
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
async executeCommand (command, args) {
|
|
19
|
+
if (!host.executeCommand) {
|
|
20
|
+
unsupported(`execute ${command}`);
|
|
21
|
+
// Reported as a failure, so the document's own `on failure` runs. Answering success would
|
|
22
|
+
// make a command that never reached a backend look like one that worked.
|
|
23
|
+
return {
|
|
24
|
+
isSuccess: false,
|
|
25
|
+
validationErrors: []
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return host.executeCommand(command, args);
|
|
29
|
+
},
|
|
30
|
+
navigate (screen, args) {
|
|
31
|
+
if (host.navigate) {
|
|
32
|
+
host.navigate(screen, args);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
unsupported(`navigate to ${screen}`);
|
|
36
|
+
},
|
|
37
|
+
navigateBack () {
|
|
38
|
+
if (host.navigateBack) {
|
|
39
|
+
host.navigateBack();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
unsupported('navigate back');
|
|
43
|
+
},
|
|
44
|
+
async openDialog (dialogTemplate, args) {
|
|
45
|
+
if (!host.openDialog) {
|
|
46
|
+
unsupported(`open dialog ${dialogTemplate}`);
|
|
47
|
+
return {
|
|
48
|
+
isConfirmed: false
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return host.openDialog(dialogTemplate, args);
|
|
52
|
+
},
|
|
53
|
+
closeDialog () {
|
|
54
|
+
if (host.closeDialog) {
|
|
55
|
+
host.closeDialog();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
unsupported('close dialog');
|
|
59
|
+
},
|
|
60
|
+
async refreshQuery (query) {
|
|
61
|
+
if (host.refreshQuery) {
|
|
62
|
+
await host.refreshQuery(query);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
unsupported(`refresh ${query}`);
|
|
66
|
+
},
|
|
67
|
+
setState (target, value) {
|
|
68
|
+
if (host.setState) {
|
|
69
|
+
host.setState(target, value);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
unsupported(`set ${target}`);
|
|
73
|
+
},
|
|
74
|
+
notify (level, message) {
|
|
75
|
+
if (host.notify) {
|
|
76
|
+
host.notify(level, message);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// The console is a poor notification, but it is a real one - and a host that has not wired
|
|
80
|
+
// notifications yet should still see what the document wanted to say.
|
|
81
|
+
console.log(`[scene:${level}] ${message}`);
|
|
82
|
+
},
|
|
83
|
+
async confirm (message) {
|
|
84
|
+
if (host.confirm) return host.confirm(message);
|
|
85
|
+
// The browser's own prompt is the honest default: a confirm exists to gate what follows, so
|
|
86
|
+
// answering `true` without asking anyone would remove the gate rather than approximate it.
|
|
87
|
+
return typeof globalThis.confirm === 'function' ? globalThis.confirm(message) : false;
|
|
88
|
+
},
|
|
89
|
+
async raise (trigger, args) {
|
|
90
|
+
if (host.raise) {
|
|
91
|
+
await host.raise(trigger, args);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
unsupported(`raise ${trigger}`);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { createBrowserDispatcher };
|
|
100
|
+
//# sourceMappingURL=createBrowserDispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createBrowserDispatcher.js","sources":["../../../interactions/createBrowserDispatcher.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ActionDispatcher, CommandOutcome, DialogOutcome } from '@cratis/scene.engine';\nimport { NotificationLevel } from '@cratis/scene.model';\n\n/**\n * What a host has to supply for an interaction to reach anything real.\n *\n * Every member is optional and every one that is missing is *reported* rather than quietly ignored. A host\n * that cannot execute commands is a legitimate state - a preview surface, a design tool - but a button that\n * appears to work and does nothing is not.\n */\nexport interface BrowserDispatcherHost {\n /** Submits a command. The transport belongs to the host: Scene does not know what a backend is. */\n executeCommand?: (command: string, args: Record<string, unknown>) => Promise<CommandOutcome>;\n\n /** Navigates to a screen. The URL belongs to the host, which is why no route is constructed here. */\n navigate?: (screen: string, args: Record<string, unknown>) => void;\n\n /** Goes back. */\n navigateBack?: () => void;\n\n /** Opens a dialog and answers with what the user did. */\n openDialog?: (dialogTemplate: string, args: Record<string, unknown>) => Promise<DialogOutcome>;\n\n /** Closes the open dialog. */\n closeDialog?: () => void;\n\n /** Re-runs a query. */\n refreshQuery?: (query: string) => Promise<void>;\n\n /** Writes screen state. */\n setState?: (target: string, value: unknown) => void;\n\n /** Surfaces a message. Falls back to the console rather than to nothing. */\n notify?: (level: NotificationLevel, message: string) => void;\n\n /** Asks the user to confirm. Falls back to the browser's own prompt. */\n confirm?: (message: string) => Promise<boolean>;\n\n /** Raises an application trigger. */\n raise?: (trigger: string, args: Record<string, unknown>) => Promise<void>;\n\n /** Where an unsupported action is reported. */\n onUnsupported?: (action: string) => void;\n}\n\n/**\n * Creates the dispatcher a browser host uses.\n *\n * This is the only place in Scene.React that knows an action has to *happen*. Everything about which actions\n * run and in what order is the engine's, which is why this file is a list of one-line delegations and holds no\n * rules of its own.\n *\n * @param host What the surrounding application can actually do.\n * @returns The {@link ActionDispatcher}.\n */\nexport function createBrowserDispatcher(host: BrowserDispatcherHost = {}): ActionDispatcher {\n const unsupported = (action: string) => {\n host.onUnsupported?.(action);\n console.warn(`[scene] The host cannot '${action}', so the interaction stopped there.`);\n };\n\n return {\n async executeCommand(command, args) {\n if (!host.executeCommand) {\n unsupported(`execute ${command}`);\n\n // Reported as a failure, so the document's own `on failure` runs. Answering success would\n // make a command that never reached a backend look like one that worked.\n return { isSuccess: false, validationErrors: [] };\n }\n\n return host.executeCommand(command, args);\n },\n\n navigate(screen, args) {\n if (host.navigate) {\n host.navigate(screen, args);\n return;\n }\n\n unsupported(`navigate to ${screen}`);\n },\n\n navigateBack() {\n if (host.navigateBack) {\n host.navigateBack();\n return;\n }\n\n unsupported('navigate back');\n },\n\n async openDialog(dialogTemplate, args) {\n if (!host.openDialog) {\n unsupported(`open dialog ${dialogTemplate}`);\n return { isConfirmed: false };\n }\n\n return host.openDialog(dialogTemplate, args);\n },\n\n closeDialog() {\n if (host.closeDialog) {\n host.closeDialog();\n return;\n }\n\n unsupported('close dialog');\n },\n\n async refreshQuery(query) {\n if (host.refreshQuery) {\n await host.refreshQuery(query);\n return;\n }\n\n unsupported(`refresh ${query}`);\n },\n\n setState(target, value) {\n if (host.setState) {\n host.setState(target, value);\n return;\n }\n\n unsupported(`set ${target}`);\n },\n\n notify(level, message) {\n if (host.notify) {\n host.notify(level, message);\n return;\n }\n\n // The console is a poor notification, but it is a real one - and a host that has not wired\n // notifications yet should still see what the document wanted to say.\n console.log(`[scene:${level}] ${message}`);\n },\n\n async confirm(message) {\n if (host.confirm) return host.confirm(message);\n\n // The browser's own prompt is the honest default: a confirm exists to gate what follows, so\n // answering `true` without asking anyone would remove the gate rather than approximate it.\n return typeof globalThis.confirm === 'function' ? globalThis.confirm(message) : false;\n },\n\n async raise(trigger, args) {\n if (host.raise) {\n await host.raise(trigger, args);\n return;\n }\n\n unsupported(`raise ${trigger}`);\n },\n };\n}\n"],"names":["createBrowserDispatcher","host","unsupported","action","onUnsupported","console","warn","executeCommand","command","args","isSuccess","validationErrors","navigate","screen","navigateBack","openDialog","dialogTemplate","isConfirmed","closeDialog","refreshQuery","query","setState","target","value","notify","level","message","log","confirm","globalThis","raise","trigger"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../interactions/index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../interactions/index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Behavior } from '@cratis/scene.model';
|
|
2
|
+
export interface InteractionHandlers {
|
|
3
|
+
onClick?: (event: {
|
|
4
|
+
stopPropagation(): void;
|
|
5
|
+
}) => void;
|
|
6
|
+
onDoubleClick?: (event: {
|
|
7
|
+
stopPropagation(): void;
|
|
8
|
+
}) => void;
|
|
9
|
+
onChange?: () => void;
|
|
10
|
+
onSubmit?: (event: {
|
|
11
|
+
preventDefault(): void;
|
|
12
|
+
}) => void;
|
|
13
|
+
onSelect?: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function useInteractions(level: string, behaviors?: Behavior[]): InteractionHandlers;
|
|
16
|
+
//# sourceMappingURL=useInteractions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInteractions.d.ts","sourceRoot":"","sources":["../../../interactions/useInteractions.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAA0B,MAAM,qBAAqB,CAAC;AAUvE,MAAM,WAAW,mBAAmB;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,eAAe,IAAI,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IACvD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,eAAe,IAAI,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,cAAc,IAAI,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA0BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAoE1F"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useMemo, useCallback, useEffect } from 'react';
|
|
2
|
+
import { runBindings, resolveBehaviors, resolveIntervalBehaviors } from '@cratis/scene.engine';
|
|
3
|
+
import { InteractionTriggerKind } from '@cratis/scene.model';
|
|
4
|
+
import { useInteractionScope, attachmentFor } from './InteractionScope.js';
|
|
5
|
+
|
|
6
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
7
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
8
|
+
const domTriggers = [
|
|
9
|
+
[
|
|
10
|
+
'onClick',
|
|
11
|
+
InteractionTriggerKind.Click
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
'onDoubleClick',
|
|
15
|
+
InteractionTriggerKind.DoubleClick
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
'onChange',
|
|
19
|
+
InteractionTriggerKind.Change
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
'onSubmit',
|
|
23
|
+
InteractionTriggerKind.Submit
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
'onSelect',
|
|
27
|
+
InteractionTriggerKind.Select
|
|
28
|
+
]
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Wires the behaviors attached to an element to the events that start them.
|
|
32
|
+
*
|
|
33
|
+
* The hook resolves and runs; it decides nothing. Which bindings a trigger runs, in what order, and where a
|
|
34
|
+
* sequence stops are all the engine's, so a different renderer inherits the same semantics instead of
|
|
35
|
+
* reimplementing them - and so all of it stays testable without a DOM.
|
|
36
|
+
*
|
|
37
|
+
* Lifecycle triggers are handled here rather than by the caller because React is what knows when an element
|
|
38
|
+
* mounts: `load` and `unload` follow the element, and `enter` and `leave` follow the screen. An `interval`
|
|
39
|
+
* becomes a timer that is cleared on unmount, because a document declaring one is asking for it to run while
|
|
40
|
+
* the thing it is attached to is on screen and not afterwards.
|
|
41
|
+
*
|
|
42
|
+
* @param level What is being attached to, used when reporting a finding.
|
|
43
|
+
* @param behaviors The behaviors attached to this element.
|
|
44
|
+
* @returns The handlers to spread onto the rendered element.
|
|
45
|
+
*/ function useInteractions(level, behaviors) {
|
|
46
|
+
const scope = useInteractionScope();
|
|
47
|
+
const attachments = useMemo(()=>[
|
|
48
|
+
...scope?.attachments ?? [],
|
|
49
|
+
...attachmentFor(level, behaviors)
|
|
50
|
+
], [
|
|
51
|
+
scope,
|
|
52
|
+
level,
|
|
53
|
+
behaviors
|
|
54
|
+
]);
|
|
55
|
+
const run = useCallback(async (kind)=>{
|
|
56
|
+
if (!scope) return;
|
|
57
|
+
const result = await runBindings(resolveBehaviors(attachments, kind), scope.dispatcher, scope.context);
|
|
58
|
+
if (result.findings.length > 0) scope.onFindings?.(result.findings);
|
|
59
|
+
}, [
|
|
60
|
+
scope,
|
|
61
|
+
attachments
|
|
62
|
+
]);
|
|
63
|
+
// Mount and unmount, for the element and for the screen it is on. Running the unload and leave bindings
|
|
64
|
+
// from the cleanup is what makes them reliable: React guarantees it, a component remembering to call
|
|
65
|
+
// something on the way out does not.
|
|
66
|
+
useEffect(()=>{
|
|
67
|
+
void run(InteractionTriggerKind.Load);
|
|
68
|
+
void run(InteractionTriggerKind.Enter);
|
|
69
|
+
return ()=>{
|
|
70
|
+
void run(InteractionTriggerKind.Unload);
|
|
71
|
+
void run(InteractionTriggerKind.Leave);
|
|
72
|
+
};
|
|
73
|
+
}, [
|
|
74
|
+
run
|
|
75
|
+
]);
|
|
76
|
+
useEffect(()=>{
|
|
77
|
+
const intervals = resolveIntervalBehaviors(attachments);
|
|
78
|
+
if (intervals.length === 0 || !scope) return;
|
|
79
|
+
const timers = intervals.map((candidate)=>{
|
|
80
|
+
const seconds = candidate.binding.trigger.seconds ?? 0;
|
|
81
|
+
return setInterval(()=>void runBindings([
|
|
82
|
+
candidate
|
|
83
|
+
], scope.dispatcher, scope.context), Math.max(seconds, 1) * 1000);
|
|
84
|
+
});
|
|
85
|
+
return ()=>timers.forEach(clearInterval);
|
|
86
|
+
}, [
|
|
87
|
+
attachments,
|
|
88
|
+
scope
|
|
89
|
+
]);
|
|
90
|
+
return useMemo(()=>{
|
|
91
|
+
const handlers = {};
|
|
92
|
+
if (!scope) return handlers;
|
|
93
|
+
for (const [handler, kind] of domTriggers){
|
|
94
|
+
// Nothing attached for this trigger means no handler at all. Attaching an empty one would make
|
|
95
|
+
// every element look interactive and would stop clicks reaching whatever encloses it.
|
|
96
|
+
if (resolveBehaviors(attachments, kind).length === 0) continue;
|
|
97
|
+
if (handler === 'onSubmit') {
|
|
98
|
+
handlers.onSubmit = (event)=>{
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
void run(kind);
|
|
101
|
+
};
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
handlers[handler] = (event)=>{
|
|
105
|
+
// An interaction that ran here is handled here. Without this, a click on a row inside a table
|
|
106
|
+
// that both declare `on click` would run the table's twice: once for the row, once on the way up.
|
|
107
|
+
event?.stopPropagation();
|
|
108
|
+
void run(kind);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return handlers;
|
|
112
|
+
}, [
|
|
113
|
+
scope,
|
|
114
|
+
attachments,
|
|
115
|
+
run
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export { useInteractions };
|
|
120
|
+
//# sourceMappingURL=useInteractions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInteractions.js","sources":["../../../interactions/useInteractions.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useEffect, useMemo } from 'react';\nimport { BehaviorAttachment, resolveBehaviors, resolveIntervalBehaviors, runBindings } from '@cratis/scene.engine';\nimport { Behavior, InteractionTriggerKind } from '@cratis/scene.model';\nimport { attachmentFor, useInteractionScope } from './InteractionScope';\n\n/**\n * The DOM handlers an element should spread onto whatever it renders.\n *\n * Only the handlers a document actually asks for are present. An element with no `on click` gets no `onClick`,\n * so it does not become clickable - and, just as importantly, does not swallow the click from something\n * enclosing it that does.\n */\nexport interface InteractionHandlers {\n onClick?: (event: { stopPropagation(): void }) => void;\n onDoubleClick?: (event: { stopPropagation(): void }) => void;\n onChange?: () => void;\n onSubmit?: (event: { preventDefault(): void }) => void;\n onSelect?: () => void;\n}\n\nconst domTriggers: [keyof InteractionHandlers, InteractionTriggerKind][] = [\n ['onClick', InteractionTriggerKind.Click],\n ['onDoubleClick', InteractionTriggerKind.DoubleClick],\n ['onChange', InteractionTriggerKind.Change],\n ['onSubmit', InteractionTriggerKind.Submit],\n ['onSelect', InteractionTriggerKind.Select],\n];\n\n/**\n * Wires the behaviors attached to an element to the events that start them.\n *\n * The hook resolves and runs; it decides nothing. Which bindings a trigger runs, in what order, and where a\n * sequence stops are all the engine's, so a different renderer inherits the same semantics instead of\n * reimplementing them - and so all of it stays testable without a DOM.\n *\n * Lifecycle triggers are handled here rather than by the caller because React is what knows when an element\n * mounts: `load` and `unload` follow the element, and `enter` and `leave` follow the screen. An `interval`\n * becomes a timer that is cleared on unmount, because a document declaring one is asking for it to run while\n * the thing it is attached to is on screen and not afterwards.\n *\n * @param level What is being attached to, used when reporting a finding.\n * @param behaviors The behaviors attached to this element.\n * @returns The handlers to spread onto the rendered element.\n */\nexport function useInteractions(level: string, behaviors?: Behavior[]): InteractionHandlers {\n const scope = useInteractionScope();\n\n const attachments = useMemo<BehaviorAttachment[]>(\n () => [...(scope?.attachments ?? []), ...attachmentFor(level, behaviors)],\n [scope, level, behaviors]);\n\n const run = useCallback(async (kind: InteractionTriggerKind) => {\n if (!scope) return;\n\n const result = await runBindings(resolveBehaviors(attachments, kind), scope.dispatcher, scope.context);\n if (result.findings.length > 0) scope.onFindings?.(result.findings);\n }, [scope, attachments]);\n\n // Mount and unmount, for the element and for the screen it is on. Running the unload and leave bindings\n // from the cleanup is what makes them reliable: React guarantees it, a component remembering to call\n // something on the way out does not.\n useEffect(() => {\n void run(InteractionTriggerKind.Load);\n void run(InteractionTriggerKind.Enter);\n\n return () => {\n void run(InteractionTriggerKind.Unload);\n void run(InteractionTriggerKind.Leave);\n };\n }, [run]);\n\n useEffect(() => {\n const intervals = resolveIntervalBehaviors(attachments);\n if (intervals.length === 0 || !scope) return;\n\n const timers = intervals.map(candidate => {\n const seconds = (candidate.binding.trigger as { seconds?: number }).seconds ?? 0;\n return setInterval(\n () => void runBindings([candidate], scope.dispatcher, scope.context),\n Math.max(seconds, 1) * 1000);\n });\n\n return () => timers.forEach(clearInterval);\n }, [attachments, scope]);\n\n return useMemo(() => {\n const handlers: InteractionHandlers = {};\n if (!scope) return handlers;\n\n for (const [handler, kind] of domTriggers) {\n // Nothing attached for this trigger means no handler at all. Attaching an empty one would make\n // every element look interactive and would stop clicks reaching whatever encloses it.\n if (resolveBehaviors(attachments, kind).length === 0) continue;\n\n if (handler === 'onSubmit') {\n handlers.onSubmit = event => {\n event.preventDefault();\n void run(kind);\n };\n continue;\n }\n\n handlers[handler] = ((event?: { stopPropagation(): void }) => {\n // An interaction that ran here is handled here. Without this, a click on a row inside a table\n // that both declare `on click` would run the table's twice: once for the row, once on the way up.\n event?.stopPropagation();\n void run(kind);\n }) as never;\n }\n\n return handlers;\n }, [scope, attachments, run]);\n}\n"],"names":["domTriggers","InteractionTriggerKind","Click","DoubleClick","Change","Submit","Select","useInteractions","level","behaviors","scope","useInteractionScope","attachments","useMemo","attachmentFor","run","useCallback","kind","result","runBindings","resolveBehaviors","dispatcher","context","findings","length","onFindings","useEffect","Load","Enter","Unload","Leave","intervals","resolveIntervalBehaviors","timers","map","candidate","seconds","binding","trigger","setInterval","Math","max","forEach","clearInterval","handlers","handler","onSubmit","event","preventDefault","stopPropagation"],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import { ExternalComponent } from '@cratis/scene.model';
|
|
3
|
+
import { InteractionHandlers } from '../interactions';
|
|
3
4
|
export interface RegisteredComponentProps {
|
|
4
5
|
element: ExternalComponent;
|
|
5
6
|
slots: Record<string, ReactNode[]>;
|
|
7
|
+
interactions?: InteractionHandlers;
|
|
6
8
|
}
|
|
7
9
|
export type ComponentRegistry = Record<string, ComponentType<RegisteredComponentProps>>;
|
|
8
10
|
//# sourceMappingURL=ComponentRegistry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentRegistry.d.ts","sourceRoot":"","sources":["../../../renderer/ComponentRegistry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ComponentRegistry.d.ts","sourceRoot":"","sources":["../../../renderer/ComponentRegistry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAKtD,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAUnC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAUD,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { ExternalComponent } from '@cratis/scene.model';
|
|
3
|
+
import { RegisteredComponentProps } from './ComponentRegistry';
|
|
4
|
+
export interface InteractiveComponentProps {
|
|
5
|
+
component: ComponentType<RegisteredComponentProps>;
|
|
6
|
+
element: ExternalComponent;
|
|
7
|
+
slots: Record<string, ReactNode[]>;
|
|
8
|
+
}
|
|
9
|
+
export declare function InteractiveComponent({ component, element, slots }: InteractiveComponentProps): import("react").ReactElement<RegisteredComponentProps, string | import("react").JSXElementConstructor<any>>;
|
|
10
|
+
//# sourceMappingURL=InteractiveComponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractiveComponent.d.ts","sourceRoot":"","sources":["../../../renderer/InteractiveComponent.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAiB,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;IACnD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;CACtC;AASD,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,yBAAyB,+GAG5F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import 'react/jsx-runtime';
|
|
3
|
+
import { useInteractions } from '../interactions/useInteractions.js';
|
|
4
|
+
|
|
5
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
6
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
7
|
+
/**
|
|
8
|
+
* Resolves an element's interactions and hands them to the component that renders it.
|
|
9
|
+
*
|
|
10
|
+
* This exists as a component rather than as a call inside the renderer because resolving them is a hook, and a
|
|
11
|
+
* hook cannot run inside the plain function a {@link Renderer} is. Keeping the renderer plain is what lets the
|
|
12
|
+
* same element tree be rendered by something that is not React at all.
|
|
13
|
+
*/ function InteractiveComponent({ component, element, slots }) {
|
|
14
|
+
const interactions = useInteractions(element.id, element.behaviors);
|
|
15
|
+
return /*#__PURE__*/ createElement(component, {
|
|
16
|
+
element,
|
|
17
|
+
slots,
|
|
18
|
+
interactions
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { InteractiveComponent };
|
|
23
|
+
//# sourceMappingURL=InteractiveComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractiveComponent.js","sources":["../../../renderer/InteractiveComponent.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ComponentType, ReactNode, createElement } from 'react';\nimport { ExternalComponent } from '@cratis/scene.model';\nimport { useInteractions } from '../interactions';\nimport { RegisteredComponentProps } from './ComponentRegistry';\n\nexport interface InteractiveComponentProps {\n component: ComponentType<RegisteredComponentProps>;\n element: ExternalComponent;\n slots: Record<string, ReactNode[]>;\n}\n\n/**\n * Resolves an element's interactions and hands them to the component that renders it.\n *\n * This exists as a component rather than as a call inside the renderer because resolving them is a hook, and a\n * hook cannot run inside the plain function a {@link Renderer} is. Keeping the renderer plain is what lets the\n * same element tree be rendered by something that is not React at all.\n */\nexport function InteractiveComponent({ component, element, slots }: InteractiveComponentProps) {\n const interactions = useInteractions(element.id, element.behaviors);\n return createElement(component, { element, slots, interactions });\n}\n"],"names":["InteractiveComponent","component","element","slots","interactions","useInteractions","id","behaviors","createElement"],"mappings":";;;;AAAA;AACA;AAaA;;;;;;IAOO,SAASA,oBAAAA,CAAqB,EAAEC,SAAS,EAAEC,OAAO,EAAEC,KAAK,EAA6B,EAAA;AACzF,IAAA,MAAMC,eAAeC,eAAAA,CAAgBH,OAAAA,CAAQI,EAAE,EAAEJ,QAAQK,SAAS,CAAA;AAClE,IAAA,qBAAOC,cAAcP,SAAAA,EAAW;AAAEC,QAAAA,OAAAA;AAASC,QAAAA,KAAAA;AAAOC,QAAAA;AAAa,KAAA,CAAA;AACnE;;"}
|