@cratis/scene.react 3.7.0 → 4.1.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.
Files changed (125) hide show
  1. package/core/CoreButton.tsx +2 -2
  2. package/dist/cjs/SceneElementView.js +2 -0
  3. package/dist/cjs/SceneElementView.js.map +1 -1
  4. package/dist/cjs/core/CoreButton.js +2 -1
  5. package/dist/cjs/core/CoreButton.js.map +1 -1
  6. package/dist/cjs/index.js +18 -0
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/interactions/ConfirmHost.js +71 -0
  9. package/dist/cjs/interactions/ConfirmHost.js.map +1 -0
  10. package/dist/cjs/interactions/InteractionScope.js +74 -0
  11. package/dist/cjs/interactions/InteractionScope.js.map +1 -0
  12. package/dist/cjs/interactions/NotificationHost.js +73 -0
  13. package/dist/cjs/interactions/NotificationHost.js.map +1 -0
  14. package/dist/cjs/interactions/UnresolvedActionHost.js +53 -0
  15. package/dist/cjs/interactions/UnresolvedActionHost.js.map +1 -0
  16. package/dist/cjs/interactions/createBrowserDispatcher.js +102 -0
  17. package/dist/cjs/interactions/createBrowserDispatcher.js.map +1 -0
  18. package/dist/cjs/interactions/useInteractionHosts.js +109 -0
  19. package/dist/cjs/interactions/useInteractionHosts.js.map +1 -0
  20. package/dist/cjs/interactions/useInteractions.js +122 -0
  21. package/dist/cjs/interactions/useInteractions.js.map +1 -0
  22. package/dist/cjs/renderer/InteractiveComponent.js +26 -0
  23. package/dist/cjs/renderer/InteractiveComponent.js.map +1 -0
  24. package/dist/cjs/renderer/createReactRenderer.js +3 -1
  25. package/dist/cjs/renderer/createReactRenderer.js.map +1 -1
  26. package/dist/esm/SceneElementView.js +2 -0
  27. package/dist/esm/SceneElementView.js.map +1 -1
  28. package/dist/esm/core/CoreButton.d.ts +1 -1
  29. package/dist/esm/core/CoreButton.d.ts.map +1 -1
  30. package/dist/esm/core/CoreButton.js +2 -1
  31. package/dist/esm/core/CoreButton.js.map +1 -1
  32. package/dist/esm/for_useInteractionHosts/when_a_document_asks_the_user_something.d.ts +2 -0
  33. package/dist/esm/for_useInteractionHosts/when_a_document_asks_the_user_something.d.ts.map +1 -0
  34. package/dist/esm/for_useInteractionHosts/when_a_document_asks_the_user_something.js +73 -0
  35. package/dist/esm/for_useInteractionHosts/when_a_document_asks_the_user_something.js.map +1 -0
  36. package/dist/esm/for_useInteractionHosts/when_the_host_cannot_do_what_was_asked.d.ts +2 -0
  37. package/dist/esm/for_useInteractionHosts/when_the_host_cannot_do_what_was_asked.d.ts.map +1 -0
  38. package/dist/esm/for_useInteractionHosts/when_the_host_cannot_do_what_was_asked.js +43 -0
  39. package/dist/esm/for_useInteractionHosts/when_the_host_cannot_do_what_was_asked.js.map +1 -0
  40. package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.d.ts +2 -0
  41. package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.d.ts.map +1 -0
  42. package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.js +94 -0
  43. package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.js.map +1 -0
  44. package/dist/esm/index.d.ts +1 -0
  45. package/dist/esm/index.d.ts.map +1 -1
  46. package/dist/esm/index.js +8 -0
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/interactions/ConfirmHost.d.ts +8 -0
  49. package/dist/esm/interactions/ConfirmHost.d.ts.map +1 -0
  50. package/dist/esm/interactions/ConfirmHost.js +69 -0
  51. package/dist/esm/interactions/ConfirmHost.js.map +1 -0
  52. package/dist/esm/interactions/ConfirmRequest.d.ts +5 -0
  53. package/dist/esm/interactions/ConfirmRequest.d.ts.map +1 -0
  54. package/dist/esm/interactions/ConfirmRequest.js +2 -0
  55. package/dist/esm/interactions/ConfirmRequest.js.map +1 -0
  56. package/dist/esm/interactions/InteractionScope.d.ts +17 -0
  57. package/dist/esm/interactions/InteractionScope.d.ts.map +1 -0
  58. package/dist/esm/interactions/InteractionScope.js +70 -0
  59. package/dist/esm/interactions/InteractionScope.js.map +1 -0
  60. package/dist/esm/interactions/NotificationHost.d.ts +7 -0
  61. package/dist/esm/interactions/NotificationHost.d.ts.map +1 -0
  62. package/dist/esm/interactions/NotificationHost.js +71 -0
  63. package/dist/esm/interactions/NotificationHost.js.map +1 -0
  64. package/dist/esm/interactions/SceneNotification.d.ts +7 -0
  65. package/dist/esm/interactions/SceneNotification.d.ts.map +1 -0
  66. package/dist/esm/interactions/SceneNotification.js +2 -0
  67. package/dist/esm/interactions/SceneNotification.js.map +1 -0
  68. package/dist/esm/interactions/UnresolvedAction.d.ts +5 -0
  69. package/dist/esm/interactions/UnresolvedAction.d.ts.map +1 -0
  70. package/dist/esm/interactions/UnresolvedAction.js +2 -0
  71. package/dist/esm/interactions/UnresolvedAction.js.map +1 -0
  72. package/dist/esm/interactions/UnresolvedActionHost.d.ts +6 -0
  73. package/dist/esm/interactions/UnresolvedActionHost.d.ts.map +1 -0
  74. package/dist/esm/interactions/UnresolvedActionHost.js +51 -0
  75. package/dist/esm/interactions/UnresolvedActionHost.js.map +1 -0
  76. package/dist/esm/interactions/createBrowserDispatcher.d.ts +17 -0
  77. package/dist/esm/interactions/createBrowserDispatcher.d.ts.map +1 -0
  78. package/dist/esm/interactions/createBrowserDispatcher.js +100 -0
  79. package/dist/esm/interactions/createBrowserDispatcher.js.map +1 -0
  80. package/dist/esm/interactions/index.d.ts +11 -0
  81. package/dist/esm/interactions/index.d.ts.map +1 -0
  82. package/dist/esm/interactions/index.js +11 -0
  83. package/dist/esm/interactions/index.js.map +1 -0
  84. package/dist/esm/interactions/useInteractionHosts.d.ts +11 -0
  85. package/dist/esm/interactions/useInteractionHosts.d.ts.map +1 -0
  86. package/dist/esm/interactions/useInteractionHosts.js +107 -0
  87. package/dist/esm/interactions/useInteractionHosts.js.map +1 -0
  88. package/dist/esm/interactions/useInteractions.d.ts +16 -0
  89. package/dist/esm/interactions/useInteractions.d.ts.map +1 -0
  90. package/dist/esm/interactions/useInteractions.js +120 -0
  91. package/dist/esm/interactions/useInteractions.js.map +1 -0
  92. package/dist/esm/renderer/ComponentRegistry.d.ts +2 -0
  93. package/dist/esm/renderer/ComponentRegistry.d.ts.map +1 -1
  94. package/dist/esm/renderer/InteractiveComponent.d.ts +10 -0
  95. package/dist/esm/renderer/InteractiveComponent.d.ts.map +1 -0
  96. package/dist/esm/renderer/InteractiveComponent.js +24 -0
  97. package/dist/esm/renderer/InteractiveComponent.js.map +1 -0
  98. package/dist/esm/renderer/createReactRenderer.d.ts.map +1 -1
  99. package/dist/esm/renderer/createReactRenderer.js +3 -1
  100. package/dist/esm/renderer/createReactRenderer.js.map +1 -1
  101. package/dist/esm/renderer/index.d.ts +1 -0
  102. package/dist/esm/renderer/index.d.ts.map +1 -1
  103. package/dist/esm/renderer/index.js +1 -0
  104. package/dist/esm/renderer/index.js.map +1 -1
  105. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  106. package/for_useInteractionHosts/when_a_document_asks_the_user_something.tsx +106 -0
  107. package/for_useInteractionHosts/when_the_host_cannot_do_what_was_asked.tsx +66 -0
  108. package/for_useInteractions/when_clicking_an_element_that_executes_a_command.tsx +141 -0
  109. package/index.ts +1 -0
  110. package/interactions/ConfirmHost.tsx +42 -0
  111. package/interactions/ConfirmRequest.ts +19 -0
  112. package/interactions/InteractionScope.tsx +94 -0
  113. package/interactions/NotificationHost.tsx +48 -0
  114. package/interactions/SceneNotification.ts +18 -0
  115. package/interactions/UnresolvedAction.ts +17 -0
  116. package/interactions/UnresolvedActionHost.tsx +33 -0
  117. package/interactions/createBrowserDispatcher.ts +160 -0
  118. package/interactions/index.ts +13 -0
  119. package/interactions/useInteractionHosts.tsx +101 -0
  120. package/interactions/useInteractions.ts +116 -0
  121. package/package.json +5 -5
  122. package/renderer/ComponentRegistry.ts +11 -0
  123. package/renderer/InteractiveComponent.tsx +25 -0
  124. package/renderer/createReactRenderer.tsx +2 -1
  125. package/renderer/index.ts +1 -0
