@cratis/scene.react 4.29.0 → 4.31.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/CoreToolbar.tsx +37 -6
- package/core/coreComponentMetadata.ts +1 -0
- package/core/coreComponents.ts +2 -0
- package/core/coreDescriptors.ts +13 -0
- package/core/corePackage.ts +1 -0
- package/dist/cjs/core/CoreToolbar.js +44 -4
- package/dist/cjs/core/CoreToolbar.js.map +1 -1
- package/dist/cjs/core/coreComponentMetadata.js +6 -0
- package/dist/cjs/core/coreComponentMetadata.js.map +1 -1
- package/dist/cjs/core/coreComponents.js +3 -1
- package/dist/cjs/core/coreComponents.js.map +1 -1
- package/dist/cjs/core/coreDescriptors.js +27 -0
- package/dist/cjs/core/coreDescriptors.js.map +1 -1
- package/dist/cjs/core/corePackage.js +2 -1
- package/dist/cjs/core/corePackage.js.map +1 -1
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationHost.js +10 -5
- package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
- package/dist/cjs/navigation/useSceneHistoryNavigation.js +3 -1
- package/dist/cjs/navigation/useSceneHistoryNavigation.js.map +1 -1
- package/dist/cjs/slices/CoreSliceDialog.js +21 -0
- package/dist/cjs/slices/CoreSliceDialog.js.map +1 -0
- package/dist/cjs/slices/SliceCommandFieldsContext.js +22 -0
- package/dist/cjs/slices/SliceCommandFieldsContext.js.map +1 -0
- package/dist/cjs/slices/SliceDialogView.js +262 -0
- package/dist/cjs/slices/SliceDialogView.js.map +1 -0
- package/dist/cjs/slices/SliceInvokerContext.js +40 -0
- package/dist/cjs/slices/SliceInvokerContext.js.map +1 -0
- package/dist/cjs/slices/createAnnouncingInvoker.js +27 -0
- package/dist/cjs/slices/createAnnouncingInvoker.js.map +1 -0
- package/dist/cjs/slices/createDryRunInvoker.js +38 -0
- package/dist/cjs/slices/createDryRunInvoker.js.map +1 -0
- package/dist/cjs/slices/sliceDialogDestination.js +41 -0
- package/dist/cjs/slices/sliceDialogDestination.js.map +1 -0
- package/dist/esm/core/CoreToolbar.d.ts.map +1 -1
- package/dist/esm/core/CoreToolbar.js +47 -7
- package/dist/esm/core/CoreToolbar.js.map +1 -1
- package/dist/esm/core/coreComponentMetadata.d.ts.map +1 -1
- package/dist/esm/core/coreComponentMetadata.js +6 -0
- package/dist/esm/core/coreComponentMetadata.js.map +1 -1
- package/dist/esm/core/coreComponents.d.ts.map +1 -1
- package/dist/esm/core/coreComponents.js +3 -1
- package/dist/esm/core/coreComponents.js.map +1 -1
- package/dist/esm/core/coreDescriptors.d.ts.map +1 -1
- package/dist/esm/core/coreDescriptors.js +27 -0
- package/dist/esm/core/coreDescriptors.js.map +1 -1
- package/dist/esm/core/corePackage.d.ts.map +1 -1
- package/dist/esm/core/corePackage.js +2 -1
- package/dist/esm/core/corePackage.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/navigation/SceneNavigationContext.d.ts +1 -0
- package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -1
- package/dist/esm/navigation/SceneNavigationContext.js.map +1 -1
- package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
- package/dist/esm/navigation/SceneNavigationHost.js +10 -5
- package/dist/esm/navigation/SceneNavigationHost.js.map +1 -1
- package/dist/esm/navigation/SceneNavigationState.d.ts +1 -0
- package/dist/esm/navigation/SceneNavigationState.d.ts.map +1 -1
- package/dist/esm/navigation/useSceneHistoryNavigation.d.ts.map +1 -1
- package/dist/esm/navigation/useSceneHistoryNavigation.js +3 -1
- package/dist/esm/navigation/useSceneHistoryNavigation.js.map +1 -1
- package/dist/esm/slices/CoreSliceDialog.d.ts +3 -0
- package/dist/esm/slices/CoreSliceDialog.d.ts.map +1 -0
- package/dist/esm/slices/CoreSliceDialog.js +19 -0
- package/dist/esm/slices/CoreSliceDialog.js.map +1 -0
- package/dist/esm/slices/InvocationResult.d.ts +10 -0
- package/dist/esm/slices/InvocationResult.d.ts.map +1 -0
- package/dist/esm/slices/InvocationResult.js +2 -0
- package/dist/esm/slices/InvocationResult.js.map +1 -0
- package/dist/esm/slices/InvocationValidationError.d.ts +5 -0
- package/dist/esm/slices/InvocationValidationError.d.ts.map +1 -0
- package/dist/esm/slices/InvocationValidationError.js +2 -0
- package/dist/esm/slices/InvocationValidationError.js.map +1 -0
- package/dist/esm/slices/SliceCommandField.d.ts +8 -0
- package/dist/esm/slices/SliceCommandField.d.ts.map +1 -0
- package/dist/esm/slices/SliceCommandField.js +2 -0
- package/dist/esm/slices/SliceCommandField.js.map +1 -0
- package/dist/esm/slices/SliceCommandFieldsContext.d.ts +10 -0
- package/dist/esm/slices/SliceCommandFieldsContext.d.ts.map +1 -0
- package/dist/esm/slices/SliceCommandFieldsContext.js +19 -0
- package/dist/esm/slices/SliceCommandFieldsContext.js.map +1 -0
- package/dist/esm/slices/SliceDialogView.d.ts +12 -0
- package/dist/esm/slices/SliceDialogView.d.ts.map +1 -0
- package/dist/esm/slices/SliceDialogView.js +258 -0
- package/dist/esm/slices/SliceDialogView.js.map +1 -0
- package/dist/esm/slices/SliceInvoker.d.ts +5 -0
- package/dist/esm/slices/SliceInvoker.d.ts.map +1 -0
- package/dist/esm/slices/SliceInvoker.js +2 -0
- package/dist/esm/slices/SliceInvoker.js.map +1 -0
- package/dist/esm/slices/SliceInvokerContext.d.ts +11 -0
- package/dist/esm/slices/SliceInvokerContext.d.ts.map +1 -0
- package/dist/esm/slices/SliceInvokerContext.js +36 -0
- package/dist/esm/slices/SliceInvokerContext.js.map +1 -0
- package/dist/esm/slices/createAnnouncingInvoker.d.ts +3 -0
- package/dist/esm/slices/createAnnouncingInvoker.d.ts.map +1 -0
- package/dist/esm/slices/createAnnouncingInvoker.js +25 -0
- package/dist/esm/slices/createAnnouncingInvoker.js.map +1 -0
- package/dist/esm/slices/createDryRunInvoker.d.ts +15 -0
- package/dist/esm/slices/createDryRunInvoker.d.ts.map +1 -0
- package/dist/esm/slices/createDryRunInvoker.js +36 -0
- package/dist/esm/slices/createDryRunInvoker.js.map +1 -0
- package/dist/esm/slices/index.d.ts +12 -0
- package/dist/esm/slices/index.d.ts.map +1 -0
- package/dist/esm/slices/index.js +12 -0
- package/dist/esm/slices/index.js.map +1 -0
- package/dist/esm/slices/sliceDialogDestination.d.ts +10 -0
- package/dist/esm/slices/sliceDialogDestination.d.ts.map +1 -0
- package/dist/esm/slices/sliceDialogDestination.js +38 -0
- package/dist/esm/slices/sliceDialogDestination.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/index.ts +1 -0
- package/navigation/SceneNavigationContext.tsx +3 -0
- package/navigation/SceneNavigationHost.tsx +8 -3
- package/navigation/SceneNavigationState.ts +3 -0
- package/navigation/useSceneHistoryNavigation.ts +2 -1
- package/package.json +5 -5
- package/slices/CoreSliceDialog.tsx +14 -0
- package/slices/InvocationResult.ts +27 -0
- package/slices/InvocationValidationError.ts +13 -0
- package/slices/SliceCommandField.ts +22 -0
- package/slices/SliceCommandFieldsContext.tsx +28 -0
- package/slices/SliceDialogView.tsx +187 -0
- package/slices/SliceInvoker.ts +19 -0
- package/slices/SliceInvokerContext.tsx +47 -0
- package/slices/createAnnouncingInvoker.ts +17 -0
- package/slices/createDryRunInvoker.ts +43 -0
- package/slices/index.ts +14 -0
- package/slices/sliceDialogDestination.ts +40 -0
|
@@ -11,12 +11,15 @@ var SceneOutletContext = require('./SceneOutletContext.js');
|
|
|
11
11
|
var useSceneHistoryNavigation = require('./useSceneHistoryNavigation.js');
|
|
12
12
|
var SceneNavigationModelContext = require('./region/SceneNavigationModelContext.js');
|
|
13
13
|
var navigationDestinations = require('./region/navigationDestinations.js');
|
|
14
|
+
var SliceDialogView = require('../slices/SliceDialogView.js');
|
|
15
|
+
var sliceDialogDestination = require('../slices/sliceDialogDestination.js');
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Executes Scene destinations against an in-memory rendered hierarchy: a destination into a nested outlet -
|
|
17
19
|
* one a screen declares with `core:outlet` - places its screen there and keeps the screens above it, to any
|
|
18
20
|
* depth; any other screen route replaces the primary region,
|
|
19
|
-
* dialog destinations render dialog content over the screen that opened them
|
|
21
|
+
* dialog destinations render dialog content over the screen that opened them - a toolbar slice button's dialog falls back to
|
|
22
|
+
* the built-in task dialog (`SliceDialogView`) when `dialogs` has no template of that name -, and external destinations
|
|
20
23
|
* only report their resolved action. Every navigation is a history entry.
|
|
21
24
|
*/ function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations: authoredDestinations, routes, navigationModel, frame }) {
|
|
22
25
|
const destinations = react.useMemo(()=>authoredDestinations ?? (navigationModel ? navigationDestinations.navigationDestinations(navigationModel.structure, navigationModel.navigation, navigationModel.screenFor) : undefined), [
|
|
@@ -42,6 +45,8 @@ var navigationDestinations = require('./region/navigationDestinations.js');
|
|
|
42
45
|
]);
|
|
43
46
|
const screen = screens[value.primaryScreen];
|
|
44
47
|
const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;
|
|
48
|
+
const sliceDialog = !dialog && value.currentDialog && sliceDialogDestination.sliceDialogParameters(value.currentDialogParameters) !== undefined;
|
|
49
|
+
const dialogName = sliceDialogDestination.sliceDialogParameters(value.currentDialogParameters)?.title ?? value.currentDialog;
|
|
45
50
|
const outlet = /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
46
51
|
"data-scene-outlet": value.primaryScreen === value.currentScreen ? value.currentOutlet ?? 'primary' : 'primary',
|
|
47
52
|
"data-scene-url": value.currentUrl,
|
|
@@ -59,18 +64,18 @@ var navigationDestinations = require('./region/navigationDestinations.js');
|
|
|
59
64
|
value: outletContext,
|
|
60
65
|
children: [
|
|
61
66
|
frame ? frame(outlet) : outlet,
|
|
62
|
-
dialog && /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
67
|
+
(dialog || sliceDialog) && /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
63
68
|
role: "dialog",
|
|
64
69
|
"aria-modal": "true",
|
|
65
|
-
"aria-label":
|
|
70
|
+
"aria-label": dialogName,
|
|
66
71
|
"data-scene-dialog": value.currentDialog,
|
|
67
|
-
children: /*#__PURE__*/ jsxRuntime.jsx(SceneElementView.SceneElementView, {
|
|
72
|
+
children: dialog ? /*#__PURE__*/ jsxRuntime.jsx(SceneElementView.SceneElementView, {
|
|
68
73
|
element: dialog,
|
|
69
74
|
registry: registry,
|
|
70
75
|
dataContext: bindingScope.dataContext,
|
|
71
76
|
queryResults: bindingScope.queryResults,
|
|
72
77
|
componentOutputs: bindingScope.componentOutputs
|
|
73
|
-
})
|
|
78
|
+
}) : /*#__PURE__*/ jsxRuntime.jsx(SliceDialogView.SliceDialogView, {})
|
|
74
79
|
})
|
|
75
80
|
]
|
|
76
81
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SceneNavigationHost.js","sources":["../../../navigation/SceneNavigationHost.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, useMemo, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationReference, SceneElement } from '@cratis/scene.model';\nimport { SceneElementView } from '../SceneElementView';\nimport { ComponentRegistry } from '../renderer';\nimport { createMemorySceneHistory } from './createMemorySceneHistory';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationProvider } from './SceneNavigationContext';\nimport { SceneRoute } from './SceneRoute';\nimport { sceneRoutesFrom } from './sceneRoutesFrom';\nimport { collectOutletOwners } from './collectOutletOwners';\nimport { SceneOutletContext } from './SceneOutletContext';\nimport { useSceneHistoryNavigation } from './useSceneHistoryNavigation';\nimport { SceneNavigationModel } from './region/SceneNavigationModel';\nimport { SceneNavigationModelProvider } from './region/SceneNavigationModelContext';\nimport { navigationDestinations } from './region/navigationDestinations';\n\nexport interface SceneNavigationHostProps {\n screens: Record<string, SceneElement>;\n dialogs?: Record<string, SceneElement>;\n initialScreen: string;\n registry: ComponentRegistry;\n bindingScope?: BindingScope;\n\n /**\n * Where entries are recorded. `createBrowserSceneHistory()` drives the address bar, so deep links,\n * refresh and back/forward work; the default is an in-memory history for embedded hosts.\n */\n history?: SceneHistory;\n\n /** The destinations the application navigates to, so their route overrides can be deep linked. */\n destinations?: DestinationReference[];\n\n /**\n * The application's structure and navigation. Navigation regions rendered inside the host (`NavigationRegionView`) read it,\n * and - when `destinations` is not given - the destinations of every navigation item become the deep-linkable ones.\n */\n navigationModel?: SceneNavigationModel;\n\n /**\n * Wraps the primary outlet in the application's chrome - a topbar, a sidebar, a toolbar. The chrome is part of the host,\n * so it stays mounted while the screens inside it change and navigation regions keep their state.\n */\n frame?(content: ReactNode): ReactNode;\n\n /** An explicit route table, used instead of the one derived from `screens` and `destinations`. */\n routes?: SceneRoute[];\n}\n\n/**\n * Executes Scene destinations against an in-memory rendered hierarchy: a destination into a nested outlet -\n * one a screen declares with `core:outlet` - places its screen there and keeps the screens above it, to any\n * depth; any other screen route replaces the primary region,\n * dialog destinations render dialog content over the screen that opened them
|
|
1
|
+
{"version":3,"file":"SceneNavigationHost.js","sources":["../../../navigation/SceneNavigationHost.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, useMemo, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationReference, SceneElement } from '@cratis/scene.model';\nimport { SceneElementView } from '../SceneElementView';\nimport { ComponentRegistry } from '../renderer';\nimport { createMemorySceneHistory } from './createMemorySceneHistory';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationProvider } from './SceneNavigationContext';\nimport { SceneRoute } from './SceneRoute';\nimport { sceneRoutesFrom } from './sceneRoutesFrom';\nimport { collectOutletOwners } from './collectOutletOwners';\nimport { SceneOutletContext } from './SceneOutletContext';\nimport { useSceneHistoryNavigation } from './useSceneHistoryNavigation';\nimport { SceneNavigationModel } from './region/SceneNavigationModel';\nimport { SceneNavigationModelProvider } from './region/SceneNavigationModelContext';\nimport { navigationDestinations } from './region/navigationDestinations';\nimport { SliceDialogView } from '../slices/SliceDialogView';\nimport { sliceDialogParameters } from '../slices/sliceDialogDestination';\n\nexport interface SceneNavigationHostProps {\n screens: Record<string, SceneElement>;\n dialogs?: Record<string, SceneElement>;\n initialScreen: string;\n registry: ComponentRegistry;\n bindingScope?: BindingScope;\n\n /**\n * Where entries are recorded. `createBrowserSceneHistory()` drives the address bar, so deep links,\n * refresh and back/forward work; the default is an in-memory history for embedded hosts.\n */\n history?: SceneHistory;\n\n /** The destinations the application navigates to, so their route overrides can be deep linked. */\n destinations?: DestinationReference[];\n\n /**\n * The application's structure and navigation. Navigation regions rendered inside the host (`NavigationRegionView`) read it,\n * and - when `destinations` is not given - the destinations of every navigation item become the deep-linkable ones.\n */\n navigationModel?: SceneNavigationModel;\n\n /**\n * Wraps the primary outlet in the application's chrome - a topbar, a sidebar, a toolbar. The chrome is part of the host,\n * so it stays mounted while the screens inside it change and navigation regions keep their state.\n */\n frame?(content: ReactNode): ReactNode;\n\n /** An explicit route table, used instead of the one derived from `screens` and `destinations`. */\n routes?: SceneRoute[];\n}\n\n/**\n * Executes Scene destinations against an in-memory rendered hierarchy: a destination into a nested outlet -\n * one a screen declares with `core:outlet` - places its screen there and keeps the screens above it, to any\n * depth; any other screen route replaces the primary region,\n * dialog destinations render dialog content over the screen that opened them - a toolbar slice button's dialog falls back to\n * the built-in task dialog (`SliceDialogView`) when `dialogs` has no template of that name -, and external destinations\n * only report their resolved action. Every navigation is a history entry.\n */\nexport function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations: authoredDestinations, routes, navigationModel, frame }: SceneNavigationHostProps) {\n const destinations = useMemo(\n () => authoredDestinations ?? (navigationModel ? navigationDestinations(navigationModel.structure, navigationModel.navigation, navigationModel.screenFor) : undefined),\n [authoredDestinations, navigationModel]);\n const [memoryHistory] = useState(() => createMemorySceneHistory());\n const routeTable = useMemo(() => routes ?? sceneRoutesFrom(Object.keys(screens), destinations), [destinations, routes, screens]);\n const outletOwners = useMemo(() => collectOutletOwners(screens), [screens]);\n const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope, outletOwners, destinations);\n const outletContext = useMemo(() => ({ screens, registry }), [registry, screens]);\n\n const screen = screens[value.primaryScreen];\n const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;\n const sliceDialog = !dialog && value.currentDialog && sliceDialogParameters(value.currentDialogParameters) !== undefined;\n const dialogName = sliceDialogParameters(value.currentDialogParameters)?.title ?? value.currentDialog;\n\n const outlet = <div data-scene-outlet={value.primaryScreen === value.currentScreen ? value.currentOutlet ?? 'primary' : 'primary'} data-scene-url={value.currentUrl}>\n {screen && <SceneElementView element={screen} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />}\n </div>;\n const page = <SceneNavigationProvider value={value}><SceneOutletContext.Provider value={outletContext}>\n {frame ? frame(outlet) : outlet}\n {(dialog || sliceDialog) && <div role='dialog' aria-modal='true' aria-label={dialogName} data-scene-dialog={value.currentDialog}>\n {dialog ? <SceneElementView element={dialog} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} /> : <SliceDialogView />}\n </div>}\n </SceneOutletContext.Provider></SceneNavigationProvider>;\n\n return navigationModel ? <SceneNavigationModelProvider model={navigationModel}>{page}</SceneNavigationModelProvider> : page;\n}\n"],"names":["SceneNavigationHost","screens","dialogs","initialScreen","registry","bindingScope","history","destinations","authoredDestinations","routes","navigationModel","frame","useMemo","navigationDestinations","structure","navigation","screenFor","undefined","memoryHistory","useState","createMemorySceneHistory","routeTable","sceneRoutesFrom","Object","keys","outletOwners","collectOutletOwners","value","useSceneHistoryNavigation","outletContext","screen","primaryScreen","dialog","currentDialog","sliceDialog","sliceDialogParameters","currentDialogParameters","dialogName","title","outlet","_jsx","div","data-scene-outlet","currentScreen","currentOutlet","data-scene-url","currentUrl","SceneElementView","element","dataContext","queryResults","componentOutputs","page","SceneNavigationProvider","_jsxs","SceneOutletContext","Provider","role","aria-modal","aria-label","data-scene-dialog","SliceDialogView","SceneNavigationModelProvider","model"],"mappings":"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"}
|
|
@@ -67,7 +67,8 @@ var resolveDestination = require('./resolveDestination.js');
|
|
|
67
67
|
if (resolution.kind === scene_model.DestinationKind.Dialog) {
|
|
68
68
|
if (resolution.dialog) go({
|
|
69
69
|
...state,
|
|
70
|
-
dialog: resolution.dialog
|
|
70
|
+
dialog: resolution.dialog,
|
|
71
|
+
dialogParameters: resolution.parameters
|
|
71
72
|
});
|
|
72
73
|
} else if (resolution.kind !== scene_model.DestinationKind.External) {
|
|
73
74
|
const screen = destination.screen ?? destination.slice ?? state.screen;
|
|
@@ -106,6 +107,7 @@ var resolveDestination = require('./resolveDestination.js');
|
|
|
106
107
|
currentUrl: state.url,
|
|
107
108
|
currentOutlet: state.outlet,
|
|
108
109
|
currentDialog: state.dialog,
|
|
110
|
+
currentDialogParameters: state.dialogParameters,
|
|
109
111
|
currentParameters: state.parameters,
|
|
110
112
|
dialogResult,
|
|
111
113
|
unresolvedUrl,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneHistoryNavigation.js","sources":["../../../navigation/useSceneHistoryNavigation.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, useRef, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { DialogResult } from './DialogResult';\nimport { matchSceneRoute } from './matchSceneRoute';\nimport { placeInOutlet } from './placeInOutlet';\nimport { resolveDestination } from './resolveDestination';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationContextValue } from './SceneNavigationContext';\nimport { SceneNavigationState } from './SceneNavigationState';\nimport { SceneRoute } from './SceneRoute';\n\n/**\n * Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,\n * so back and forward step through them, a refresh restores the stored entry, and a deep link is matched\n * against the route table. Closing a dialog goes back to the entry that opened it.\n */\nexport function useSceneHistoryNavigation(\n history: SceneHistory,\n routes: SceneRoute[],\n initialScreen: string,\n bindingScope: BindingScope,\n outletOwners: Record<string, string> = {},\n destinations: DestinationReference[] = [],\n): SceneNavigationContextValue {\n const fromLocation = useCallback((url: string | undefined): SceneNavigationState | undefined => {\n const match = url ? matchSceneRoute(url, routes) : undefined;\n if (!match) return undefined;\n const start: SceneNavigationState = { screen: initialScreen, parameters: {} };\n const placement = placeInOutlet(start, match.route.screen, match.route.outlet, outletOwners, destinations);\n return { screen: match.route.screen, url, outlet: match.route.outlet, parameters: match.parameters, ...placement };\n }, [destinations, initialScreen, outletOwners, routes]);\n const fallback = useMemo<SceneNavigationState>(() => ({ screen: initialScreen, url: initialScreen, parameters: {} }), [initialScreen]);\n\n const [unresolvedUrl] = useState(() => {\n const url = history.location();\n return !history.state() && url && !fromLocation(url) ? url : undefined;\n });\n const [state, setState] = useState<SceneNavigationState>(() => history.state() ?? fromLocation(history.location()) ?? fallback);\n const [dialogResult, setDialogResult] = useState<DialogResult>();\n\n // Back and forward restore the stored entry, or match a URL the host never wrote against the current routes.\n const restore = useRef((url: string | undefined, stored: SceneNavigationState | undefined) => stored ?? fromLocation(url) ?? fallback);\n restore.current = (url, stored) => stored ?? fromLocation(url) ?? fallback;\n const initialState = useRef(state);\n\n // The current entry is written once, when a history is attached; later entries are written by navigate.\n useEffect(() => {\n history.replace(initialState.current);\n return history.listen((url, stored) => setState(restore.current(url, stored)));\n }, [history]);\n\n const go = useCallback((next: SceneNavigationState) => {\n history.push(next);\n setState(next);\n }, [history]);\n\n const navigate = useCallback((destination: DestinationReference) => {\n const resolution = resolveDestination(destination, bindingScope);\n if (resolution.kind === DestinationKind.Dialog) {\n if (resolution.dialog) go({ ...state, dialog: resolution.dialog });\n } else if (resolution.kind !== DestinationKind.External) {\n const screen = destination.screen ?? destination.slice ?? state.screen;\n const placement = placeInOutlet(state, screen, resolution.outlet, outletOwners, destinations);\n go({ screen, url: resolution.url, outlet: resolution.outlet, parameters: resolution.parameters, ...placement });\n }\n\n return resolution;\n }, [bindingScope, destinations, go, outletOwners, state]);\n\n const closeDialog = useCallback((result?: unknown) => {\n if (!state.dialog) return;\n setDialogResult({ dialog: state.dialog, result });\n history.back();\n }, [history, state.dialog]);\n\n return useMemo(() => ({\n currentScreen: state.screen,\n primaryScreen: state.primary ?? state.screen,\n outlets: state.outlets ?? {},\n currentUrl: state.url,\n currentOutlet: state.outlet,\n currentDialog: state.dialog,\n currentParameters: state.parameters,\n dialogResult,\n unresolvedUrl,\n bindingScope,\n navigate,\n closeDialog,\n back: () => history.back(),\n }), [bindingScope, closeDialog, dialogResult, history, navigate, state, unresolvedUrl]);\n}\n"],"names":["useSceneHistoryNavigation","history","routes","initialScreen","bindingScope","outletOwners","destinations","fromLocation","useCallback","url","match","matchSceneRoute","undefined","start","screen","placement","placeInOutlet","route","outlet","parameters","fallback","useMemo","unresolvedUrl","useState","location","state","setState","dialogResult","setDialogResult","restore","useRef","stored","current","initialState","useEffect","replace","listen","go","next","push","navigate","destination","resolution","resolveDestination","kind","DestinationKind","Dialog","dialog","External","slice","closeDialog","result","back","currentScreen","primaryScreen","primary","outlets","currentUrl","currentOutlet","currentDialog","currentParameters"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useSceneHistoryNavigation.js","sources":["../../../navigation/useSceneHistoryNavigation.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, useRef, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { DialogResult } from './DialogResult';\nimport { matchSceneRoute } from './matchSceneRoute';\nimport { placeInOutlet } from './placeInOutlet';\nimport { resolveDestination } from './resolveDestination';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationContextValue } from './SceneNavigationContext';\nimport { SceneNavigationState } from './SceneNavigationState';\nimport { SceneRoute } from './SceneRoute';\n\n/**\n * Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,\n * so back and forward step through them, a refresh restores the stored entry, and a deep link is matched\n * against the route table. Closing a dialog goes back to the entry that opened it.\n */\nexport function useSceneHistoryNavigation(\n history: SceneHistory,\n routes: SceneRoute[],\n initialScreen: string,\n bindingScope: BindingScope,\n outletOwners: Record<string, string> = {},\n destinations: DestinationReference[] = [],\n): SceneNavigationContextValue {\n const fromLocation = useCallback((url: string | undefined): SceneNavigationState | undefined => {\n const match = url ? matchSceneRoute(url, routes) : undefined;\n if (!match) return undefined;\n const start: SceneNavigationState = { screen: initialScreen, parameters: {} };\n const placement = placeInOutlet(start, match.route.screen, match.route.outlet, outletOwners, destinations);\n return { screen: match.route.screen, url, outlet: match.route.outlet, parameters: match.parameters, ...placement };\n }, [destinations, initialScreen, outletOwners, routes]);\n const fallback = useMemo<SceneNavigationState>(() => ({ screen: initialScreen, url: initialScreen, parameters: {} }), [initialScreen]);\n\n const [unresolvedUrl] = useState(() => {\n const url = history.location();\n return !history.state() && url && !fromLocation(url) ? url : undefined;\n });\n const [state, setState] = useState<SceneNavigationState>(() => history.state() ?? fromLocation(history.location()) ?? fallback);\n const [dialogResult, setDialogResult] = useState<DialogResult>();\n\n // Back and forward restore the stored entry, or match a URL the host never wrote against the current routes.\n const restore = useRef((url: string | undefined, stored: SceneNavigationState | undefined) => stored ?? fromLocation(url) ?? fallback);\n restore.current = (url, stored) => stored ?? fromLocation(url) ?? fallback;\n const initialState = useRef(state);\n\n // The current entry is written once, when a history is attached; later entries are written by navigate.\n useEffect(() => {\n history.replace(initialState.current);\n return history.listen((url, stored) => setState(restore.current(url, stored)));\n }, [history]);\n\n const go = useCallback((next: SceneNavigationState) => {\n history.push(next);\n setState(next);\n }, [history]);\n\n const navigate = useCallback((destination: DestinationReference) => {\n const resolution = resolveDestination(destination, bindingScope);\n if (resolution.kind === DestinationKind.Dialog) {\n if (resolution.dialog) go({ ...state, dialog: resolution.dialog, dialogParameters: resolution.parameters });\n } else if (resolution.kind !== DestinationKind.External) {\n const screen = destination.screen ?? destination.slice ?? state.screen;\n const placement = placeInOutlet(state, screen, resolution.outlet, outletOwners, destinations);\n go({ screen, url: resolution.url, outlet: resolution.outlet, parameters: resolution.parameters, ...placement });\n }\n\n return resolution;\n }, [bindingScope, destinations, go, outletOwners, state]);\n\n const closeDialog = useCallback((result?: unknown) => {\n if (!state.dialog) return;\n setDialogResult({ dialog: state.dialog, result });\n history.back();\n }, [history, state.dialog]);\n\n return useMemo(() => ({\n currentScreen: state.screen,\n primaryScreen: state.primary ?? state.screen,\n outlets: state.outlets ?? {},\n currentUrl: state.url,\n currentOutlet: state.outlet,\n currentDialog: state.dialog,\n currentDialogParameters: state.dialogParameters,\n currentParameters: state.parameters,\n dialogResult,\n unresolvedUrl,\n bindingScope,\n navigate,\n closeDialog,\n back: () => history.back(),\n }), [bindingScope, closeDialog, dialogResult, history, navigate, state, unresolvedUrl]);\n}\n"],"names":["useSceneHistoryNavigation","history","routes","initialScreen","bindingScope","outletOwners","destinations","fromLocation","useCallback","url","match","matchSceneRoute","undefined","start","screen","placement","placeInOutlet","route","outlet","parameters","fallback","useMemo","unresolvedUrl","useState","location","state","setState","dialogResult","setDialogResult","restore","useRef","stored","current","initialState","useEffect","replace","listen","go","next","push","navigate","destination","resolution","resolveDestination","kind","DestinationKind","Dialog","dialog","dialogParameters","External","slice","closeDialog","result","back","currentScreen","primaryScreen","primary","outlets","currentUrl","currentOutlet","currentDialog","currentDialogParameters","currentParameters"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var SliceDialogView = require('./SliceDialogView.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
|
+
* The `core:sliceDialog` component: the task dialog of a state change slice. Its `slice`, `command` and `title` properties are
|
|
10
|
+
* optional - opened from a toolbar button the dialog's own parameters supply them - so one dialog template serves every slice.
|
|
11
|
+
*/ function CoreSliceDialog({ element }) {
|
|
12
|
+
const text = (name)=>typeof element.properties[name] === 'string' ? element.properties[name] : undefined;
|
|
13
|
+
return /*#__PURE__*/ jsxRuntime.jsx(SliceDialogView.SliceDialogView, {
|
|
14
|
+
slice: text('slice'),
|
|
15
|
+
command: text('command'),
|
|
16
|
+
title: text('title')
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.CoreSliceDialog = CoreSliceDialog;
|
|
21
|
+
//# sourceMappingURL=CoreSliceDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoreSliceDialog.js","sources":["../../../slices/CoreSliceDialog.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';\nimport { SliceDialogView } from './SliceDialogView';\n\n/**\n * The `core:sliceDialog` component: the task dialog of a state change slice. Its `slice`, `command` and `title` properties are\n * optional - opened from a toolbar button the dialog's own parameters supply them - so one dialog template serves every slice.\n */\nexport function CoreSliceDialog({ element }: RegisteredComponentProps) {\n const text = (name: string) => typeof element.properties[name] === 'string' ? element.properties[name] as string : undefined;\n return <SliceDialogView slice={text('slice')} command={text('command')} title={text('title')} />;\n}\n"],"names":["CoreSliceDialog","element","text","name","properties","undefined","_jsx","SliceDialogView","slice","command","title"],"mappings":";;;;;AAAA;AACA;AAKA;;;AAGC,IACM,SAASA,eAAAA,CAAgB,EAAEC,OAAO,EAA4B,EAAA;AACjE,IAAA,MAAMC,IAAAA,GAAO,CAACC,IAAAA,GAAiB,OAAOF,QAAQG,UAAU,CAACD,IAAAA,CAAK,KAAK,QAAA,GAAWF,OAAAA,CAAQG,UAAU,CAACD,KAAK,GAAaE,SAAAA;AACnH,IAAA,qBAAOC,cAAA,CAACC,+BAAAA,EAAAA;AAAgBC,QAAAA,KAAAA,EAAON,IAAAA,CAAK,OAAA,CAAA;AAAUO,QAAAA,OAAAA,EAASP,IAAAA,CAAK,SAAA,CAAA;AAAYQ,QAAAA,KAAAA,EAAOR,IAAAA,CAAK,OAAA;;AACxF;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
const Context = /*#__PURE__*/ react.createContext(undefined);
|
|
7
|
+
/**
|
|
8
|
+
* Supplies the fields of a command to the task dialogs below it. The fields come from the command's metadata - Stage reads it
|
|
9
|
+
* from the generated schemas, Studio from the model - so this package does not know where commands are defined.
|
|
10
|
+
*/ function SliceCommandFieldsProvider({ fieldsFor, children }) {
|
|
11
|
+
return /*#__PURE__*/ jsxRuntime.jsx(Context.Provider, {
|
|
12
|
+
value: fieldsFor,
|
|
13
|
+
children: children
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** The fields source in scope, or undefined when a host supplied none. */ function useSliceCommandFields() {
|
|
17
|
+
return react.useContext(Context);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.SliceCommandFieldsProvider = SliceCommandFieldsProvider;
|
|
21
|
+
exports.useSliceCommandFields = useSliceCommandFields;
|
|
22
|
+
//# sourceMappingURL=SliceCommandFieldsContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SliceCommandFieldsContext.js","sources":["../../../slices/SliceCommandFieldsContext.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 } from 'react';\nimport { SliceCommandField } from './SliceCommandField';\n\n/** Tells a task dialog which fields a command has. */\nexport type SliceCommandFieldsSource = (command: string, sliceId: string) => SliceCommandField[];\n\nconst Context = createContext<SliceCommandFieldsSource | undefined>(undefined);\n\nexport interface SliceCommandFieldsProviderProps {\n fieldsFor: SliceCommandFieldsSource;\n children?: ReactNode;\n}\n\n/**\n * Supplies the fields of a command to the task dialogs below it. The fields come from the command's metadata - Stage reads it\n * from the generated schemas, Studio from the model - so this package does not know where commands are defined.\n */\nexport function SliceCommandFieldsProvider({ fieldsFor, children }: SliceCommandFieldsProviderProps) {\n return <Context.Provider value={fieldsFor}>{children}</Context.Provider>;\n}\n\n/** The fields source in scope, or undefined when a host supplied none. */\nexport function useSliceCommandFields(): SliceCommandFieldsSource | undefined {\n return useContext(Context);\n}\n"],"names":["Context","createContext","undefined","SliceCommandFieldsProvider","fieldsFor","children","_jsx","Provider","value","useSliceCommandFields","useContext"],"mappings":";;;;;AASA,MAAMA,wBAAUC,mBAAAA,CAAoDC,SAAAA,CAAAA;AAOpE;;;AAGC,IACM,SAASC,0BAAAA,CAA2B,EAAEC,SAAS,EAAEC,QAAQ,EAAmC,EAAA;IAC/F,qBAAOC,cAAA,CAACN,QAAQO,QAAQ,EAAA;QAACC,KAAAA,EAAOJ,SAAAA;AAAYC,QAAAA,QAAAA,EAAAA;;AAChD;AAEA,2EACO,SAASI,qBAAAA,GAAAA;AACZ,IAAA,OAAOC,gBAAAA,CAAWV,OAAAA,CAAAA;AACtB;;;"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var scene_engine = require('@cratis/scene.engine');
|
|
6
|
+
var SceneNavigationContext = require('../navigation/SceneNavigationContext.js');
|
|
7
|
+
var SceneNavigationModelContext = require('../navigation/region/SceneNavigationModelContext.js');
|
|
8
|
+
var SliceCommandFieldsContext = require('./SliceCommandFieldsContext.js');
|
|
9
|
+
var SliceInvokerContext = require('./SliceInvokerContext.js');
|
|
10
|
+
var sliceDialogDestination = require('./sliceDialogDestination.js');
|
|
11
|
+
|
|
12
|
+
const focusable = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])';
|
|
13
|
+
/** A label for a command property: `customerName` becomes `Customer name`. */ function sliceFieldLabel(field) {
|
|
14
|
+
if (field.label) return field.label;
|
|
15
|
+
const spaced = field.name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim().toLowerCase();
|
|
16
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
17
|
+
}
|
|
18
|
+
function inputTypeOf(field) {
|
|
19
|
+
switch(field.type.toLowerCase()){
|
|
20
|
+
case 'number':
|
|
21
|
+
case 'int':
|
|
22
|
+
case 'integer':
|
|
23
|
+
case 'long':
|
|
24
|
+
case 'double':
|
|
25
|
+
case 'decimal':
|
|
26
|
+
case 'float':
|
|
27
|
+
case 'single':
|
|
28
|
+
case 'int32':
|
|
29
|
+
case 'int64':
|
|
30
|
+
return 'number';
|
|
31
|
+
case 'boolean':
|
|
32
|
+
case 'bool':
|
|
33
|
+
return 'checkbox';
|
|
34
|
+
case 'date':
|
|
35
|
+
case 'dateonly':
|
|
36
|
+
return 'date';
|
|
37
|
+
default:
|
|
38
|
+
return 'text';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** What the form starts with: the fields that carry an initial value, as the strings and booleans the inputs hold. */ function initialValuesOf(fields) {
|
|
42
|
+
const values = {};
|
|
43
|
+
for (const field of fields){
|
|
44
|
+
if (field.initialValue === undefined) continue;
|
|
45
|
+
values[field.name] = typeof field.initialValue === 'boolean' && inputTypeOf(field) === 'checkbox' ? field.initialValue : String(field.initialValue);
|
|
46
|
+
}
|
|
47
|
+
return values;
|
|
48
|
+
}
|
|
49
|
+
/** The payload a form's values make: numbers and booleans as such, an empty number left out. */ function slicePayload(fields, values) {
|
|
50
|
+
const payload = {};
|
|
51
|
+
for (const field of fields){
|
|
52
|
+
const value = values[field.name];
|
|
53
|
+
const type = inputTypeOf(field);
|
|
54
|
+
if (type === 'checkbox') payload[field.name] = value === true;
|
|
55
|
+
else if (type === 'number') {
|
|
56
|
+
if (value !== undefined && value !== '') payload[field.name] = Number(value);
|
|
57
|
+
} else payload[field.name] = value ?? '';
|
|
58
|
+
}
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The task dialog of a toolbar slice button (TB4): a modal card with the button's title, the slice as its subtitle, a labelled
|
|
63
|
+
* field for each command property, and Cancel and a submit button labelled with the title.
|
|
64
|
+
*
|
|
65
|
+
* - The first field takes focus when it opens, Tab stays inside, Escape cancels, and focus returns to the button that opened it.
|
|
66
|
+
* - Submit validates required fields, then calls the `SliceInvoker` in scope - real, dry-run or the announcing default. The
|
|
67
|
+
* problems it reports are shown against their fields (`aria-invalid`, `aria-describedby`) or for the form; success closes the
|
|
68
|
+
* dialog and hands the result to the host (`dialogResult`).
|
|
69
|
+
* - The fields come from the `SliceCommandFieldsProvider`; the subtitle names the slice from the navigation model.
|
|
70
|
+
*
|
|
71
|
+
* Inside a navigation host it is the content of the dialog a toolbar button opens; `core:sliceDialog` registers it so a host can
|
|
72
|
+
* author the dialog template with it.
|
|
73
|
+
*/ function SliceDialogView({ slice: sliceProp, command: commandProp, title: titleProp, onClose }) {
|
|
74
|
+
const navigation = react.useContext(SceneNavigationContext.SceneNavigationContextInternal);
|
|
75
|
+
const opened = sliceDialogDestination.sliceDialogParameters(navigation?.currentDialogParameters);
|
|
76
|
+
const slice = sliceProp ?? opened?.slice ?? '';
|
|
77
|
+
const command = commandProp ?? opened?.command ?? slice;
|
|
78
|
+
const title = titleProp ?? opened?.title ?? command;
|
|
79
|
+
const invoker = SliceInvokerContext.useSliceInvoker();
|
|
80
|
+
const fieldsFor = SliceCommandFieldsContext.useSliceCommandFields();
|
|
81
|
+
const model = SceneNavigationModelContext.useSceneNavigationModel();
|
|
82
|
+
const fields = react.useMemo(()=>fieldsFor?.(command, slice) ?? [], [
|
|
83
|
+
command,
|
|
84
|
+
fieldsFor,
|
|
85
|
+
slice
|
|
86
|
+
]);
|
|
87
|
+
const sliceName = react.useMemo(()=>{
|
|
88
|
+
if (!model) return slice.split('.').pop() ?? slice;
|
|
89
|
+
const index = model.structure instanceof scene_engine.ApplicationStructureIndex ? model.structure : new scene_engine.ApplicationStructureIndex(model.structure);
|
|
90
|
+
return index.findSlice(slice)?.id ?? slice.split('.').pop() ?? slice;
|
|
91
|
+
}, [
|
|
92
|
+
model,
|
|
93
|
+
slice
|
|
94
|
+
]);
|
|
95
|
+
const [values, setValues] = react.useState(()=>initialValuesOf(fields));
|
|
96
|
+
const [errors, setErrors] = react.useState([]);
|
|
97
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
98
|
+
const form = react.useRef(null);
|
|
99
|
+
const identity = react.useId();
|
|
100
|
+
const close = (result)=>onClose ? onClose(result) : navigation?.closeDialog(result);
|
|
101
|
+
// Focus the first field, and give focus back to whatever opened the dialog when it goes.
|
|
102
|
+
react.useEffect(()=>{
|
|
103
|
+
const opener = document.activeElement;
|
|
104
|
+
(form.current?.querySelector('input, select, textarea') ?? form.current?.querySelector('button'))?.focus();
|
|
105
|
+
return ()=>{
|
|
106
|
+
if (opener && opener.isConnected) opener.focus();
|
|
107
|
+
};
|
|
108
|
+
}, []);
|
|
109
|
+
const errorsOf = (name)=>errors.filter((error)=>error.field === name);
|
|
110
|
+
const fieldNames = new Set(fields.map((field)=>field.name));
|
|
111
|
+
const general = errors.filter((error)=>error.field === undefined || !fieldNames.has(error.field));
|
|
112
|
+
const submit = async (event)=>{
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
if (submitting) return;
|
|
115
|
+
const missing = fields.filter((field)=>field.required && (values[field.name] === undefined || values[field.name] === '')).map((field)=>({
|
|
116
|
+
field: field.name,
|
|
117
|
+
message: `${sliceFieldLabel(field)} is required.`
|
|
118
|
+
}));
|
|
119
|
+
setErrors(missing);
|
|
120
|
+
if (missing.length > 0) {
|
|
121
|
+
form.current?.querySelector('[aria-invalid="true"]')?.focus();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
setSubmitting(true);
|
|
125
|
+
try {
|
|
126
|
+
const result = await invoker.invoke(slice, slicePayload(fields, values), command);
|
|
127
|
+
if (result.succeeded) {
|
|
128
|
+
close(result);
|
|
129
|
+
} else {
|
|
130
|
+
const reported = result.validationErrors ?? [];
|
|
131
|
+
setErrors(reported.length > 0 || !result.message ? reported : [
|
|
132
|
+
{
|
|
133
|
+
message: result.message
|
|
134
|
+
}
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
} finally{
|
|
138
|
+
setSubmitting(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const onKeyDown = (event)=>{
|
|
142
|
+
if (event.key === 'Escape') {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
close();
|
|
145
|
+
} else if (event.key === 'Tab') {
|
|
146
|
+
const targets = Array.from(form.current?.querySelectorAll(focusable) ?? []);
|
|
147
|
+
if (targets.length === 0) return;
|
|
148
|
+
const first = targets[0];
|
|
149
|
+
const last = targets[targets.length - 1];
|
|
150
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
last.focus();
|
|
153
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
first.focus();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("form", {
|
|
160
|
+
ref: form,
|
|
161
|
+
className: "scene-slice-dialog",
|
|
162
|
+
"data-scene-slice-dialog": slice,
|
|
163
|
+
"data-scene-command": command,
|
|
164
|
+
noValidate: true,
|
|
165
|
+
onSubmit: submit,
|
|
166
|
+
onKeyDown: onKeyDown,
|
|
167
|
+
children: [
|
|
168
|
+
/*#__PURE__*/ jsxRuntime.jsxs("header", {
|
|
169
|
+
className: "scene-slice-dialog-header",
|
|
170
|
+
children: [
|
|
171
|
+
/*#__PURE__*/ jsxRuntime.jsx("h2", {
|
|
172
|
+
className: "scene-slice-dialog-title",
|
|
173
|
+
children: title
|
|
174
|
+
}),
|
|
175
|
+
/*#__PURE__*/ jsxRuntime.jsxs("p", {
|
|
176
|
+
className: "scene-slice-dialog-subtitle",
|
|
177
|
+
children: [
|
|
178
|
+
"State Change · ",
|
|
179
|
+
sliceName
|
|
180
|
+
]
|
|
181
|
+
})
|
|
182
|
+
]
|
|
183
|
+
}),
|
|
184
|
+
/*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
185
|
+
className: "scene-slice-dialog-fields",
|
|
186
|
+
children: fields.map((field)=>{
|
|
187
|
+
const id = `${identity}-${field.name}`;
|
|
188
|
+
const own = errorsOf(field.name);
|
|
189
|
+
const type = inputTypeOf(field);
|
|
190
|
+
const description = own.length > 0 ? `${id}-error` : undefined;
|
|
191
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("div", {
|
|
192
|
+
className: "scene-slice-dialog-field",
|
|
193
|
+
"data-type": type,
|
|
194
|
+
children: [
|
|
195
|
+
/*#__PURE__*/ jsxRuntime.jsxs("label", {
|
|
196
|
+
htmlFor: id,
|
|
197
|
+
children: [
|
|
198
|
+
sliceFieldLabel(field),
|
|
199
|
+
field.required ? /*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
200
|
+
"aria-hidden": "true",
|
|
201
|
+
children: " *"
|
|
202
|
+
}) : null
|
|
203
|
+
]
|
|
204
|
+
}),
|
|
205
|
+
/*#__PURE__*/ jsxRuntime.jsx("input", {
|
|
206
|
+
id: id,
|
|
207
|
+
name: field.name,
|
|
208
|
+
type: type,
|
|
209
|
+
required: field.required,
|
|
210
|
+
"aria-required": field.required || undefined,
|
|
211
|
+
"aria-invalid": own.length > 0 ? true : undefined,
|
|
212
|
+
"aria-describedby": description,
|
|
213
|
+
...type === 'checkbox' ? {
|
|
214
|
+
checked: values[field.name] === true
|
|
215
|
+
} : {
|
|
216
|
+
value: values[field.name] ?? ''
|
|
217
|
+
},
|
|
218
|
+
onChange: (event)=>setValues({
|
|
219
|
+
...values,
|
|
220
|
+
[field.name]: type === 'checkbox' ? event.target.checked : event.target.value
|
|
221
|
+
})
|
|
222
|
+
}),
|
|
223
|
+
own.length > 0 && /*#__PURE__*/ jsxRuntime.jsx("p", {
|
|
224
|
+
id: description,
|
|
225
|
+
className: "scene-slice-dialog-error",
|
|
226
|
+
role: "alert",
|
|
227
|
+
children: own.map((error)=>error.message).join(' ')
|
|
228
|
+
})
|
|
229
|
+
]
|
|
230
|
+
}, field.name);
|
|
231
|
+
})
|
|
232
|
+
}),
|
|
233
|
+
general.length > 0 && /*#__PURE__*/ jsxRuntime.jsx("p", {
|
|
234
|
+
className: "scene-slice-dialog-error scene-slice-dialog-summary",
|
|
235
|
+
role: "alert",
|
|
236
|
+
children: general.map((error)=>error.message).join(' ')
|
|
237
|
+
}),
|
|
238
|
+
/*#__PURE__*/ jsxRuntime.jsxs("footer", {
|
|
239
|
+
className: "scene-slice-dialog-footer",
|
|
240
|
+
children: [
|
|
241
|
+
/*#__PURE__*/ jsxRuntime.jsx("button", {
|
|
242
|
+
type: "button",
|
|
243
|
+
className: "scene-slice-dialog-cancel",
|
|
244
|
+
onClick: ()=>close(),
|
|
245
|
+
children: "Cancel"
|
|
246
|
+
}),
|
|
247
|
+
/*#__PURE__*/ jsxRuntime.jsx("button", {
|
|
248
|
+
type: "submit",
|
|
249
|
+
className: "scene-slice-dialog-submit",
|
|
250
|
+
disabled: submitting,
|
|
251
|
+
children: title
|
|
252
|
+
})
|
|
253
|
+
]
|
|
254
|
+
})
|
|
255
|
+
]
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
exports.SliceDialogView = SliceDialogView;
|
|
260
|
+
exports.sliceFieldLabel = sliceFieldLabel;
|
|
261
|
+
exports.slicePayload = slicePayload;
|
|
262
|
+
//# sourceMappingURL=SliceDialogView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SliceDialogView.js","sources":["../../../slices/SliceDialogView.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 { FormEvent, KeyboardEvent, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport { ApplicationStructureIndex } from '@cratis/scene.engine';\nimport { SceneNavigationContextInternal } from '../navigation/SceneNavigationContext';\nimport { useSceneNavigationModel } from '../navigation/region/SceneNavigationModelContext';\nimport { InvocationResult } from './InvocationResult';\nimport { InvocationValidationError } from './InvocationValidationError';\nimport { SliceCommandField } from './SliceCommandField';\nimport { useSliceCommandFields } from './SliceCommandFieldsContext';\nimport { useSliceInvoker } from './SliceInvokerContext';\nimport { sliceDialogParameters } from './sliceDialogDestination';\n\nexport interface SliceDialogViewProps {\n /** The qualified id of the slice; the open dialog's `slice` parameter when absent. */\n slice?: string;\n\n /** The command the slice accepts; the open dialog's `command` parameter when absent. */\n command?: string;\n\n /** The title - the toolbar button's - also the label of the submit button; the open dialog's `title` parameter when absent. */\n title?: string;\n\n /** Called when the dialog is cancelled or the slice accepted the command; the host's `closeDialog` when absent. */\n onClose?(result?: InvocationResult): void;\n}\n\nconst focusable = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])';\n\n/** A label for a command property: `customerName` becomes `Customer name`. */\nexport function sliceFieldLabel(field: SliceCommandField): string {\n if (field.label) return field.label;\n const spaced = field.name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim().toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\nfunction inputTypeOf(field: SliceCommandField): string {\n switch (field.type.toLowerCase()) {\n case 'number': case 'int': case 'integer': case 'long': case 'double': case 'decimal': case 'float': case 'single': case 'int32': case 'int64': return 'number';\n case 'boolean': case 'bool': return 'checkbox';\n case 'date': case 'dateonly': return 'date';\n default: return 'text';\n }\n}\n\n/** What the form starts with: the fields that carry an initial value, as the strings and booleans the inputs hold. */\nfunction initialValuesOf(fields: SliceCommandField[]): Record<string, string | boolean> {\n const values: Record<string, string | boolean> = {};\n for (const field of fields) {\n if (field.initialValue === undefined) continue;\n values[field.name] = typeof field.initialValue === 'boolean' && inputTypeOf(field) === 'checkbox' ? field.initialValue : String(field.initialValue);\n }\n\n return values;\n}\n\n/** The payload a form's values make: numbers and booleans as such, an empty number left out. */\nexport function slicePayload(fields: SliceCommandField[], values: Record<string, string | boolean>): Record<string, unknown> {\n const payload: Record<string, unknown> = {};\n for (const field of fields) {\n const value = values[field.name];\n const type = inputTypeOf(field);\n if (type === 'checkbox') payload[field.name] = value === true;\n else if (type === 'number') { if (value !== undefined && value !== '') payload[field.name] = Number(value); }\n else payload[field.name] = value ?? '';\n }\n\n return payload;\n}\n\n/**\n * The task dialog of a toolbar slice button (TB4): a modal card with the button's title, the slice as its subtitle, a labelled\n * field for each command property, and Cancel and a submit button labelled with the title.\n *\n * - The first field takes focus when it opens, Tab stays inside, Escape cancels, and focus returns to the button that opened it.\n * - Submit validates required fields, then calls the `SliceInvoker` in scope - real, dry-run or the announcing default. The\n * problems it reports are shown against their fields (`aria-invalid`, `aria-describedby`) or for the form; success closes the\n * dialog and hands the result to the host (`dialogResult`).\n * - The fields come from the `SliceCommandFieldsProvider`; the subtitle names the slice from the navigation model.\n *\n * Inside a navigation host it is the content of the dialog a toolbar button opens; `core:sliceDialog` registers it so a host can\n * author the dialog template with it.\n */\nexport function SliceDialogView({ slice: sliceProp, command: commandProp, title: titleProp, onClose }: SliceDialogViewProps) {\n const navigation = useContext(SceneNavigationContextInternal);\n const opened = sliceDialogParameters(navigation?.currentDialogParameters);\n const slice = sliceProp ?? opened?.slice ?? '';\n const command = commandProp ?? opened?.command ?? slice;\n const title = titleProp ?? opened?.title ?? command;\n\n const invoker = useSliceInvoker();\n const fieldsFor = useSliceCommandFields();\n const model = useSceneNavigationModel();\n const fields = useMemo(() => fieldsFor?.(command, slice) ?? [], [command, fieldsFor, slice]);\n const sliceName = useMemo(() => {\n if (!model) return slice.split('.').pop() ?? slice;\n const index = model.structure instanceof ApplicationStructureIndex ? model.structure : new ApplicationStructureIndex(model.structure);\n return index.findSlice(slice)?.id ?? slice.split('.').pop() ?? slice;\n }, [model, slice]);\n\n const [values, setValues] = useState<Record<string, string | boolean>>(() => initialValuesOf(fields));\n const [errors, setErrors] = useState<InvocationValidationError[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const form = useRef<HTMLFormElement>(null);\n const identity = useId();\n\n const close = (result?: InvocationResult) => onClose ? onClose(result) : navigation?.closeDialog(result);\n\n // Focus the first field, and give focus back to whatever opened the dialog when it goes.\n useEffect(() => {\n const opener = document.activeElement as HTMLElement | null;\n (form.current?.querySelector<HTMLElement>('input, select, textarea') ?? form.current?.querySelector<HTMLElement>('button'))?.focus();\n return () => { if (opener && opener.isConnected) opener.focus(); };\n }, []);\n\n const errorsOf = (name: string | undefined) => errors.filter(error => error.field === name);\n const fieldNames = new Set(fields.map(field => field.name));\n const general = errors.filter(error => error.field === undefined || !fieldNames.has(error.field));\n\n const submit = async (event: FormEvent) => {\n event.preventDefault();\n if (submitting) return;\n\n const missing = fields.filter(field => field.required && (values[field.name] === undefined || values[field.name] === '')).map(field => ({ field: field.name, message: `${sliceFieldLabel(field)} is required.` }));\n setErrors(missing);\n if (missing.length > 0) {\n form.current?.querySelector<HTMLElement>('[aria-invalid=\"true\"]')?.focus();\n return;\n }\n\n setSubmitting(true);\n try {\n const result = await invoker.invoke(slice, slicePayload(fields, values), command);\n if (result.succeeded) {\n close(result);\n } else {\n const reported = result.validationErrors ?? [];\n setErrors(reported.length > 0 || !result.message ? reported : [{ message: result.message }]);\n }\n } finally {\n setSubmitting(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLFormElement>) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n close();\n } else if (event.key === 'Tab') {\n const targets = Array.from(form.current?.querySelectorAll<HTMLElement>(focusable) ?? []);\n if (targets.length === 0) return;\n const first = targets[0];\n const last = targets[targets.length - 1];\n if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }\n else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }\n }\n };\n\n return <form ref={form} className='scene-slice-dialog' data-scene-slice-dialog={slice} data-scene-command={command} noValidate onSubmit={submit} onKeyDown={onKeyDown}>\n <header className='scene-slice-dialog-header'>\n <h2 className='scene-slice-dialog-title'>{title}</h2>\n <p className='scene-slice-dialog-subtitle'>State Change · {sliceName}</p>\n </header>\n <div className='scene-slice-dialog-fields'>\n {fields.map(field => {\n const id = `${identity}-${field.name}`;\n const own = errorsOf(field.name);\n const type = inputTypeOf(field);\n const description = own.length > 0 ? `${id}-error` : undefined;\n return <div key={field.name} className='scene-slice-dialog-field' data-type={type}>\n <label htmlFor={id}>{sliceFieldLabel(field)}{field.required ? <span aria-hidden='true'> *</span> : null}</label>\n <input id={id} name={field.name} type={type} required={field.required} aria-required={field.required || undefined}\n aria-invalid={own.length > 0 ? true : undefined} aria-describedby={description}\n {...(type === 'checkbox' ? { checked: values[field.name] === true } : { value: (values[field.name] as string | undefined) ?? '' })}\n onChange={event => setValues({ ...values, [field.name]: type === 'checkbox' ? event.target.checked : event.target.value })} />\n {own.length > 0 && <p id={description} className='scene-slice-dialog-error' role='alert'>{own.map(error => error.message).join(' ')}</p>}\n </div>;\n })}\n </div>\n {general.length > 0 && <p className='scene-slice-dialog-error scene-slice-dialog-summary' role='alert'>{general.map(error => error.message).join(' ')}</p>}\n <footer className='scene-slice-dialog-footer'>\n <button type='button' className='scene-slice-dialog-cancel' onClick={() => close()}>Cancel</button>\n <button type='submit' className='scene-slice-dialog-submit' disabled={submitting}>{title}</button>\n </footer>\n </form>;\n}\n"],"names":["focusable","sliceFieldLabel","field","label","spaced","name","replace","trim","toLowerCase","charAt","toUpperCase","slice","inputTypeOf","type","initialValuesOf","fields","values","initialValue","undefined","String","slicePayload","payload","value","Number","SliceDialogView","sliceProp","command","commandProp","title","titleProp","onClose","navigation","useContext","SceneNavigationContextInternal","opened","sliceDialogParameters","currentDialogParameters","invoker","useSliceInvoker","fieldsFor","useSliceCommandFields","model","useSceneNavigationModel","useMemo","sliceName","split","pop","index","structure","ApplicationStructureIndex","findSlice","id","setValues","useState","errors","setErrors","submitting","setSubmitting","form","useRef","identity","useId","close","result","closeDialog","useEffect","opener","document","activeElement","current","querySelector","focus","isConnected","errorsOf","filter","error","fieldNames","Set","map","general","has","submit","event","preventDefault","missing","required","message","length","invoke","succeeded","reported","validationErrors","onKeyDown","key","targets","Array","from","querySelectorAll","first","last","shiftKey","_jsxs","ref","className","data-scene-slice-dialog","data-scene-command","noValidate","onSubmit","header","_jsx","h2","p","div","own","description","data-type","htmlFor","span","aria-hidden","input","aria-required","aria-invalid","aria-describedby","checked","onChange","target","role","join","footer","button","onClick","disabled"],"mappings":";;;;;;;;;;;AA4BA,MAAMA,SAAAA,GAAY,0IAAA;AAElB,+EACO,SAASC,eAAAA,CAAgBC,KAAwB,EAAA;AACpD,IAAA,IAAIA,KAAAA,CAAMC,KAAK,EAAE,OAAOD,MAAMC,KAAK;AACnC,IAAA,MAAMC,MAAAA,GAASF,KAAAA,CAAMG,IAAI,CAACC,OAAO,CAAC,oBAAA,EAAsB,OAAA,CAAA,CAASA,OAAO,CAAC,QAAA,EAAU,GAAA,CAAA,CAAKC,IAAI,GAAGC,WAAW,EAAA;IAC1G,OAAOJ,MAAAA,CAAOK,MAAM,CAAC,CAAA,CAAA,CAAGC,WAAW,EAAA,GAAKN,MAAAA,CAAOO,KAAK,CAAC,CAAA,CAAA;AACzD;AAEA,SAASC,YAAYV,KAAwB,EAAA;IACzC,OAAQA,KAAAA,CAAMW,IAAI,CAACL,WAAW,EAAA;QAC1B,KAAK,QAAA;QAAU,KAAK,KAAA;QAAO,KAAK,SAAA;QAAW,KAAK,MAAA;QAAQ,KAAK,QAAA;QAAU,KAAK,SAAA;QAAW,KAAK,OAAA;QAAS,KAAK,QAAA;QAAU,KAAK,OAAA;QAAS,KAAK,OAAA;YAAS,OAAO,QAAA;QACvJ,KAAK,SAAA;QAAW,KAAK,MAAA;YAAQ,OAAO,UAAA;QACpC,KAAK,MAAA;QAAQ,KAAK,UAAA;YAAY,OAAO,MAAA;AACrC,QAAA;YAAS,OAAO,MAAA;AACpB;AACJ;AAEA,uHACA,SAASM,eAAAA,CAAgBC,MAA2B,EAAA;AAChD,IAAA,MAAMC,SAA2C,EAAC;IAClD,KAAK,MAAMd,SAASa,MAAAA,CAAQ;QACxB,IAAIb,KAAAA,CAAMe,YAAY,KAAKC,SAAAA,EAAW;AACtCF,QAAAA,MAAM,CAACd,KAAAA,CAAMG,IAAI,CAAC,GAAG,OAAOH,MAAMe,YAAY,KAAK,SAAA,IAAaL,WAAAA,CAAYV,WAAW,UAAA,GAAaA,KAAAA,CAAMe,YAAY,GAAGE,MAAAA,CAAOjB,MAAMe,YAAY,CAAA;AACtJ,IAAA;IAEA,OAAOD,MAAAA;AACX;AAEA,iGACO,SAASI,YAAAA,CAAaL,MAA2B,EAAEC,MAAwC,EAAA;AAC9F,IAAA,MAAMK,UAAmC,EAAC;IAC1C,KAAK,MAAMnB,SAASa,MAAAA,CAAQ;AACxB,QAAA,MAAMO,KAAAA,GAAQN,MAAM,CAACd,KAAAA,CAAMG,IAAI,CAAC;AAChC,QAAA,MAAMQ,OAAOD,WAAAA,CAAYV,KAAAA,CAAAA;QACzB,IAAIW,IAAAA,KAAS,YAAYQ,OAAO,CAACnB,MAAMG,IAAI,CAAC,GAAGiB,KAAAA,KAAU,IAAA;AACpD,aAAA,IAAIT,SAAS,QAAA,EAAU;YAAE,IAAIS,KAAAA,KAAUJ,SAAAA,IAAaI,KAAAA,KAAU,EAAA,EAAID,OAAO,CAACnB,KAAAA,CAAMG,IAAI,CAAC,GAAGkB,MAAAA,CAAOD,KAAAA,CAAAA;AAAQ,QAAA,CAAA,MACvGD,OAAO,CAACnB,KAAAA,CAAMG,IAAI,CAAC,GAAGiB,KAAAA,IAAS,EAAA;AACxC,IAAA;IAEA,OAAOD,OAAAA;AACX;AAEA;;;;;;;;;;;;AAYC,IACM,SAASG,eAAAA,CAAgB,EAAEb,OAAOc,SAAS,EAAEC,OAAAA,EAASC,WAAW,EAAEC,KAAAA,EAAOC,SAAS,EAAEC,OAAO,EAAwB,EAAA;AACvH,IAAA,MAAMC,aAAaC,gBAAAA,CAAWC,qDAAAA,CAAAA;IAC9B,MAAMC,MAAAA,GAASC,6CAAsBJ,UAAAA,EAAYK,uBAAAA,CAAAA;IACjD,MAAMzB,KAAAA,GAAQc,SAAAA,IAAaS,MAAAA,EAAQvB,KAAAA,IAAS,EAAA;IAC5C,MAAMe,OAAAA,GAAUC,WAAAA,IAAeO,MAAAA,EAAQR,OAAAA,IAAWf,KAAAA;IAClD,MAAMiB,KAAAA,GAAQC,SAAAA,IAAaK,MAAAA,EAAQN,KAAAA,IAASF,OAAAA;AAE5C,IAAA,MAAMW,OAAAA,GAAUC,mCAAAA,EAAAA;AAChB,IAAA,MAAMC,SAAAA,GAAYC,+CAAAA,EAAAA;AAClB,IAAA,MAAMC,KAAAA,GAAQC,mDAAAA,EAAAA;AACd,IAAA,MAAM3B,SAAS4B,aAAAA,CAAQ,IAAMJ,YAAYb,OAAAA,EAASf,KAAAA,CAAAA,IAAU,EAAE,EAAE;AAACe,QAAAA,OAAAA;AAASa,QAAAA,SAAAA;AAAW5B,QAAAA;AAAM,KAAA,CAAA;AAC3F,IAAA,MAAMiC,YAAYD,aAAAA,CAAQ,IAAA;QACtB,IAAI,CAACF,OAAO,OAAO9B,KAAAA,CAAMkC,KAAK,CAAC,GAAA,CAAA,CAAKC,GAAG,EAAA,IAAMnC,KAAAA;QAC7C,MAAMoC,KAAAA,GAAQN,KAAAA,CAAMO,SAAS,YAAYC,sCAAAA,GAA4BR,KAAAA,CAAMO,SAAS,GAAG,IAAIC,sCAAAA,CAA0BR,KAAAA,CAAMO,SAAS,CAAA;QACpI,OAAOD,KAAAA,CAAMG,SAAS,CAACvC,KAAAA,CAAAA,EAAQwC,EAAAA,IAAMxC,MAAMkC,KAAK,CAAC,GAAA,CAAA,CAAKC,GAAG,EAAA,IAAMnC,KAAAA;IACnE,CAAA,EAAG;AAAC8B,QAAAA,KAAAA;AAAO9B,QAAAA;AAAM,KAAA,CAAA;AAEjB,IAAA,MAAM,CAACK,MAAAA,EAAQoC,SAAAA,CAAU,GAAGC,cAAAA,CAA2C,IAAMvC,eAAAA,CAAgBC,MAAAA,CAAAA,CAAAA;AAC7F,IAAA,MAAM,CAACuC,MAAAA,EAAQC,SAAAA,CAAU,GAAGF,eAAsC,EAAE,CAAA;AACpE,IAAA,MAAM,CAACG,UAAAA,EAAYC,aAAAA,CAAc,GAAGJ,cAAAA,CAAS,KAAA,CAAA;AAC7C,IAAA,MAAMK,OAAOC,YAAAA,CAAwB,IAAA,CAAA;AACrC,IAAA,MAAMC,QAAAA,GAAWC,WAAAA,EAAAA;AAEjB,IAAA,MAAMC,QAAQ,CAACC,MAAAA,GAA8BjC,UAAUA,OAAAA,CAAQiC,MAAAA,CAAAA,GAAUhC,YAAYiC,WAAAA,CAAYD,MAAAA,CAAAA;;IAGjGE,eAAAA,CAAU,IAAA;QACN,MAAMC,MAAAA,GAASC,SAASC,aAAa;QACpCV,CAAAA,IAAAA,CAAKW,OAAO,EAAEC,aAAAA,CAA2B,yBAAA,CAAA,IAA8BZ,KAAKW,OAAO,EAAEC,aAAAA,CAA2B,QAAA,CAAQ,GAAIC,KAAAA,EAAAA;QAC7H,OAAO,IAAA;AAAQ,YAAA,IAAIL,MAAAA,IAAUA,MAAAA,CAAOM,WAAW,EAAEN,OAAOK,KAAK,EAAA;AAAI,QAAA,CAAA;AACrE,IAAA,CAAA,EAAG,EAAE,CAAA;IAEL,MAAME,QAAAA,GAAW,CAACpE,IAAAA,GAA6BiD,MAAAA,CAAOoB,MAAM,CAACC,CAAAA,KAAAA,GAASA,KAAAA,CAAMzE,KAAK,KAAKG,IAAAA,CAAAA;IACtF,MAAMuE,UAAAA,GAAa,IAAIC,GAAAA,CAAI9D,MAAAA,CAAO+D,GAAG,CAAC5E,CAAAA,KAAAA,GAASA,KAAAA,CAAMG,IAAI,CAAA,CAAA;AACzD,IAAA,MAAM0E,OAAAA,GAAUzB,MAAAA,CAAOoB,MAAM,CAACC,CAAAA,KAAAA,GAASA,KAAAA,CAAMzE,KAAK,KAAKgB,aAAa,CAAC0D,UAAAA,CAAWI,GAAG,CAACL,MAAMzE,KAAK,CAAA,CAAA;AAE/F,IAAA,MAAM+E,SAAS,OAAOC,KAAAA,GAAAA;AAClBA,QAAAA,KAAAA,CAAMC,cAAc,EAAA;AACpB,QAAA,IAAI3B,UAAAA,EAAY;AAEhB,QAAA,MAAM4B,OAAAA,GAAUrE,MAAAA,CAAO2D,MAAM,CAACxE,CAAAA,KAAAA,GAASA,KAAAA,CAAMmF,QAAQ,KAAKrE,MAAM,CAACd,KAAAA,CAAMG,IAAI,CAAC,KAAKa,SAAAA,IAAaF,MAAM,CAACd,KAAAA,CAAMG,IAAI,CAAC,KAAK,EAAC,CAAA,CAAA,CAAIyE,GAAG,CAAC5E,CAAAA,SAAU;AAAEA,gBAAAA,KAAAA,EAAOA,MAAMG,IAAI;AAAEiF,gBAAAA,OAAAA,EAAS,CAAA,EAAGrF,eAAAA,CAAgBC,KAAAA,CAAAA,CAAO,aAAa;aAAE,CAAA,CAAA;QAC/MqD,SAAAA,CAAU6B,OAAAA,CAAAA;QACV,IAAIA,OAAAA,CAAQG,MAAM,GAAG,CAAA,EAAG;YACpB7B,IAAAA,CAAKW,OAAO,EAAEC,aAAAA,CAA2B,uBAAA,CAAA,EAA0BC,KAAAA,EAAAA;AACnE,YAAA;AACJ,QAAA;QAEAd,aAAAA,CAAc,IAAA,CAAA;QACd,IAAI;YACA,MAAMM,MAAAA,GAAS,MAAM1B,OAAAA,CAAQmD,MAAM,CAAC7E,KAAAA,EAAOS,YAAAA,CAAaL,QAAQC,MAAAA,CAAAA,EAASU,OAAAA,CAAAA;YACzE,IAAIqC,MAAAA,CAAO0B,SAAS,EAAE;gBAClB3B,KAAAA,CAAMC,MAAAA,CAAAA;YACV,CAAA,MAAO;AACH,gBAAA,MAAM2B,QAAAA,GAAW3B,MAAAA,CAAO4B,gBAAgB,IAAI,EAAE;gBAC9CpC,SAAAA,CAAUmC,QAAAA,CAASH,MAAM,GAAG,CAAA,IAAK,CAACxB,MAAAA,CAAOuB,OAAO,GAAGI,QAAAA,GAAW;AAAC,oBAAA;AAAEJ,wBAAAA,OAAAA,EAASvB,OAAOuB;AAAQ;AAAE,iBAAA,CAAA;AAC/F,YAAA;QACJ,CAAA,QAAU;YACN7B,aAAAA,CAAc,KAAA,CAAA;AAClB,QAAA;AACJ,IAAA,CAAA;AAEA,IAAA,MAAMmC,YAAY,CAACV,KAAAA,GAAAA;QACf,IAAIA,KAAAA,CAAMW,GAAG,KAAK,QAAA,EAAU;AACxBX,YAAAA,KAAAA,CAAMC,cAAc,EAAA;AACpBrB,YAAAA,KAAAA,EAAAA;AACJ,QAAA,CAAA,MAAO,IAAIoB,KAAAA,CAAMW,GAAG,KAAK,KAAA,EAAO;YAC5B,MAAMC,OAAAA,GAAUC,MAAMC,IAAI,CAACtC,KAAKW,OAAO,EAAE4B,gBAAAA,CAA8BjG,SAAAA,CAAAA,IAAc,EAAE,CAAA;YACvF,IAAI8F,OAAAA,CAAQP,MAAM,KAAK,CAAA,EAAG;YAC1B,MAAMW,KAAAA,GAAQJ,OAAO,CAAC,CAAA,CAAE;AACxB,YAAA,MAAMK,OAAOL,OAAO,CAACA,OAAAA,CAAQP,MAAM,GAAG,CAAA,CAAE;AACxC,YAAA,IAAIL,MAAMkB,QAAQ,IAAIjC,QAAAA,CAASC,aAAa,KAAK8B,KAAAA,EAAO;AAAEhB,gBAAAA,KAAAA,CAAMC,cAAc,EAAA;AAAIgB,gBAAAA,IAAAA,CAAK5B,KAAK,EAAA;YAAI,CAAA,MAC3F,IAAI,CAACW,KAAAA,CAAMkB,QAAQ,IAAIjC,QAAAA,CAASC,aAAa,KAAK+B,IAAAA,EAAM;AAAEjB,gBAAAA,KAAAA,CAAMC,cAAc,EAAA;AAAIe,gBAAAA,KAAAA,CAAM3B,KAAK,EAAA;AAAI,YAAA;AAC1G,QAAA;AACJ,IAAA,CAAA;AAEA,IAAA,qBAAO8B,eAAA,CAAC3C,MAAAA,EAAAA;QAAK4C,GAAAA,EAAK5C,IAAAA;QAAM6C,SAAAA,EAAU,oBAAA;QAAqBC,yBAAAA,EAAyB7F,KAAAA;QAAO8F,oBAAAA,EAAoB/E,OAAAA;QAASgF,UAAU,EAAA,IAAA;QAACC,QAAAA,EAAU1B,MAAAA;QAAQW,SAAAA,EAAWA,SAAAA;;0BACxJS,eAAA,CAACO,QAAAA,EAAAA;gBAAOL,SAAAA,EAAU,2BAAA;;kCACdM,cAAA,CAACC,IAAAA,EAAAA;wBAAGP,SAAAA,EAAU,0BAAA;AAA4B3E,wBAAAA,QAAAA,EAAAA;;kCAC1CyE,eAAA,CAACU,GAAAA,EAAAA;wBAAER,SAAAA,EAAU,6BAAA;;AAA8B,4BAAA,iBAAA;AAAgB3D,4BAAAA;;;;;0BAE/DiE,cAAA,CAACG,KAAAA,EAAAA;gBAAIT,SAAAA,EAAU,2BAAA;0BACVxF,MAAAA,CAAO+D,GAAG,CAAC5E,CAAAA,KAAAA,GAAAA;AACR,oBAAA,MAAMiD,KAAK,CAAA,EAAGS,QAAAA,CAAS,CAAC,EAAE1D,KAAAA,CAAMG,IAAI,CAAA,CAAE;oBACtC,MAAM4G,GAAAA,GAAMxC,QAAAA,CAASvE,KAAAA,CAAMG,IAAI,CAAA;AAC/B,oBAAA,MAAMQ,OAAOD,WAAAA,CAAYV,KAAAA,CAAAA;oBACzB,MAAMgH,WAAAA,GAAcD,IAAI1B,MAAM,GAAG,IAAI,CAAA,EAAGpC,EAAAA,CAAG,MAAM,CAAC,GAAGjC,SAAAA;AACrD,oBAAA,qBAAOmF,eAAA,CAACW,KAAAA,EAAAA;wBAAqBT,SAAAA,EAAU,0BAAA;wBAA2BY,WAAAA,EAAWtG,IAAAA;;0CACzEwF,eAAA,CAAClG,OAAAA,EAAAA;gCAAMiH,OAAAA,EAASjE,EAAAA;;oCAAKlD,eAAAA,CAAgBC,KAAAA,CAAAA;oCAAQA,KAAAA,CAAMmF,QAAQ,iBAAGwB,cAAA,CAACQ,MAAAA,EAAAA;wCAAKC,aAAAA,EAAY,MAAA;AAAO,wCAAA,QAAA,EAAA;AAAY,qCAAA,CAAA,GAAA;;;0CACnGT,cAAA,CAACU,OAAAA,EAAAA;gCAAMpE,EAAAA,EAAIA,EAAAA;AAAI9C,gCAAAA,IAAAA,EAAMH,MAAMG,IAAI;gCAAEQ,IAAAA,EAAMA,IAAAA;AAAMwE,gCAAAA,QAAAA,EAAUnF,MAAMmF,QAAQ;gCAAEmC,eAAAA,EAAetH,KAAAA,CAAMmF,QAAQ,IAAInE,SAAAA;AACpGuG,gCAAAA,cAAAA,EAAcR,GAAAA,CAAI1B,MAAM,GAAG,CAAA,GAAI,IAAA,GAAOrE,SAAAA;gCAAWwG,kBAAAA,EAAkBR,WAAAA;AAClE,gCAAA,GAAIrG,SAAS,UAAA,GAAa;AAAE8G,oCAAAA,OAAAA,EAAS3G,MAAM,CAACd,KAAAA,CAAMG,IAAI,CAAC,KAAK;iCAAK,GAAI;AAAEiB,oCAAAA,KAAAA,EAAO,MAAO,CAACpB,KAAAA,CAAMG,IAAI,CAAC,IAA2B;iCAAI;gCACjIuH,QAAAA,EAAU1C,CAAAA,QAAS9B,SAAAA,CAAU;AAAE,wCAAA,GAAGpC,MAAM;AAAE,wCAAA,CAACd,KAAAA,CAAMG,IAAI,GAAGQ,SAAS,UAAA,GAAaqE,KAAAA,CAAM2C,MAAM,CAACF,OAAO,GAAGzC,KAAAA,CAAM2C,MAAM,CAACvG;AAAM,qCAAA;;4BAC3H2F,GAAAA,CAAI1B,MAAM,GAAG,CAAA,kBAAKsB,cAAA,CAACE,GAAAA,EAAAA;gCAAE5D,EAAAA,EAAI+D,WAAAA;gCAAaX,SAAAA,EAAU,0BAAA;gCAA2BuB,IAAAA,EAAK,OAAA;0CAASb,GAAAA,CAAInC,GAAG,CAACH,CAAAA,KAAAA,GAASA,MAAMW,OAAO,CAAA,CAAEyC,IAAI,CAAC,GAAA;;;AANlH7H,qBAAAA,EAAAA,KAAAA,CAAMG,IAAI,CAAA;AAQ/B,gBAAA,CAAA;;YAEH0E,OAAAA,CAAQQ,MAAM,GAAG,CAAA,kBAAKsB,cAAA,CAACE,GAAAA,EAAAA;gBAAER,SAAAA,EAAU,qDAAA;gBAAsDuB,IAAAA,EAAK,OAAA;0BAAS/C,OAAAA,CAAQD,GAAG,CAACH,CAAAA,KAAAA,GAASA,MAAMW,OAAO,CAAA,CAAEyC,IAAI,CAAC,GAAA;;0BACjJ1B,eAAA,CAAC2B,QAAAA,EAAAA;gBAAOzB,SAAAA,EAAU,2BAAA;;kCACdM,cAAA,CAACoB,QAAAA,EAAAA;wBAAOpH,IAAAA,EAAK,QAAA;wBAAS0F,SAAAA,EAAU,2BAAA;AAA4B2B,wBAAAA,OAAAA,EAAS,IAAMpE,KAAAA,EAAAA;AAAS,wBAAA,QAAA,EAAA;;kCACpF+C,cAAA,CAACoB,QAAAA,EAAAA;wBAAOpH,IAAAA,EAAK,QAAA;wBAAS0F,SAAAA,EAAU,2BAAA;wBAA4B4B,QAAAA,EAAU3E,UAAAA;AAAa5B,wBAAAA,QAAAA,EAAAA;;;;;;AAG/F;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var createAnnouncingInvoker = require('./createAnnouncingInvoker.js');
|
|
6
|
+
|
|
7
|
+
const announcing = createAnnouncingInvoker.createAnnouncingInvoker();
|
|
8
|
+
const defaultScope = {
|
|
9
|
+
invoker: announcing,
|
|
10
|
+
warnEmptySlices: false
|
|
11
|
+
};
|
|
12
|
+
const Context = /*#__PURE__*/ react.createContext(defaultScope);
|
|
13
|
+
/**
|
|
14
|
+
* Supplies the `sliceInvoker` every toolbar dialog below it submits through: a real one in Stage, a dry-run one in the Studio
|
|
15
|
+
* preview. Without a provider the command is announced as `cratis.scene.command`.
|
|
16
|
+
*/ function SliceInvokerProvider({ invoker, warnEmptySlices, children }) {
|
|
17
|
+
const warn = warnEmptySlices ?? invoker.dryRun === true;
|
|
18
|
+
const value = react.useMemo(()=>({
|
|
19
|
+
invoker,
|
|
20
|
+
warnEmptySlices: warn
|
|
21
|
+
}), [
|
|
22
|
+
invoker,
|
|
23
|
+
warn
|
|
24
|
+
]);
|
|
25
|
+
return /*#__PURE__*/ jsxRuntime.jsx(Context.Provider, {
|
|
26
|
+
value: value,
|
|
27
|
+
children: children
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** The invoker in scope: the provider's, or the one that announces the command. */ function useSliceInvoker() {
|
|
31
|
+
return react.useContext(Context).invoker;
|
|
32
|
+
}
|
|
33
|
+
/** Whether a button without a slice should show a warning badge here. */ function useWarnEmptySlices() {
|
|
34
|
+
return react.useContext(Context).warnEmptySlices;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.SliceInvokerProvider = SliceInvokerProvider;
|
|
38
|
+
exports.useSliceInvoker = useSliceInvoker;
|
|
39
|
+
exports.useWarnEmptySlices = useWarnEmptySlices;
|
|
40
|
+
//# sourceMappingURL=SliceInvokerContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SliceInvokerContext.js","sources":["../../../slices/SliceInvokerContext.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 { createAnnouncingInvoker } from './createAnnouncingInvoker';\nimport { SliceInvoker } from './SliceInvoker';\n\ninterface SliceInvokerScope {\n invoker: SliceInvoker;\n warnEmptySlices: boolean;\n}\n\nconst announcing = createAnnouncingInvoker();\nconst defaultScope: SliceInvokerScope = { invoker: announcing, warnEmptySlices: false };\nconst Context = createContext<SliceInvokerScope>(defaultScope);\n\nexport interface SliceInvokerProviderProps {\n /** The invoker toolbar dialogs submit through. */\n invoker: SliceInvoker;\n\n /**\n * Whether a toolbar button without a slice shows a warning badge. Design and preview hosts turn it on (TB5); a\n * running application never shows it. A dry-run invoker implies it.\n */\n warnEmptySlices?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Supplies the `sliceInvoker` every toolbar dialog below it submits through: a real one in Stage, a dry-run one in the Studio\n * preview. Without a provider the command is announced as `cratis.scene.command`.\n */\nexport function SliceInvokerProvider({ invoker, warnEmptySlices, children }: SliceInvokerProviderProps) {\n const warn = warnEmptySlices ?? (invoker as { dryRun?: boolean }).dryRun === true;\n const value = useMemo(() => ({ invoker, warnEmptySlices: warn }), [invoker, warn]);\n return <Context.Provider value={value}>{children}</Context.Provider>;\n}\n\n/** The invoker in scope: the provider's, or the one that announces the command. */\nexport function useSliceInvoker(): SliceInvoker {\n return useContext(Context).invoker;\n}\n\n/** Whether a button without a slice should show a warning badge here. */\nexport function useWarnEmptySlices(): boolean {\n return useContext(Context).warnEmptySlices;\n}\n"],"names":["announcing","createAnnouncingInvoker","defaultScope","invoker","warnEmptySlices","Context","createContext","SliceInvokerProvider","children","warn","dryRun","value","useMemo","_jsx","Provider","useSliceInvoker","useContext","useWarnEmptySlices"],"mappings":";;;;;;AAYA,MAAMA,UAAAA,GAAaC,+CAAAA,EAAAA;AACnB,MAAMC,YAAAA,GAAkC;IAAEC,OAAAA,EAASH,UAAAA;IAAYI,eAAAA,EAAiB;AAAM,CAAA;AACtF,MAAMC,wBAAUC,mBAAAA,CAAiCJ,YAAAA,CAAAA;AAcjD;;;IAIO,SAASK,oBAAAA,CAAqB,EAAEJ,OAAO,EAAEC,eAAe,EAAEI,QAAQ,EAA6B,EAAA;AAClG,IAAA,MAAMC,IAAAA,GAAOL,eAAAA,IAAmB,OAACD,CAAiCO,MAAM,KAAK,IAAA;IAC7E,MAAMC,KAAAA,GAAQC,aAAAA,CAAQ,KAAO;AAAET,YAAAA,OAAAA;YAASC,eAAAA,EAAiBK;AAAK,SAAA,CAAA,EAAI;AAACN,QAAAA,OAAAA;AAASM,QAAAA;AAAK,KAAA,CAAA;IACjF,qBAAOI,cAAA,CAACR,QAAQS,QAAQ,EAAA;QAACH,KAAAA,EAAOA,KAAAA;AAAQH,QAAAA,QAAAA,EAAAA;;AAC5C;AAEA,oFACO,SAASO,eAAAA,GAAAA;IACZ,OAAOC,gBAAAA,CAAWX,SAASF,OAAO;AACtC;AAEA,0EACO,SAASc,kBAAAA,GAAAA;IACZ,OAAOD,gBAAAA,CAAWX,SAASD,eAAe;AAC9C;;;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
* The invoker used when a host provides none: it announces the command with the host-neutral `cratis.scene.command` event,
|
|
7
|
+
* as a `core:toolbarItem` action always has, and reports success. A host that listens for the event runs the command.
|
|
8
|
+
*/ function createAnnouncingInvoker() {
|
|
9
|
+
return {
|
|
10
|
+
async invoke (sliceId, payload, command) {
|
|
11
|
+
globalThis.dispatchEvent(new CustomEvent('cratis.scene.command', {
|
|
12
|
+
detail: {
|
|
13
|
+
command: command ?? sliceId,
|
|
14
|
+
slice: sliceId,
|
|
15
|
+
arguments: payload
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
return {
|
|
19
|
+
succeeded: true,
|
|
20
|
+
payload
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.createAnnouncingInvoker = createAnnouncingInvoker;
|
|
27
|
+
//# sourceMappingURL=createAnnouncingInvoker.js.map
|