@cratis/scene.react 3.6.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
package/core/CoreButton.tsx
CHANGED
|
@@ -6,10 +6,10 @@ import { RegisteredComponentProps } from '../renderer';
|
|
|
6
6
|
/**
|
|
7
7
|
* The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.
|
|
8
8
|
*/
|
|
9
|
-
export function CoreButton({ element, slots }: RegisteredComponentProps) {
|
|
9
|
+
export function CoreButton({ element, slots, interactions }: RegisteredComponentProps) {
|
|
10
10
|
const label = typeof element.properties.label === 'string' ? element.properties.label : '';
|
|
11
11
|
return (
|
|
12
|
-
<button type="button" data-scene-id={element.id}>
|
|
12
|
+
<button type="button" data-scene-id={element.id} {...interactions}>
|
|
13
13
|
{label}
|
|
14
14
|
{slots.content}
|
|
15
15
|
</button>
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var scene_engine = require('@cratis/scene.engine');
|
|
6
6
|
var createReactRenderer = require('./renderer/createReactRenderer.js');
|
|
7
|
+
require('./interactions/useInteractions.js');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SceneElementView.js","sources":["../../SceneElementView.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 { useMemo } from 'react';\nimport { renderElement, BindingResolver } from '@cratis/scene.engine';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry, createReactRenderer } from './renderer';\n\nexport interface SceneElementViewProps {\n element: SceneElement;\n registry: ComponentRegistry;\n resolveBinding: BindingResolver;\n}\n\n/**\n * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's\n * preview surface and Stage's shipped web bundle both consume unmodified.\n */\nexport function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {\n const renderer = useMemo(() => createReactRenderer(registry), [registry]);\n return <>{renderElement(element, renderer, resolveBinding)}</>;\n}\n"],"names":["SceneElementView","element","registry","resolveBinding","renderer","useMemo","createReactRenderer","_jsx","_Fragment","renderElement"],"mappings":"
|
|
1
|
+
{"version":3,"file":"SceneElementView.js","sources":["../../SceneElementView.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 { useMemo } from 'react';\nimport { renderElement, BindingResolver } from '@cratis/scene.engine';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry, createReactRenderer } from './renderer';\n\nexport interface SceneElementViewProps {\n element: SceneElement;\n registry: ComponentRegistry;\n resolveBinding: BindingResolver;\n}\n\n/**\n * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's\n * preview surface and Stage's shipped web bundle both consume unmodified.\n */\nexport function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {\n const renderer = useMemo(() => createReactRenderer(registry), [registry]);\n return <>{renderElement(element, renderer, resolveBinding)}</>;\n}\n"],"names":["SceneElementView","element","registry","resolveBinding","renderer","useMemo","createReactRenderer","_jsx","_Fragment","renderElement"],"mappings":";;;;;;;;AAcA;;;IAIO,SAASA,gBAAAA,CAAiB,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAyB,EAAA;AACzF,IAAA,MAAMC,QAAAA,GAAWC,aAAAA,CAAQ,IAAMC,uCAAAA,CAAoBJ,QAAAA,CAAAA,EAAW;AAACA,QAAAA;AAAS,KAAA,CAAA;IACxE,qBAAOK,cAAA,CAAAC,mBAAA,EAAA;AAAGC,QAAAA,QAAAA,EAAAA,0BAAAA,CAAcR,SAASG,QAAAA,EAAUD,cAAAA;;AAC/C;;"}
|
|
@@ -6,11 +6,12 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
6
6
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
7
7
|
/**
|
|
8
8
|
* The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.
|
|
9
|
-
*/ function CoreButton({ element, slots }) {
|
|
9
|
+
*/ function CoreButton({ element, slots, interactions }) {
|
|
10
10
|
const label = typeof element.properties.label === 'string' ? element.properties.label : '';
|
|
11
11
|
return /*#__PURE__*/ jsxRuntime.jsxs("button", {
|
|
12
12
|
type: "button",
|
|
13
13
|
"data-scene-id": element.id,
|
|
14
|
+
...interactions,
|
|
14
15
|
children: [
|
|
15
16
|
label,
|
|
16
17
|
slots.content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoreButton.js","sources":["../../../core/CoreButton.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 { RegisteredComponentProps } from '../renderer';\n\n/**\n * The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.\n */\nexport function CoreButton({ element, slots }: RegisteredComponentProps) {\n const label = typeof element.properties.label === 'string' ? element.properties.label : '';\n return (\n <button type=\"button\" data-scene-id={element.id}>\n {label}\n {slots.content}\n </button>\n );\n}\n"],"names":["CoreButton","element","slots","label","properties","_jsxs","button","type","data-scene-id","id","content"],"mappings":";;;;AAAA;AACA;AAIA;;
|
|
1
|
+
{"version":3,"file":"CoreButton.js","sources":["../../../core/CoreButton.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 { RegisteredComponentProps } from '../renderer';\n\n/**\n * The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.\n */\nexport function CoreButton({ element, slots, interactions }: RegisteredComponentProps) {\n const label = typeof element.properties.label === 'string' ? element.properties.label : '';\n return (\n <button type=\"button\" data-scene-id={element.id} {...interactions}>\n {label}\n {slots.content}\n </button>\n );\n}\n"],"names":["CoreButton","element","slots","interactions","label","properties","_jsxs","button","type","data-scene-id","id","content"],"mappings":";;;;AAAA;AACA;AAIA;;IAGO,SAASA,UAAAA,CAAW,EAAEC,OAAO,EAAEC,KAAK,EAAEC,YAAY,EAA4B,EAAA;AACjF,IAAA,MAAMC,KAAAA,GAAQ,OAAOH,OAAAA,CAAQI,UAAU,CAACD,KAAK,KAAK,QAAA,GAAWH,OAAAA,CAAQI,UAAU,CAACD,KAAK,GAAG,EAAA;AACxF,IAAA,qBACIE,eAAA,CAACC,QAAAA,EAAAA;QAAOC,IAAAA,EAAK,QAAA;AAASC,QAAAA,eAAAA,EAAeR,QAAQS,EAAE;AAAG,QAAA,GAAGP,YAAY;;AAC5DC,YAAAA,KAAAA;AACAF,YAAAA,KAAAA,CAAMS;;;AAGnB;;"}
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var SceneElementView = require('./SceneElementView.js');
|
|
4
4
|
var UnresolvedComponent = require('./renderer/UnresolvedComponent.js');
|
|
5
5
|
var createReactRenderer = require('./renderer/createReactRenderer.js');
|
|
6
|
+
var InteractiveComponent = require('./renderer/InteractiveComponent.js');
|
|
6
7
|
var CoreText = require('./core/CoreText.js');
|
|
7
8
|
var CoreButton = require('./core/CoreButton.js');
|
|
8
9
|
var CoreCard = require('./core/CoreCard.js');
|
|
@@ -14,12 +15,16 @@ var extractNavigationItem = require('./NavBar/extractNavigationItem.js');
|
|
|
14
15
|
var ScenePackageBundle = require('./packages/ScenePackageBundle.js');
|
|
15
16
|
var themeTokens = require('./theme/themeTokens.js');
|
|
16
17
|
var SceneThemeProvider = require('./theme/SceneThemeProvider.js');
|
|
18
|
+
var InteractionScope = require('./interactions/InteractionScope.js');
|
|
19
|
+
var useInteractions = require('./interactions/useInteractions.js');
|
|
20
|
+
var createBrowserDispatcher = require('./interactions/createBrowserDispatcher.js');
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
exports.SceneElementView = SceneElementView.SceneElementView;
|
|
21
25
|
exports.UnresolvedComponent = UnresolvedComponent.UnresolvedComponent;
|
|
22
26
|
exports.createReactRenderer = createReactRenderer.createReactRenderer;
|
|
27
|
+
exports.InteractiveComponent = InteractiveComponent.InteractiveComponent;
|
|
23
28
|
exports.CoreText = CoreText.CoreText;
|
|
24
29
|
exports.CoreButton = CoreButton.CoreButton;
|
|
25
30
|
exports.CoreCard = CoreCard.CoreCard;
|
|
@@ -47,4 +52,9 @@ exports.themeTokenPrefix = themeTokens.themeTokenPrefix;
|
|
|
47
52
|
exports.themeTokenProperty = themeTokens.themeTokenProperty;
|
|
48
53
|
exports.SceneThemeProvider = SceneThemeProvider.SceneThemeProvider;
|
|
49
54
|
exports.useSceneTheme = SceneThemeProvider.useSceneTheme;
|
|
55
|
+
exports.InteractionScope = InteractionScope.InteractionScope;
|
|
56
|
+
exports.attachmentFor = InteractionScope.attachmentFor;
|
|
57
|
+
exports.useInteractionScope = InteractionScope.useInteractionScope;
|
|
58
|
+
exports.useInteractions = useInteractions.useInteractions;
|
|
59
|
+
exports.createBrowserDispatcher = createBrowserDispatcher.createBrowserDispatcher;
|
|
50
60
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/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,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
const Scope = /*#__PURE__*/ react.createContext(undefined);
|
|
7
|
+
/**
|
|
8
|
+
* Provides the dispatcher, the resolution context and the enclosing attachments to everything beneath it.
|
|
9
|
+
*
|
|
10
|
+
* Attachments nest: a scope inside another scope appends to it rather than replacing it, so an element's
|
|
11
|
+
* enclosing chain is assembled by the tree rather than reconstructed at each leaf. That is what keeps the
|
|
12
|
+
* additive rule true in a renderer - a module's behavior is still in scope at a button six levels down,
|
|
13
|
+
* without the button knowing the module exists.
|
|
14
|
+
*/ function InteractionScope({ children, dispatcher, context, attachments, onFindings }) {
|
|
15
|
+
const enclosing = react.useContext(Scope);
|
|
16
|
+
const value = react.useMemo(()=>({
|
|
17
|
+
dispatcher,
|
|
18
|
+
context,
|
|
19
|
+
attachments: [
|
|
20
|
+
...enclosing?.attachments ?? [],
|
|
21
|
+
...attachments
|
|
22
|
+
],
|
|
23
|
+
onFindings: onFindings ?? enclosing?.onFindings
|
|
24
|
+
}), [
|
|
25
|
+
dispatcher,
|
|
26
|
+
context,
|
|
27
|
+
attachments,
|
|
28
|
+
onFindings,
|
|
29
|
+
enclosing
|
|
30
|
+
]);
|
|
31
|
+
return /*#__PURE__*/ jsxRuntime.jsx(Scope.Provider, {
|
|
32
|
+
value: value,
|
|
33
|
+
children: children
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads the enclosing interaction scope.
|
|
38
|
+
*
|
|
39
|
+
* @returns The scope, or `undefined` when the tree is rendered without one.
|
|
40
|
+
* @remarks
|
|
41
|
+
* Rendering without a scope is legitimate - a preview surface showing what a screen looks like has nothing to
|
|
42
|
+
* dispatch to - so this answers `undefined` rather than throwing. What must not happen is an element silently
|
|
43
|
+
* appearing interactive when nothing can run; {@link useInteractions} is where that is decided.
|
|
44
|
+
*/ function useInteractionScope() {
|
|
45
|
+
return react.useContext(Scope);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds the attachment list for one element, so a caller does not have to know the shape.
|
|
49
|
+
*
|
|
50
|
+
* @param level What is being attached to, used when reporting.
|
|
51
|
+
* @param behaviors The behaviors attached there.
|
|
52
|
+
* @returns The attachment, or an empty list when there are none.
|
|
53
|
+
*/ function attachmentFor(level, behaviors) {
|
|
54
|
+
return behaviors && behaviors.length > 0 ? [
|
|
55
|
+
{
|
|
56
|
+
level,
|
|
57
|
+
behaviors
|
|
58
|
+
}
|
|
59
|
+
] : [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exports.InteractionScope = InteractionScope;
|
|
63
|
+
exports.attachmentFor = attachmentFor;
|
|
64
|
+
exports.useInteractionScope = useInteractionScope;
|
|
65
|
+
//# 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,mBAAAA,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,gBAAAA,CAAWV,KAAAA,CAAAA;IAE7B,MAAMW,KAAAA,GAAQC,aAAAA,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,cAAA,CAACb,MAAMc,QAAQ,EAAA;QAACH,KAAAA,EAAOA,KAAAA;AAAQP,QAAAA,QAAAA,EAAAA;;AAC1C;AAEA;;;;;;;;AAQC,IACM,SAASW,mBAAAA,GAAAA;AACZ,IAAA,OAAOL,gBAAAA,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,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
4
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
|
+
/**
|
|
6
|
+
* Creates the dispatcher a browser host uses.
|
|
7
|
+
*
|
|
8
|
+
* This is the only place in Scene.React that knows an action has to *happen*. Everything about which actions
|
|
9
|
+
* run and in what order is the engine's, which is why this file is a list of one-line delegations and holds no
|
|
10
|
+
* rules of its own.
|
|
11
|
+
*
|
|
12
|
+
* @param host What the surrounding application can actually do.
|
|
13
|
+
* @returns The {@link ActionDispatcher}.
|
|
14
|
+
*/ function createBrowserDispatcher(host = {}) {
|
|
15
|
+
const unsupported = (action)=>{
|
|
16
|
+
host.onUnsupported?.(action);
|
|
17
|
+
console.warn(`[scene] The host cannot '${action}', so the interaction stopped there.`);
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
async executeCommand (command, args) {
|
|
21
|
+
if (!host.executeCommand) {
|
|
22
|
+
unsupported(`execute ${command}`);
|
|
23
|
+
// Reported as a failure, so the document's own `on failure` runs. Answering success would
|
|
24
|
+
// make a command that never reached a backend look like one that worked.
|
|
25
|
+
return {
|
|
26
|
+
isSuccess: false,
|
|
27
|
+
validationErrors: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return host.executeCommand(command, args);
|
|
31
|
+
},
|
|
32
|
+
navigate (screen, args) {
|
|
33
|
+
if (host.navigate) {
|
|
34
|
+
host.navigate(screen, args);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
unsupported(`navigate to ${screen}`);
|
|
38
|
+
},
|
|
39
|
+
navigateBack () {
|
|
40
|
+
if (host.navigateBack) {
|
|
41
|
+
host.navigateBack();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
unsupported('navigate back');
|
|
45
|
+
},
|
|
46
|
+
async openDialog (dialogTemplate, args) {
|
|
47
|
+
if (!host.openDialog) {
|
|
48
|
+
unsupported(`open dialog ${dialogTemplate}`);
|
|
49
|
+
return {
|
|
50
|
+
isConfirmed: false
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return host.openDialog(dialogTemplate, args);
|
|
54
|
+
},
|
|
55
|
+
closeDialog () {
|
|
56
|
+
if (host.closeDialog) {
|
|
57
|
+
host.closeDialog();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
unsupported('close dialog');
|
|
61
|
+
},
|
|
62
|
+
async refreshQuery (query) {
|
|
63
|
+
if (host.refreshQuery) {
|
|
64
|
+
await host.refreshQuery(query);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
unsupported(`refresh ${query}`);
|
|
68
|
+
},
|
|
69
|
+
setState (target, value) {
|
|
70
|
+
if (host.setState) {
|
|
71
|
+
host.setState(target, value);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
unsupported(`set ${target}`);
|
|
75
|
+
},
|
|
76
|
+
notify (level, message) {
|
|
77
|
+
if (host.notify) {
|
|
78
|
+
host.notify(level, message);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// The console is a poor notification, but it is a real one - and a host that has not wired
|
|
82
|
+
// notifications yet should still see what the document wanted to say.
|
|
83
|
+
console.log(`[scene:${level}] ${message}`);
|
|
84
|
+
},
|
|
85
|
+
async confirm (message) {
|
|
86
|
+
if (host.confirm) return host.confirm(message);
|
|
87
|
+
// The browser's own prompt is the honest default: a confirm exists to gate what follows, so
|
|
88
|
+
// answering `true` without asking anyone would remove the gate rather than approximate it.
|
|
89
|
+
return typeof globalThis.confirm === 'function' ? globalThis.confirm(message) : false;
|
|
90
|
+
},
|
|
91
|
+
async raise (trigger, args) {
|
|
92
|
+
if (host.raise) {
|
|
93
|
+
await host.raise(trigger, args);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
unsupported(`raise ${trigger}`);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
exports.createBrowserDispatcher = createBrowserDispatcher;
|
|
102
|
+
//# 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,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var scene_engine = require('@cratis/scene.engine');
|
|
5
|
+
var scene_model = require('@cratis/scene.model');
|
|
6
|
+
var InteractionScope = require('./InteractionScope.js');
|
|
7
|
+
|
|
8
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
9
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
10
|
+
const domTriggers = [
|
|
11
|
+
[
|
|
12
|
+
'onClick',
|
|
13
|
+
scene_model.InteractionTriggerKind.Click
|
|
14
|
+
],
|
|
15
|
+
[
|
|
16
|
+
'onDoubleClick',
|
|
17
|
+
scene_model.InteractionTriggerKind.DoubleClick
|
|
18
|
+
],
|
|
19
|
+
[
|
|
20
|
+
'onChange',
|
|
21
|
+
scene_model.InteractionTriggerKind.Change
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
'onSubmit',
|
|
25
|
+
scene_model.InteractionTriggerKind.Submit
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
'onSelect',
|
|
29
|
+
scene_model.InteractionTriggerKind.Select
|
|
30
|
+
]
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Wires the behaviors attached to an element to the events that start them.
|
|
34
|
+
*
|
|
35
|
+
* The hook resolves and runs; it decides nothing. Which bindings a trigger runs, in what order, and where a
|
|
36
|
+
* sequence stops are all the engine's, so a different renderer inherits the same semantics instead of
|
|
37
|
+
* reimplementing them - and so all of it stays testable without a DOM.
|
|
38
|
+
*
|
|
39
|
+
* Lifecycle triggers are handled here rather than by the caller because React is what knows when an element
|
|
40
|
+
* mounts: `load` and `unload` follow the element, and `enter` and `leave` follow the screen. An `interval`
|
|
41
|
+
* becomes a timer that is cleared on unmount, because a document declaring one is asking for it to run while
|
|
42
|
+
* the thing it is attached to is on screen and not afterwards.
|
|
43
|
+
*
|
|
44
|
+
* @param level What is being attached to, used when reporting a finding.
|
|
45
|
+
* @param behaviors The behaviors attached to this element.
|
|
46
|
+
* @returns The handlers to spread onto the rendered element.
|
|
47
|
+
*/ function useInteractions(level, behaviors) {
|
|
48
|
+
const scope = InteractionScope.useInteractionScope();
|
|
49
|
+
const attachments = react.useMemo(()=>[
|
|
50
|
+
...scope?.attachments ?? [],
|
|
51
|
+
...InteractionScope.attachmentFor(level, behaviors)
|
|
52
|
+
], [
|
|
53
|
+
scope,
|
|
54
|
+
level,
|
|
55
|
+
behaviors
|
|
56
|
+
]);
|
|
57
|
+
const run = react.useCallback(async (kind)=>{
|
|
58
|
+
if (!scope) return;
|
|
59
|
+
const result = await scene_engine.runBindings(scene_engine.resolveBehaviors(attachments, kind), scope.dispatcher, scope.context);
|
|
60
|
+
if (result.findings.length > 0) scope.onFindings?.(result.findings);
|
|
61
|
+
}, [
|
|
62
|
+
scope,
|
|
63
|
+
attachments
|
|
64
|
+
]);
|
|
65
|
+
// Mount and unmount, for the element and for the screen it is on. Running the unload and leave bindings
|
|
66
|
+
// from the cleanup is what makes them reliable: React guarantees it, a component remembering to call
|
|
67
|
+
// something on the way out does not.
|
|
68
|
+
react.useEffect(()=>{
|
|
69
|
+
void run(scene_model.InteractionTriggerKind.Load);
|
|
70
|
+
void run(scene_model.InteractionTriggerKind.Enter);
|
|
71
|
+
return ()=>{
|
|
72
|
+
void run(scene_model.InteractionTriggerKind.Unload);
|
|
73
|
+
void run(scene_model.InteractionTriggerKind.Leave);
|
|
74
|
+
};
|
|
75
|
+
}, [
|
|
76
|
+
run
|
|
77
|
+
]);
|
|
78
|
+
react.useEffect(()=>{
|
|
79
|
+
const intervals = scene_engine.resolveIntervalBehaviors(attachments);
|
|
80
|
+
if (intervals.length === 0 || !scope) return;
|
|
81
|
+
const timers = intervals.map((candidate)=>{
|
|
82
|
+
const seconds = candidate.binding.trigger.seconds ?? 0;
|
|
83
|
+
return setInterval(()=>void scene_engine.runBindings([
|
|
84
|
+
candidate
|
|
85
|
+
], scope.dispatcher, scope.context), Math.max(seconds, 1) * 1000);
|
|
86
|
+
});
|
|
87
|
+
return ()=>timers.forEach(clearInterval);
|
|
88
|
+
}, [
|
|
89
|
+
attachments,
|
|
90
|
+
scope
|
|
91
|
+
]);
|
|
92
|
+
return react.useMemo(()=>{
|
|
93
|
+
const handlers = {};
|
|
94
|
+
if (!scope) return handlers;
|
|
95
|
+
for (const [handler, kind] of domTriggers){
|
|
96
|
+
// Nothing attached for this trigger means no handler at all. Attaching an empty one would make
|
|
97
|
+
// every element look interactive and would stop clicks reaching whatever encloses it.
|
|
98
|
+
if (scene_engine.resolveBehaviors(attachments, kind).length === 0) continue;
|
|
99
|
+
if (handler === 'onSubmit') {
|
|
100
|
+
handlers.onSubmit = (event)=>{
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
void run(kind);
|
|
103
|
+
};
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
handlers[handler] = (event)=>{
|
|
107
|
+
// An interaction that ran here is handled here. Without this, a click on a row inside a table
|
|
108
|
+
// that both declare `on click` would run the table's twice: once for the row, once on the way up.
|
|
109
|
+
event?.stopPropagation();
|
|
110
|
+
void run(kind);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return handlers;
|
|
114
|
+
}, [
|
|
115
|
+
scope,
|
|
116
|
+
attachments,
|
|
117
|
+
run
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
exports.useInteractions = useInteractions;
|
|
122
|
+
//# 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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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
require('react/jsx-runtime');
|
|
5
|
+
var useInteractions = require('../interactions/useInteractions.js');
|
|
6
|
+
|
|
7
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
8
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
9
|
+
/**
|
|
10
|
+
* Resolves an element's interactions and hands them to the component that renders it.
|
|
11
|
+
*
|
|
12
|
+
* This exists as a component rather than as a call inside the renderer because resolving them is a hook, and a
|
|
13
|
+
* hook cannot run inside the plain function a {@link Renderer} is. Keeping the renderer plain is what lets the
|
|
14
|
+
* same element tree be rendered by something that is not React at all.
|
|
15
|
+
*/ function InteractiveComponent({ component, element, slots }) {
|
|
16
|
+
const interactions = useInteractions.useInteractions(element.id, element.behaviors);
|
|
17
|
+
return /*#__PURE__*/ react.createElement(component, {
|
|
18
|
+
element,
|
|
19
|
+
slots,
|
|
20
|
+
interactions
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
exports.InteractiveComponent = InteractiveComponent;
|
|
25
|
+
//# 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,+BAAAA,CAAgBH,OAAAA,CAAQI,EAAE,EAAEJ,QAAQK,SAAS,CAAA;AAClE,IAAA,qBAAOC,oBAAcP,SAAAA,EAAW;AAAEC,QAAAA,OAAAA;AAASC,QAAAA,KAAAA;AAAOC,QAAAA;AAAa,KAAA,CAAA;AACnE;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
+
var InteractiveComponent = require('./InteractiveComponent.js');
|
|
4
5
|
var panelLayout = require('./panelLayout.js');
|
|
5
6
|
var UnresolvedComponent = require('./UnresolvedComponent.js');
|
|
6
7
|
|
|
@@ -31,8 +32,9 @@ var UnresolvedComponent = require('./UnresolvedComponent.js');
|
|
|
31
32
|
},
|
|
32
33
|
renderExternalComponent (element, slots) {
|
|
33
34
|
const Component = registry[element.componentName] ?? UnresolvedComponent.UnresolvedComponent;
|
|
34
|
-
return /*#__PURE__*/ react.createElement(
|
|
35
|
+
return /*#__PURE__*/ react.createElement(InteractiveComponent.InteractiveComponent, {
|
|
35
36
|
key: element.id,
|
|
37
|
+
component: Component,
|
|
36
38
|
element,
|
|
37
39
|
slots
|
|
38
40
|
});
|
|
@@ -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(
|
|
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,oBAAc,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,oBAAc,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,uCAAAA;AACrD,YAAA,qBAAOT,oBAAcU,yCAAAA,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,sBAAAA,CAAWrB,OAAAA,EAASA,QAAQe,QAAQ,CAACI,MAAM,EAAEA,KAAAA,CAAAA;gBAC3D,OAAOC,KAAAA,iBACDlB,oBAAc,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,oBACH,KAAA,EACA;AAAEC,gBAAAA,GAAAA,EAAKH,QAAQI,EAAE;AAAE,gBAAA,eAAA,EAAiBJ,QAAQI,EAAE;gBAAE,iBAAA,EAAmB,OAAA;AAASgB,gBAAAA,KAAAA,EAAOE,sBAAAA,CAAWtB,OAAAA;aAAS,EACvGgB,MAAAA,CAAAA;AACR,QAAA;AACJ,KAAA;AACJ;;"}
|
|
@@ -2,6 +2,7 @@ import { jsx, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { renderElement } from '@cratis/scene.engine';
|
|
4
4
|
import { createReactRenderer } from './renderer/createReactRenderer.js';
|
|
5
|
+
import './interactions/useInteractions.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SceneElementView.js","sources":["../../SceneElementView.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 { useMemo } from 'react';\nimport { renderElement, BindingResolver } from '@cratis/scene.engine';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry, createReactRenderer } from './renderer';\n\nexport interface SceneElementViewProps {\n element: SceneElement;\n registry: ComponentRegistry;\n resolveBinding: BindingResolver;\n}\n\n/**\n * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's\n * preview surface and Stage's shipped web bundle both consume unmodified.\n */\nexport function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {\n const renderer = useMemo(() => createReactRenderer(registry), [registry]);\n return <>{renderElement(element, renderer, resolveBinding)}</>;\n}\n"],"names":["SceneElementView","element","registry","resolveBinding","renderer","useMemo","createReactRenderer","_jsx","_Fragment","renderElement"],"mappings":"
|
|
1
|
+
{"version":3,"file":"SceneElementView.js","sources":["../../SceneElementView.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 { useMemo } from 'react';\nimport { renderElement, BindingResolver } from '@cratis/scene.engine';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry, createReactRenderer } from './renderer';\n\nexport interface SceneElementViewProps {\n element: SceneElement;\n registry: ComponentRegistry;\n resolveBinding: BindingResolver;\n}\n\n/**\n * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's\n * preview surface and Stage's shipped web bundle both consume unmodified.\n */\nexport function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {\n const renderer = useMemo(() => createReactRenderer(registry), [registry]);\n return <>{renderElement(element, renderer, resolveBinding)}</>;\n}\n"],"names":["SceneElementView","element","registry","resolveBinding","renderer","useMemo","createReactRenderer","_jsx","_Fragment","renderElement"],"mappings":";;;;;;AAcA;;;IAIO,SAASA,gBAAAA,CAAiB,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAyB,EAAA;AACzF,IAAA,MAAMC,QAAAA,GAAWC,OAAAA,CAAQ,IAAMC,mBAAAA,CAAoBJ,QAAAA,CAAAA,EAAW;AAACA,QAAAA;AAAS,KAAA,CAAA;IACxE,qBAAOK,GAAA,CAAAC,QAAA,EAAA;AAAGC,QAAAA,QAAAA,EAAAA,aAAAA,CAAcR,SAASG,QAAAA,EAAUD,cAAAA;;AAC/C;;"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { RegisteredComponentProps } from '../renderer';
|
|
2
|
-
export declare function CoreButton({ element, slots }: RegisteredComponentProps): import("react").JSX.Element;
|
|
2
|
+
export declare function CoreButton({ element, slots, interactions }: RegisteredComponentProps): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=CoreButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoreButton.d.ts","sourceRoot":"","sources":["../../../core/CoreButton.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAKvD,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,wBAAwB,+
|
|
1
|
+
{"version":3,"file":"CoreButton.d.ts","sourceRoot":"","sources":["../../../core/CoreButton.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAKvD,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,wBAAwB,+BAQpF"}
|
|
@@ -4,11 +4,12 @@ import { jsxs } from 'react/jsx-runtime';
|
|
|
4
4
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
5
|
/**
|
|
6
6
|
* The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.
|
|
7
|
-
*/ function CoreButton({ element, slots }) {
|
|
7
|
+
*/ function CoreButton({ element, slots, interactions }) {
|
|
8
8
|
const label = typeof element.properties.label === 'string' ? element.properties.label : '';
|
|
9
9
|
return /*#__PURE__*/ jsxs("button", {
|
|
10
10
|
type: "button",
|
|
11
11
|
"data-scene-id": element.id,
|
|
12
|
+
...interactions,
|
|
12
13
|
children: [
|
|
13
14
|
label,
|
|
14
15
|
slots.content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoreButton.js","sources":["../../../core/CoreButton.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 { RegisteredComponentProps } from '../renderer';\n\n/**\n * The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.\n */\nexport function CoreButton({ element, slots }: RegisteredComponentProps) {\n const label = typeof element.properties.label === 'string' ? element.properties.label : '';\n return (\n <button type=\"button\" data-scene-id={element.id}>\n {label}\n {slots.content}\n </button>\n );\n}\n"],"names":["CoreButton","element","slots","label","properties","_jsxs","button","type","data-scene-id","id","content"],"mappings":";;AAAA;AACA;AAIA;;
|
|
1
|
+
{"version":3,"file":"CoreButton.js","sources":["../../../core/CoreButton.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 { RegisteredComponentProps } from '../renderer';\n\n/**\n * The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.\n */\nexport function CoreButton({ element, slots, interactions }: RegisteredComponentProps) {\n const label = typeof element.properties.label === 'string' ? element.properties.label : '';\n return (\n <button type=\"button\" data-scene-id={element.id} {...interactions}>\n {label}\n {slots.content}\n </button>\n );\n}\n"],"names":["CoreButton","element","slots","interactions","label","properties","_jsxs","button","type","data-scene-id","id","content"],"mappings":";;AAAA;AACA;AAIA;;IAGO,SAASA,UAAAA,CAAW,EAAEC,OAAO,EAAEC,KAAK,EAAEC,YAAY,EAA4B,EAAA;AACjF,IAAA,MAAMC,KAAAA,GAAQ,OAAOH,OAAAA,CAAQI,UAAU,CAACD,KAAK,KAAK,QAAA,GAAWH,OAAAA,CAAQI,UAAU,CAACD,KAAK,GAAG,EAAA;AACxF,IAAA,qBACIE,IAAA,CAACC,QAAAA,EAAAA;QAAOC,IAAAA,EAAK,QAAA;AAASC,QAAAA,eAAAA,EAAeR,QAAQS,EAAE;AAAG,QAAA,GAAGP,YAAY;;AAC5DC,YAAAA,KAAAA;AACAF,YAAAA,KAAAA,CAAMS;;;AAGnB;;"}
|
package/dist/esm/for_useInteractions/when_clicking_an_element_that_executes_a_command.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when_clicking_an_element_that_executes_a_command.d.ts","sourceRoot":"","sources":["../../../for_useInteractions/when_clicking_an_element_that_executes_a_command.tsx"],"names":[],"mappings":""}
|