@@ -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,11 @@
1
+ export * from './InteractionScope';
2
+ export * from './useInteractions';
3
+ export * from './createBrowserDispatcher';
4
+ export * from './ConfirmHost';
5
+ export * from './ConfirmRequest';
6
+ export * from './NotificationHost';
7
+ export * from './SceneNotification';
8
+ export * from './UnresolvedAction';
9
+ export * from './UnresolvedActionHost';
10
+ export * from './useInteractionHosts';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -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;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './InteractionScope';
2
+ export * from './useInteractions';
3
+ export * from './createBrowserDispatcher';
4
+ export * from './ConfirmHost';
5
+ export * from './ConfirmRequest';
6
+ export * from './NotificationHost';
7
+ export * from './SceneNotification';
8
+ export * from './UnresolvedAction';
9
+ export * from './UnresolvedActionHost';
10
+ export * from './useInteractionHosts';
11
+ //# sourceMappingURL=index.js.map
@@ -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;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ import { BrowserDispatcherHost } from './createBrowserDispatcher';
3
+ export interface InteractionHosts {
4
+ readonly host: Pick<BrowserDispatcherHost, 'notify' | 'confirm' | 'onUnsupported'>;
5
+ readonly surfaces: ReactNode;
6
+ }
7
+ export interface InteractionHostOptions {
8
+ readonly dismissAfter?: number;
9
+ }
10
+ export declare function useInteractionHosts(options?: InteractionHostOptions): InteractionHosts;
11
+ //# sourceMappingURL=useInteractionHosts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInteractionHosts.d.ts","sourceRoot":"","sources":["../../../interactions/useInteractionHosts.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAA0C,MAAM,OAAO,CAAC;AAE1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAWlE,MAAM,WAAW,gBAAgB;IAE7B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC,CAAC;IAGnF,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAChC;AAKD,MAAM,WAAW,sBAAsB;IAEnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAClC;AAuBD,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,sBAA2B,GAAG,gBAAgB,CA+C1F"}
@@ -0,0 +1,107 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useRef, useCallback, useMemo } from 'react';
3
+ import { ConfirmHost } from './ConfirmHost.js';
4
+ import { NotificationHost } from './NotificationHost.js';
5
+ import { UnresolvedActionHost } from './UnresolvedActionHost.js';
6
+
7
+ /**
8
+ * Provides the default surfaces for the actions that need somewhere to appear.
9
+ *
10
+ * `notify` and `confirm` are the two actions a document can take that have no backend and no route: they are
11
+ * purely things the user sees. Without a surface, a host either invents one or the action goes to the console,
12
+ * which is why these ship with Scene rather than being every application's first chore.
13
+ *
14
+ * The three are returned as one unit because they are wired the same way and forgetting to render one of them
15
+ * is exactly the failure they exist to prevent:
16
+ *
17
+ * ```tsx
18
+ * const { host, surfaces } = useInteractionHosts();
19
+ * const dispatcher = useMemo(() => createBrowserDispatcher({ ...host, executeCommand, navigate }), [host]);
20
+ *
21
+ * return <InteractionScope dispatcher={dispatcher}>{children}{surfaces}</InteractionScope>;
22
+ * ```
23
+ *
24
+ * An application with a design system of its own passes its own `notify` after the spread and keeps the rest.
25
+ * @param options The {@link InteractionHostOptions}.
26
+ * @returns The {@link InteractionHosts}.
27
+ */ function useInteractionHosts(options = {}) {
28
+ const { dismissAfter = 6000 } = options;
29
+ const [notifications, setNotifications] = useState([]);
30
+ const [unresolved, setUnresolved] = useState([]);
31
+ const [request, setRequest] = useState(undefined);
32
+ const nextId = useRef(0);
33
+ const dismiss = useCallback((id)=>setNotifications((current)=>current.filter((_)=>_.id !== id)), []);
34
+ const notify = useCallback((level, message)=>{
35
+ const id = nextId.current++;
36
+ setNotifications((current)=>[
37
+ ...current,
38
+ {
39
+ id,
40
+ level,
41
+ message
42
+ }
43
+ ]);
44
+ if (dismissAfter > 0) {
45
+ setTimeout(()=>setNotifications((current)=>current.filter((_)=>_.id !== id)), dismissAfter);
46
+ }
47
+ }, [
48
+ dismissAfter
49
+ ]);
50
+ const confirm = useCallback((message)=>new Promise((resolve)=>{
51
+ setRequest({
52
+ message,
53
+ answer (confirmed) {
54
+ // Cleared before resolving, so the sequence that continues cannot find a question on screen
55
+ // that has already been answered.
56
+ setRequest(undefined);
57
+ resolve(confirmed);
58
+ }
59
+ });
60
+ }), []);
61
+ const onUnsupported = useCallback((action)=>{
62
+ // Reported once per distinct action. An unsupported action on a hover or an interval would otherwise
63
+ // grow the list until it covered the application it is trying to tell you about.
64
+ setUnresolved((current)=>current.some((_)=>_.action === action) ? current : [
65
+ ...current,
66
+ {
67
+ id: nextId.current++,
68
+ action
69
+ }
70
+ ]);
71
+ }, []);
72
+ const host = useMemo(()=>({
73
+ notify,
74
+ confirm,
75
+ onUnsupported
76
+ }), [
77
+ notify,
78
+ confirm,
79
+ onUnsupported
80
+ ]);
81
+ const surfaces = useMemo(()=>/*#__PURE__*/ jsxs(Fragment, {
82
+ children: [
83
+ /*#__PURE__*/ jsx(NotificationHost, {
84
+ notifications: notifications,
85
+ onDismiss: dismiss
86
+ }),
87
+ /*#__PURE__*/ jsx(ConfirmHost, {
88
+ request: request
89
+ }),
90
+ /*#__PURE__*/ jsx(UnresolvedActionHost, {
91
+ unresolved: unresolved
92
+ })
93
+ ]
94
+ }), [
95
+ notifications,
96
+ dismiss,
97
+ request,
98
+ unresolved
99
+ ]);
100
+ return {
101
+ host,
102
+ surfaces
103
+ };
104
+ }
105
+
106
+ export { useInteractionHosts };
107
+ //# sourceMappingURL=useInteractionHosts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInteractionHosts.js","sources":["../../../interactions/useInteractionHosts.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, useCallback, useMemo, useRef, useState } from 'react';\nimport { NotificationLevel } from '@cratis/scene.model';\nimport { BrowserDispatcherHost } from './createBrowserDispatcher';\nimport { ConfirmHost } from './ConfirmHost';\nimport { ConfirmRequest } from './ConfirmRequest';\nimport { NotificationHost } from './NotificationHost';\nimport { SceneNotification } from './SceneNotification';\nimport { UnresolvedAction } from './UnresolvedAction';\nimport { UnresolvedActionHost } from './UnresolvedActionHost';\n\n/**\n * What {@link useInteractionHosts} gives an application.\n */\nexport interface InteractionHosts {\n /** The parts of a {@link BrowserDispatcherHost} these surfaces provide. Spread it into the host you build. */\n readonly host: Pick<BrowserDispatcherHost, 'notify' | 'confirm' | 'onUnsupported'>;\n\n /** The surfaces themselves. Render them once, anywhere inside the application. */\n readonly surfaces: ReactNode;\n}\n\n/**\n * How long a notification stays before it removes itself, in milliseconds.\n */\nexport interface InteractionHostOptions {\n /** How long a notification stays up. `0` keeps it until it is dismissed. */\n readonly dismissAfter?: number;\n}\n\n/**\n * Provides the default surfaces for the actions that need somewhere to appear.\n *\n * `notify` and `confirm` are the two actions a document can take that have no backend and no route: they are\n * purely things the user sees. Without a surface, a host either invents one or the action goes to the console,\n * which is why these ship with Scene rather than being every application's first chore.\n *\n * The three are returned as one unit because they are wired the same way and forgetting to render one of them\n * is exactly the failure they exist to prevent:\n *\n * ```tsx\n * const { host, surfaces } = useInteractionHosts();\n * const dispatcher = useMemo(() => createBrowserDispatcher({ ...host, executeCommand, navigate }), [host]);\n *\n * return <InteractionScope dispatcher={dispatcher}>{children}{surfaces}</InteractionScope>;\n * ```\n *\n * An application with a design system of its own passes its own `notify` after the spread and keeps the rest.\n * @param options The {@link InteractionHostOptions}.\n * @returns The {@link InteractionHosts}.\n */\nexport function useInteractionHosts(options: InteractionHostOptions = {}): InteractionHosts {\n const { dismissAfter = 6000 } = options;\n const [notifications, setNotifications] = useState<readonly SceneNotification[]>([]);\n const [unresolved, setUnresolved] = useState<readonly UnresolvedAction[]>([]);\n const [request, setRequest] = useState<ConfirmRequest | undefined>(undefined);\n const nextId = useRef(0);\n\n const dismiss = useCallback((id: number) => setNotifications(current => current.filter(_ => _.id !== id)), []);\n\n const notify = useCallback((level: NotificationLevel, message: string) => {\n const id = nextId.current++;\n setNotifications(current => [...current, { id, level, message }]);\n\n if (dismissAfter > 0) {\n setTimeout(() => setNotifications(current => current.filter(_ => _.id !== id)), dismissAfter);\n }\n }, [dismissAfter]);\n\n const confirm = useCallback((message: string) => new Promise<boolean>(resolve => {\n setRequest({\n message,\n answer(confirmed) {\n // Cleared before resolving, so the sequence that continues cannot find a question on screen\n // that has already been answered.\n setRequest(undefined);\n resolve(confirmed);\n },\n });\n }), []);\n\n const onUnsupported = useCallback((action: string) => {\n // Reported once per distinct action. An unsupported action on a hover or an interval would otherwise\n // grow the list until it covered the application it is trying to tell you about.\n setUnresolved(current => current.some(_ => _.action === action) ? current : [...current, { id: nextId.current++, action }]);\n }, []);\n\n const host = useMemo(() => ({ notify, confirm, onUnsupported }), [notify, confirm, onUnsupported]);\n\n const surfaces = useMemo(() => (\n <>\n <NotificationHost notifications={notifications} onDismiss={dismiss} />\n <ConfirmHost request={request} />\n <UnresolvedActionHost unresolved={unresolved} />\n </>\n ), [notifications, dismiss, request, unresolved]);\n\n return { host, surfaces };\n}\n"],"names":["useInteractionHosts","options","dismissAfter","notifications","setNotifications","useState","unresolved","setUnresolved","request","setRequest","undefined","nextId","useRef","dismiss","useCallback","id","current","filter","_","notify","level","message","setTimeout","confirm","Promise","resolve","answer","confirmed","onUnsupported","action","some","host","useMemo","surfaces","_jsxs","_Fragment","_jsx","NotificationHost","onDismiss","ConfirmHost","UnresolvedActionHost"],"mappings":"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"}
@@ -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;AAKxD,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;CACtC;AAUD,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC,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,24 @@
1
+ import { createElement } from 'react';
2
+ import 'react/jsx-runtime';
3
+ import { useInteractions } from '../interactions/useInteractions.js';
4
+ import '../interactions/NotificationHost.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
+ /**
9
+ * Resolves an element's interactions and hands them to the component that renders it.
10
+ *
11
+ * This exists as a component rather than as a call inside the renderer because resolving them is a hook, and a
12
+ * hook cannot run inside the plain function a {@link Renderer} is. Keeping the renderer plain is what lets the
13
+ * same element tree be rendered by something that is not React at all.
14
+ */ function InteractiveComponent({ component, element, slots }) {
15
+ const interactions = useInteractions(element.id, element.behaviors);
16
+ return /*#__PURE__*/ createElement(component, {
17
+ element,
18
+ slots,
19
+ interactions
20
+ });
21
+ }
22
+
23
+ export { InteractiveComponent };
24
+ //# 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;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createReactRenderer.d.ts","sourceRoot":"","sources":["../../../renderer/createReactRenderer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAiB,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAYxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,QAAQ,CAAC,SAAS,CAAC,CAgCpF"}
1
+ {"version":3,"file":"createReactRenderer.d.ts","sourceRoot":"","sources":["../../../renderer/createReactRenderer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAiB,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAaxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,QAAQ,CAAC,SAAS,CAAC,CAgCpF"}
@@ -1,4 +1,5 @@
1
1
  import { createElement } from 'react';
2
+ import { InteractiveComponent } from './InteractiveComponent.js';
2
3
  import { childStyle, panelStyle } from './panelLayout.js';
3
4
  import { UnresolvedComponent } from './UnresolvedComponent.js';
4
5
 
@@ -29,8 +30,9 @@ import { UnresolvedComponent } from './UnresolvedComponent.js';
29
30
  },
30
31
  renderExternalComponent (element, slots) {
31
32
  const Component = registry[element.componentName] ?? UnresolvedComponent;
32
- return /*#__PURE__*/ createElement(Component, {
33
+ return /*#__PURE__*/ createElement(InteractiveComponent, {
33
34
  key: element.id,
35
+ component: Component,
34
36
  element,
35
37
  slots
36
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"createReactRenderer.js","sources":["../../../renderer/createReactRenderer.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, createElement } from 'react';\nimport { Renderer } from '@cratis/scene.engine';\nimport { ContentControl, ExternalComponent, ItemsControl, Panel } from '@cratis/scene.model';\nimport { ComponentRegistry } from './ComponentRegistry';\nimport { childStyle, panelStyle } from './panelLayout';\nimport { UnresolvedComponent } from './UnresolvedComponent';\n\n/**\n * Creates a {@link Renderer} that turns a Scene element tree into React elements.\n *\n * `ItemsControl` and `ContentControl` render as plain wrapping `div`s. A `Panel` renders as the\n * arrangement it declares - a grid, a stack, a wrap or a dock - because that is what those panels are\n * for; see `panelLayout` for the mapping. Arranging a *layout's slots* by `flow`/`freeform` is a\n * different thing and stays Scene#4's, layered on top of this renderer rather than folded into it.\n */\nexport function createReactRenderer(registry: ComponentRegistry): Renderer<ReactNode> {\n return {\n renderContentControl(element: ContentControl, content: ReactNode): ReactNode {\n return createElement('div', { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'ContentControl' }, content);\n },\n\n renderItemsControl(element: ItemsControl, items: ReactNode[]): ReactNode {\n return createElement('div', { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'ItemsControl' }, items);\n },\n\n renderExternalComponent(element: ExternalComponent, slots: Record<string, ReactNode[]>): ReactNode {\n const Component = registry[element.componentName] ?? UnresolvedComponent;\n return createElement(Component, { key: element.id, element, slots });\n },\n\n renderPanel(element: Panel, children: ReactNode[]): ReactNode {\n // The rendered children arrive in the same order as `element.children`, so a panel that places\n // its children individually - a grid cell, a docked edge - can wrap each one in the box that\n // places it without needing to re-render anything.\n const placed = children.map((child, index) => {\n const style = childStyle(element, element.children[index], index);\n return style\n ? createElement('div', { key: `${element.id}-${index}`, 'data-scene-placement': index, style }, child)\n : child;\n });\n\n return createElement(\n 'div',\n { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'Panel', style: panelStyle(element) },\n placed);\n },\n };\n}\n"],"names":["createReactRenderer","registry","renderContentControl","element","content","createElement","key","id","renderItemsControl","items","renderExternalComponent","slots","Component","componentName","UnresolvedComponent","renderPanel","children","placed","map","child","index","style","childStyle","panelStyle"],"mappings":";;;;AAAA;AACA;AASA;;;;;;;IAQO,SAASA,mBAAAA,CAAoBC,QAA2B,EAAA;IAC3D,OAAO;QACHC,oBAAAA,CAAAA,CAAqBC,OAAuB,EAAEC,OAAkB,EAAA;AAC5D,YAAA,qBAAOC,cAAc,KAAA,EAAO;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB;aAAiB,EAAGH,OAAAA,CAAAA;AACvH,QAAA,CAAA;QAEAI,kBAAAA,CAAAA,CAAmBL,OAAqB,EAAEM,KAAkB,EAAA;AACxD,YAAA,qBAAOJ,cAAc,KAAA,EAAO;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB;aAAe,EAAGE,KAAAA,CAAAA;AACrH,QAAA,CAAA;QAEAC,uBAAAA,CAAAA,CAAwBP,OAA0B,EAAEQ,KAAkC,EAAA;AAClF,YAAA,MAAMC,YAAYX,QAAQ,CAACE,OAAAA,CAAQU,aAAa,CAAC,IAAIC,mBAAAA;AACrD,YAAA,qBAAOT,cAAcO,SAAAA,EAAW;AAAEN,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAEJ,gBAAAA,OAAAA;AAASQ,gBAAAA;AAAM,aAAA,CAAA;AACtE,QAAA,CAAA;QAEAI,WAAAA,CAAAA,CAAYZ,OAAc,EAAEa,QAAqB,EAAA;;;;AAI7C,YAAA,MAAMC,MAAAA,GAASD,QAAAA,CAASE,GAAG,CAAC,CAACC,KAAAA,EAAOC,KAAAA,GAAAA;AAChC,gBAAA,MAAMC,QAAQC,UAAAA,CAAWnB,OAAAA,EAASA,QAAQa,QAAQ,CAACI,MAAM,EAAEA,KAAAA,CAAAA;gBAC3D,OAAOC,KAAAA,iBACDhB,cAAc,KAAA,EAAO;AAAEC,oBAAAA,GAAAA,EAAK,GAAGH,OAAAA,CAAQI,EAAE,CAAC,CAAC,EAAEa,KAAAA,CAAAA,CAAO;oBAAE,sBAAA,EAAwBA,KAAAA;AAAOC,oBAAAA;AAAM,iBAAA,EAAGF,KAAAA,CAAAA,GAC9FA,KAAAA;AACV,YAAA,CAAA,CAAA;AAEA,YAAA,qBAAOd,cACH,KAAA,EACA;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB,OAAA;AAASc,gBAAAA,KAAAA,EAAOE,UAAAA,CAAWpB,OAAAA;aAAS,EACvGc,MAAAA,CAAAA;AACR,QAAA;AACJ,KAAA;AACJ;;"}
1
+ {"version":3,"file":"createReactRenderer.js","sources":["../../../renderer/createReactRenderer.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, createElement } from 'react';\nimport { Renderer } from '@cratis/scene.engine';\nimport { ContentControl, ExternalComponent, ItemsControl, Panel } from '@cratis/scene.model';\nimport { ComponentRegistry } from './ComponentRegistry';\nimport { InteractiveComponent } from './InteractiveComponent';\nimport { childStyle, panelStyle } from './panelLayout';\nimport { UnresolvedComponent } from './UnresolvedComponent';\n\n/**\n * Creates a {@link Renderer} that turns a Scene element tree into React elements.\n *\n * `ItemsControl` and `ContentControl` render as plain wrapping `div`s. A `Panel` renders as the\n * arrangement it declares - a grid, a stack, a wrap or a dock - because that is what those panels are\n * for; see `panelLayout` for the mapping. Arranging a *layout's slots* by `flow`/`freeform` is a\n * different thing and stays Scene#4's, layered on top of this renderer rather than folded into it.\n */\nexport function createReactRenderer(registry: ComponentRegistry): Renderer<ReactNode> {\n return {\n renderContentControl(element: ContentControl, content: ReactNode): ReactNode {\n return createElement('div', { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'ContentControl' }, content);\n },\n\n renderItemsControl(element: ItemsControl, items: ReactNode[]): ReactNode {\n return createElement('div', { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'ItemsControl' }, items);\n },\n\n renderExternalComponent(element: ExternalComponent, slots: Record<string, ReactNode[]>): ReactNode {\n const Component = registry[element.componentName] ?? UnresolvedComponent;\n return createElement(InteractiveComponent, { key: element.id, component: Component, element, slots });\n },\n\n renderPanel(element: Panel, children: ReactNode[]): ReactNode {\n // The rendered children arrive in the same order as `element.children`, so a panel that places\n // its children individually - a grid cell, a docked edge - can wrap each one in the box that\n // places it without needing to re-render anything.\n const placed = children.map((child, index) => {\n const style = childStyle(element, element.children[index], index);\n return style\n ? createElement('div', { key: `${element.id}-${index}`, 'data-scene-placement': index, style }, child)\n : child;\n });\n\n return createElement(\n 'div',\n { key: element.id, 'data-scene-id': element.id, 'data-scene-kind': 'Panel', style: panelStyle(element) },\n placed);\n },\n };\n}\n"],"names":["createReactRenderer","registry","renderContentControl","element","content","createElement","key","id","renderItemsControl","items","renderExternalComponent","slots","Component","componentName","UnresolvedComponent","InteractiveComponent","component","renderPanel","children","placed","map","child","index","style","childStyle","panelStyle"],"mappings":";;;;;AAAA;AACA;AAUA;;;;;;;IAQO,SAASA,mBAAAA,CAAoBC,QAA2B,EAAA;IAC3D,OAAO;QACHC,oBAAAA,CAAAA,CAAqBC,OAAuB,EAAEC,OAAkB,EAAA;AAC5D,YAAA,qBAAOC,cAAc,KAAA,EAAO;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB;aAAiB,EAAGH,OAAAA,CAAAA;AACvH,QAAA,CAAA;QAEAI,kBAAAA,CAAAA,CAAmBL,OAAqB,EAAEM,KAAkB,EAAA;AACxD,YAAA,qBAAOJ,cAAc,KAAA,EAAO;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB;aAAe,EAAGE,KAAAA,CAAAA;AACrH,QAAA,CAAA;QAEAC,uBAAAA,CAAAA,CAAwBP,OAA0B,EAAEQ,KAAkC,EAAA;AAClF,YAAA,MAAMC,YAAYX,QAAQ,CAACE,OAAAA,CAAQU,aAAa,CAAC,IAAIC,mBAAAA;AACrD,YAAA,qBAAOT,cAAcU,oBAAAA,EAAsB;AAAET,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;gBAAES,SAAAA,EAAWJ,SAAAA;AAAWT,gBAAAA,OAAAA;AAASQ,gBAAAA;AAAM,aAAA,CAAA;AACvG,QAAA,CAAA;QAEAM,WAAAA,CAAAA,CAAYd,OAAc,EAAEe,QAAqB,EAAA;;;;AAI7C,YAAA,MAAMC,MAAAA,GAASD,QAAAA,CAASE,GAAG,CAAC,CAACC,KAAAA,EAAOC,KAAAA,GAAAA;AAChC,gBAAA,MAAMC,QAAQC,UAAAA,CAAWrB,OAAAA,EAASA,QAAQe,QAAQ,CAACI,MAAM,EAAEA,KAAAA,CAAAA;gBAC3D,OAAOC,KAAAA,iBACDlB,cAAc,KAAA,EAAO;AAAEC,oBAAAA,GAAAA,EAAK,GAAGH,OAAAA,CAAQI,EAAE,CAAC,CAAC,EAAEe,KAAAA,CAAAA,CAAO;oBAAE,sBAAA,EAAwBA,KAAAA;AAAOC,oBAAAA;AAAM,iBAAA,EAAGF,KAAAA,CAAAA,GAC9FA,KAAAA;AACV,YAAA,CAAA,CAAA;AAEA,YAAA,qBAAOhB,cACH,KAAA,EACA;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB,OAAA;AAASgB,gBAAAA,KAAAA,EAAOE,UAAAA,CAAWtB,OAAAA;aAAS,EACvGgB,MAAAA,CAAAA;AACR,QAAA;AACJ,KAAA;AACJ;;"}
@@ -1,4 +1,5 @@
1
1
  export * from './ComponentRegistry';
2
2
  export * from './UnresolvedComponent';
3
3
  export * from './createReactRenderer';
4
+ export * from './InteractiveComponent';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../renderer/index.ts"],"names":[],"mappings":"AAGA,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../renderer/index.ts"],"names":[],"mappings":"AAGA,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from './ComponentRegistry';
2
2
  export * from './UnresolvedComponent';
3
3
  export * from './createReactRenderer';
4
+ export * from './InteractiveComponent';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../renderer/index.ts"],"names":[],"mappings":"AAGA,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../renderer/index.ts"],"names":[],"mappings":"AAGA,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC"}