@cratis/scene.react 4.28.0 → 4.30.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 +36 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
- package/dist/cjs/navigation/SceneNavigationHost.js +34 -18
- package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
- package/dist/cjs/navigation/region/NavigationRegionClassNames.js +21 -0
- package/dist/cjs/navigation/region/NavigationRegionClassNames.js.map +1 -0
- package/dist/cjs/navigation/region/NavigationRegionView.js +283 -0
- package/dist/cjs/navigation/region/NavigationRegionView.js.map +1 -0
- package/dist/cjs/navigation/region/SceneNavigationModelContext.js +22 -0
- package/dist/cjs/navigation/region/SceneNavigationModelContext.js.map +1 -0
- package/dist/cjs/navigation/region/currentModuleOf.js +15 -0
- package/dist/cjs/navigation/region/currentModuleOf.js.map +1 -0
- package/dist/cjs/navigation/region/navigationDestinations.js +46 -0
- package/dist/cjs/navigation/region/navigationDestinations.js.map +1 -0
- package/dist/cjs/navigation/region/navigationItemActive.js +31 -0
- package/dist/cjs/navigation/region/navigationItemActive.js.map +1 -0
- package/dist/cjs/navigation/region/navigationItemDestination.js +34 -0
- package/dist/cjs/navigation/region/navigationItemDestination.js.map +1 -0
- 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 +254 -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 +14 -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 +5 -1
- package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
- package/dist/esm/navigation/SceneNavigationHost.js +35 -19
- 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/index.d.ts +8 -0
- package/dist/esm/navigation/index.d.ts.map +1 -1
- package/dist/esm/navigation/index.js +8 -0
- package/dist/esm/navigation/index.js.map +1 -1
- package/dist/esm/navigation/region/NavigationRegionClassNames.d.ts +12 -0
- package/dist/esm/navigation/region/NavigationRegionClassNames.d.ts.map +1 -0
- package/dist/esm/navigation/region/NavigationRegionClassNames.js +19 -0
- package/dist/esm/navigation/region/NavigationRegionClassNames.js.map +1 -0
- package/dist/esm/navigation/region/NavigationRegionView.d.ts +20 -0
- package/dist/esm/navigation/region/NavigationRegionView.d.ts.map +1 -0
- package/dist/esm/navigation/region/NavigationRegionView.js +281 -0
- package/dist/esm/navigation/region/NavigationRegionView.js.map +1 -0
- package/dist/esm/navigation/region/SceneNavigationModel.d.ts +9 -0
- package/dist/esm/navigation/region/SceneNavigationModel.d.ts.map +1 -0
- package/dist/esm/navigation/region/SceneNavigationModel.js +2 -0
- package/dist/esm/navigation/region/SceneNavigationModel.js.map +1 -0
- package/dist/esm/navigation/region/SceneNavigationModelContext.d.ts +9 -0
- package/dist/esm/navigation/region/SceneNavigationModelContext.d.ts.map +1 -0
- package/dist/esm/navigation/region/SceneNavigationModelContext.js +19 -0
- package/dist/esm/navigation/region/SceneNavigationModelContext.js.map +1 -0
- package/dist/esm/navigation/region/currentModuleOf.d.ts +3 -0
- package/dist/esm/navigation/region/currentModuleOf.d.ts.map +1 -0
- package/dist/esm/navigation/region/currentModuleOf.js +13 -0
- package/dist/esm/navigation/region/currentModuleOf.js.map +1 -0
- package/dist/esm/navigation/region/navigationDestinations.d.ts +4 -0
- package/dist/esm/navigation/region/navigationDestinations.d.ts.map +1 -0
- package/dist/esm/navigation/region/navigationDestinations.js +44 -0
- package/dist/esm/navigation/region/navigationDestinations.js.map +1 -0
- package/dist/esm/navigation/region/navigationItemActive.d.ts +5 -0
- package/dist/esm/navigation/region/navigationItemActive.d.ts.map +1 -0
- package/dist/esm/navigation/region/navigationItemActive.js +29 -0
- package/dist/esm/navigation/region/navigationItemActive.js.map +1 -0
- package/dist/esm/navigation/region/navigationItemDestination.d.ts +5 -0
- package/dist/esm/navigation/region/navigationItemDestination.d.ts.map +1 -0
- package/dist/esm/navigation/region/navigationItemDestination.js +31 -0
- package/dist/esm/navigation/region/navigationItemDestination.js.map +1 -0
- 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 +7 -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 +250 -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 +35 -9
- package/navigation/SceneNavigationState.ts +3 -0
- package/navigation/index.ts +8 -0
- package/navigation/region/NavigationRegionClassNames.ts +46 -0
- package/navigation/region/NavigationRegionView.tsx +234 -0
- package/navigation/region/SceneNavigationModel.ts +29 -0
- package/navigation/region/SceneNavigationModelContext.tsx +25 -0
- package/navigation/region/currentModuleOf.ts +14 -0
- package/navigation/region/navigationDestinations.ts +41 -0
- package/navigation/region/navigationItemActive.ts +39 -0
- package/navigation/region/navigationItemDestination.ts +29 -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 +19 -0
- package/slices/SliceCommandFieldsContext.tsx +28 -0
- package/slices/SliceDialogView.tsx +176 -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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var scene_model = require('@cratis/scene.model');
|
|
4
|
+
var scene_engine = require('@cratis/scene.engine');
|
|
5
|
+
var navigationItemDestination = require('./navigationItemDestination.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
|
+
function flatten(items) {
|
|
10
|
+
return items.flatMap((item)=>[
|
|
11
|
+
item,
|
|
12
|
+
...flatten(item.children ?? [])
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Every destination the navigation of the application and of each module can lead to, once each: what a
|
|
17
|
+
* `SceneNavigationHost` is given as `destinations` so deep links to a navigation item resolve, and an item that renders
|
|
18
|
+
* into a nested outlet is placed with the screen that declares the outlet.
|
|
19
|
+
*/ function navigationDestinations(structure, navigation, screenFor) {
|
|
20
|
+
const index = structure instanceof scene_engine.ApplicationStructureIndex ? structure : new scene_engine.ApplicationStructureIndex(structure);
|
|
21
|
+
const modules = [
|
|
22
|
+
undefined,
|
|
23
|
+
...index.structure.modules.map((module)=>module.id)
|
|
24
|
+
];
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const destinations = [];
|
|
27
|
+
for (const module of modules){
|
|
28
|
+
for (const region of [
|
|
29
|
+
scene_model.NavigationRegionKind.Topbar,
|
|
30
|
+
scene_model.NavigationRegionKind.Sidebar
|
|
31
|
+
]){
|
|
32
|
+
for (const item of flatten(scene_engine.effectiveNavigation(index, navigation, module, region).items)){
|
|
33
|
+
const destination = navigationItemDestination.navigationItemDestination(item, screenFor);
|
|
34
|
+
if (destination === undefined) continue;
|
|
35
|
+
const key = JSON.stringify(destination);
|
|
36
|
+
if (seen.has(key)) continue;
|
|
37
|
+
seen.add(key);
|
|
38
|
+
destinations.push(destination);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return destinations;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.navigationDestinations = navigationDestinations;
|
|
46
|
+
//# sourceMappingURL=navigationDestinations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigationDestinations.js","sources":["../../../../navigation/region/navigationDestinations.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 { ApplicationStructure, DestinationReference, NavigationRegionKind, NavigationModel } from '@cratis/scene.model';\nimport { ApplicationStructureIndex, EffectiveNavItem, effectiveNavigation } from '@cratis/scene.engine';\nimport { navigationItemDestination } from './navigationItemDestination';\n\nfunction flatten(items: EffectiveNavItem[]): EffectiveNavItem[] {\n return items.flatMap(item => [item, ...flatten(item.children ?? [])]);\n}\n\n/**\n * Every destination the navigation of the application and of each module can lead to, once each: what a\n * `SceneNavigationHost` is given as `destinations` so deep links to a navigation item resolve, and an item that renders\n * into a nested outlet is placed with the screen that declares the outlet.\n */\nexport function navigationDestinations(\n structure: ApplicationStructure | ApplicationStructureIndex,\n navigation: NavigationModel,\n screenFor?: (item: EffectiveNavItem) => string | undefined,\n): DestinationReference[] {\n const index = structure instanceof ApplicationStructureIndex ? structure : new ApplicationStructureIndex(structure);\n const modules = [undefined, ...index.structure.modules.map(module => module.id)];\n const seen = new Set<string>();\n const destinations: DestinationReference[] = [];\n\n for (const module of modules) {\n for (const region of [NavigationRegionKind.Topbar, NavigationRegionKind.Sidebar]) {\n for (const item of flatten(effectiveNavigation(index, navigation, module, region).items)) {\n const destination = navigationItemDestination(item, screenFor);\n if (destination === undefined) continue;\n const key = JSON.stringify(destination);\n if (seen.has(key)) continue;\n seen.add(key);\n destinations.push(destination);\n }\n }\n }\n\n return destinations;\n}\n"],"names":["flatten","items","flatMap","item","children","navigationDestinations","structure","navigation","screenFor","index","ApplicationStructureIndex","modules","undefined","map","module","id","seen","Set","destinations","region","NavigationRegionKind","Topbar","Sidebar","effectiveNavigation","destination","navigationItemDestination","key","JSON","stringify","has","add","push"],"mappings":";;;;;;AAAA;AACA;AAMA,SAASA,QAAQC,KAAyB,EAAA;AACtC,IAAA,OAAOA,KAAAA,CAAMC,OAAO,CAACC,CAAAA,IAAAA,GAAQ;AAACA,YAAAA,IAAAA;eAASH,OAAAA,CAAQG,IAAAA,CAAKC,QAAQ,IAAI,EAAE;AAAE,SAAA,CAAA;AACxE;AAEA;;;;AAIC,IACM,SAASC,sBAAAA,CACZC,SAA2D,EAC3DC,UAA2B,EAC3BC,SAA0D,EAAA;AAE1D,IAAA,MAAMC,KAAAA,GAAQH,SAAAA,YAAqBI,sCAAAA,GAA4BJ,SAAAA,GAAY,IAAII,sCAAAA,CAA0BJ,SAAAA,CAAAA;AACzG,IAAA,MAAMK,OAAAA,GAAU;AAACC,QAAAA,SAAAA;WAAcH,KAAAA,CAAMH,SAAS,CAACK,OAAO,CAACE,GAAG,CAACC,CAAAA,MAAAA,GAAUA,MAAAA,CAAOC,EAAE;AAAE,KAAA;AAChF,IAAA,MAAMC,OAAO,IAAIC,GAAAA,EAAAA;AACjB,IAAA,MAAMC,eAAuC,EAAE;IAE/C,KAAK,MAAMJ,UAAUH,OAAAA,CAAS;AAC1B,QAAA,KAAK,MAAMQ,MAAAA,IAAU;AAACC,YAAAA,gCAAAA,CAAqBC,MAAM;AAAED,YAAAA,gCAAAA,CAAqBE;SAAQ,CAAE;YAC9E,KAAK,MAAMnB,QAAQH,OAAAA,CAAQuB,gCAAAA,CAAoBd,OAAOF,UAAAA,EAAYO,MAAAA,EAAQK,MAAAA,CAAAA,CAAQlB,KAAK,CAAA,CAAG;gBACtF,MAAMuB,WAAAA,GAAcC,oDAA0BtB,IAAAA,EAAMK,SAAAA,CAAAA;AACpD,gBAAA,IAAIgB,gBAAgBZ,SAAAA,EAAW;gBAC/B,MAAMc,GAAAA,GAAMC,IAAAA,CAAKC,SAAS,CAACJ,WAAAA,CAAAA;gBAC3B,IAAIR,IAAAA,CAAKa,GAAG,CAACH,GAAAA,CAAAA,EAAM;AACnBV,gBAAAA,IAAAA,CAAKc,GAAG,CAACJ,GAAAA,CAAAA;AACTR,gBAAAA,YAAAA,CAAaa,IAAI,CAACP,WAAAA,CAAAA;AACtB,YAAA;AACJ,QAAA;AACJ,IAAA;IAEA,OAAON,YAAAA;AACX;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var scene_engine = require('@cratis/scene.engine');
|
|
4
|
+
var navigationItemDestination = require('./navigationItemDestination.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
|
+
* Whether a navigation item is the one the page shows.
|
|
10
|
+
*
|
|
11
|
+
* An item is active when the screen it opens is the one in the slot it renders into: the primary region for the
|
|
12
|
+
* `content` slot, the nested outlet of that name otherwise. So several items can be active at once - each is shown in
|
|
13
|
+
* its own slot - and closing a slot's screen un-highlights its item. With `matchModule` - a topbar - an item that targets
|
|
14
|
+
* a module without a feature is also active while any screen of that module is in its slot.
|
|
15
|
+
*
|
|
16
|
+
* @param item The effective item.
|
|
17
|
+
* @param placement What the host currently shows.
|
|
18
|
+
* @param screenFor Names the screen an item opens; the feature the item resolves to when absent.
|
|
19
|
+
* @param matchModule Whether a module item is active on any screen of the module; a topbar's are, a sidebar's are not.
|
|
20
|
+
*/ function navigationItemActive(item, placement, screenFor = navigationItemDestination.defaultScreenFor, matchModule = false) {
|
|
21
|
+
const screen = screenFor(item);
|
|
22
|
+
if (screen === undefined) return false;
|
|
23
|
+
const shown = item.slot === scene_engine.primarySlot ? placement.primaryScreen : placement.outlets[item.slot];
|
|
24
|
+
if (shown === undefined) return false;
|
|
25
|
+
if (shown === screen) return true;
|
|
26
|
+
const target = item.target;
|
|
27
|
+
return matchModule && target !== undefined && target.feature === undefined && shown.startsWith(`${target.module}.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
exports.navigationItemActive = navigationItemActive;
|
|
31
|
+
//# sourceMappingURL=navigationItemActive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigationItemActive.js","sources":["../../../../navigation/region/navigationItemActive.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 { EffectiveNavItem, primarySlot } from '@cratis/scene.engine';\nimport { defaultScreenFor } from './navigationItemDestination';\nimport { SceneNavigationContextValue } from '../SceneNavigationContext';\n\n/** Where the navigation host is: the part of {@link SceneNavigationContextValue} that says what is on the page. */\nexport type NavigationPlacement = Pick<SceneNavigationContextValue, 'primaryScreen' | 'outlets'>;\n\n/**\n * Whether a navigation item is the one the page shows.\n *\n * An item is active when the screen it opens is the one in the slot it renders into: the primary region for the\n * `content` slot, the nested outlet of that name otherwise. So several items can be active at once - each is shown in\n * its own slot - and closing a slot's screen un-highlights its item. With `matchModule` - a topbar - an item that targets\n * a module without a feature is also active while any screen of that module is in its slot.\n *\n * @param item The effective item.\n * @param placement What the host currently shows.\n * @param screenFor Names the screen an item opens; the feature the item resolves to when absent.\n * @param matchModule Whether a module item is active on any screen of the module; a topbar's are, a sidebar's are not.\n */\nexport function navigationItemActive(\n item: EffectiveNavItem,\n placement: NavigationPlacement,\n screenFor: (item: EffectiveNavItem) => string | undefined = defaultScreenFor,\n matchModule = false,\n): boolean {\n const screen = screenFor(item);\n if (screen === undefined) return false;\n\n const shown = item.slot === primarySlot ? placement.primaryScreen : placement.outlets[item.slot];\n if (shown === undefined) return false;\n if (shown === screen) return true;\n\n const target = item.target;\n return matchModule && target !== undefined && target.feature === undefined && shown.startsWith(`${target.module}.`);\n}\n"],"names":["navigationItemActive","item","placement","screenFor","defaultScreenFor","matchModule","screen","undefined","shown","slot","primarySlot","primaryScreen","outlets","target","feature","startsWith","module"],"mappings":";;;;;AAAA;AACA;AASA;;;;;;;;;;;;IAaO,SAASA,oBAAAA,CACZC,IAAsB,EACtBC,SAA8B,EAC9BC,SAAAA,GAA4DC,0CAAgB,EAC5EC,WAAAA,GAAc,KAAK,EAAA;AAEnB,IAAA,MAAMC,SAASH,SAAAA,CAAUF,IAAAA,CAAAA;IACzB,IAAIK,MAAAA,KAAWC,WAAW,OAAO,KAAA;AAEjC,IAAA,MAAMC,KAAAA,GAAQP,IAAAA,CAAKQ,IAAI,KAAKC,wBAAAA,GAAcR,SAAAA,CAAUS,aAAa,GAAGT,SAAAA,CAAUU,OAAO,CAACX,IAAAA,CAAKQ,IAAI,CAAC;IAChG,IAAID,KAAAA,KAAUD,WAAW,OAAO,KAAA;IAChC,IAAIC,KAAAA,KAAUF,QAAQ,OAAO,IAAA;IAE7B,MAAMO,MAAAA,GAASZ,KAAKY,MAAM;AAC1B,IAAA,OAAOR,WAAAA,IAAeQ,MAAAA,KAAWN,SAAAA,IAAaM,MAAAA,CAAOC,OAAO,KAAKP,SAAAA,IAAaC,KAAAA,CAAMO,UAAU,CAAC,CAAA,EAAGF,MAAAA,CAAOG,MAAM,CAAC,CAAC,CAAC,CAAA;AACtH;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var scene_model = require('@cratis/scene.model');
|
|
4
|
+
var scene_engine = require('@cratis/scene.engine');
|
|
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
|
+
/** The screen an item opens by default: the qualified id of the feature it resolves to. */ function defaultScreenFor(item) {
|
|
9
|
+
return item.target?.resolvedFeature;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Where activating a navigation item goes: its stable module/feature identity, the screen it opens, and the slot it
|
|
13
|
+
* renders into as the outlet - the primary `content` slot is the host's primary outlet.
|
|
14
|
+
*
|
|
15
|
+
* @param item The effective item.
|
|
16
|
+
* @param screenFor Names the screen an item opens; {@link defaultScreenFor} when absent.
|
|
17
|
+
* @returns The destination, or undefined when the item leads nowhere.
|
|
18
|
+
*/ function navigationItemDestination(item, screenFor = defaultScreenFor) {
|
|
19
|
+
const screen = screenFor(item);
|
|
20
|
+
if (screen === undefined || item.target === undefined) return undefined;
|
|
21
|
+
const resolved = item.target.resolvedFeature;
|
|
22
|
+
const destination = {
|
|
23
|
+
kind: scene_model.DestinationKind.Outlet,
|
|
24
|
+
module: item.target.module,
|
|
25
|
+
screen
|
|
26
|
+
};
|
|
27
|
+
if (resolved !== undefined && resolved.startsWith(`${item.target.module}.`)) destination.feature = resolved.substring(item.target.module.length + 1);
|
|
28
|
+
if (item.slot !== scene_engine.primarySlot) destination.outlet = item.slot;
|
|
29
|
+
return destination;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.defaultScreenFor = defaultScreenFor;
|
|
33
|
+
exports.navigationItemDestination = navigationItemDestination;
|
|
34
|
+
//# sourceMappingURL=navigationItemDestination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigationItemDestination.js","sources":["../../../../navigation/region/navigationItemDestination.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 { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { EffectiveNavItem, primarySlot } from '@cratis/scene.engine';\n\n/** The screen an item opens by default: the qualified id of the feature it resolves to. */\nexport function defaultScreenFor(item: EffectiveNavItem): string | undefined {\n return item.target?.resolvedFeature;\n}\n\n/**\n * Where activating a navigation item goes: its stable module/feature identity, the screen it opens, and the slot it\n * renders into as the outlet - the primary `content` slot is the host's primary outlet.\n *\n * @param item The effective item.\n * @param screenFor Names the screen an item opens; {@link defaultScreenFor} when absent.\n * @returns The destination, or undefined when the item leads nowhere.\n */\nexport function navigationItemDestination(item: EffectiveNavItem, screenFor: (item: EffectiveNavItem) => string | undefined = defaultScreenFor): DestinationReference | undefined {\n const screen = screenFor(item);\n if (screen === undefined || item.target === undefined) return undefined;\n\n const resolved = item.target.resolvedFeature;\n const destination: DestinationReference = { kind: DestinationKind.Outlet, module: item.target.module, screen };\n if (resolved !== undefined && resolved.startsWith(`${item.target.module}.`)) destination.feature = resolved.substring(item.target.module.length + 1);\n if (item.slot !== primarySlot) destination.outlet = item.slot;\n return destination;\n}\n"],"names":["defaultScreenFor","item","target","resolvedFeature","navigationItemDestination","screenFor","screen","undefined","resolved","destination","kind","DestinationKind","Outlet","module","startsWith","feature","substring","length","slot","primarySlot","outlet"],"mappings":";;;;;AAAA;AACA;AAKA,4FACO,SAASA,gBAAAA,CAAiBC,IAAsB,EAAA;IACnD,OAAOA,IAAAA,CAAKC,MAAM,EAAEC,eAAAA;AACxB;AAEA;;;;;;;AAOC,IACM,SAASC,yBAAAA,CAA0BH,IAAsB,EAAEI,YAA4DL,gBAAgB,EAAA;AAC1I,IAAA,MAAMM,SAASD,SAAAA,CAAUJ,IAAAA,CAAAA;AACzB,IAAA,IAAIK,WAAWC,SAAAA,IAAaN,IAAAA,CAAKC,MAAM,KAAKK,WAAW,OAAOA,SAAAA;AAE9D,IAAA,MAAMC,QAAAA,GAAWP,IAAAA,CAAKC,MAAM,CAACC,eAAe;AAC5C,IAAA,MAAMM,WAAAA,GAAoC;AAAEC,QAAAA,IAAAA,EAAMC,4BAAgBC,MAAM;QAAEC,MAAAA,EAAQZ,IAAAA,CAAKC,MAAM,CAACW,MAAM;AAAEP,QAAAA;AAAO,KAAA;IAC7G,IAAIE,QAAAA,KAAaD,SAAAA,IAAaC,QAAAA,CAASM,UAAU,CAAC,CAAA,EAAGb,IAAAA,CAAKC,MAAM,CAACW,MAAM,CAAC,CAAC,CAAC,GAAGJ,WAAAA,CAAYM,OAAO,GAAGP,QAAAA,CAASQ,SAAS,CAACf,IAAAA,CAAKC,MAAM,CAACW,MAAM,CAACI,MAAM,GAAG,CAAA,CAAA;IAClJ,IAAIhB,IAAAA,CAAKiB,IAAI,KAAKC,wBAAAA,EAAaV,YAAYW,MAAM,GAAGnB,KAAKiB,IAAI;IAC7D,OAAOT,WAAAA;AACX;;;"}
|
|
@@ -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,254 @@
|
|
|
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
|
+
/** The payload a form's values make: numbers and booleans as such, an empty number left out. */ function slicePayload(fields, values) {
|
|
42
|
+
const payload = {};
|
|
43
|
+
for (const field of fields){
|
|
44
|
+
const value = values[field.name];
|
|
45
|
+
const type = inputTypeOf(field);
|
|
46
|
+
if (type === 'checkbox') payload[field.name] = value === true;
|
|
47
|
+
else if (type === 'number') {
|
|
48
|
+
if (value !== undefined && value !== '') payload[field.name] = Number(value);
|
|
49
|
+
} else payload[field.name] = value ?? '';
|
|
50
|
+
}
|
|
51
|
+
return payload;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The task dialog of a toolbar slice button (TB4): a modal card with the button's title, the slice as its subtitle, a labelled
|
|
55
|
+
* field for each command property, and Cancel and a submit button labelled with the title.
|
|
56
|
+
*
|
|
57
|
+
* - The first field takes focus when it opens, Tab stays inside, Escape cancels, and focus returns to the button that opened it.
|
|
58
|
+
* - Submit validates required fields, then calls the `SliceInvoker` in scope - real, dry-run or the announcing default. The
|
|
59
|
+
* problems it reports are shown against their fields (`aria-invalid`, `aria-describedby`) or for the form; success closes the
|
|
60
|
+
* dialog and hands the result to the host (`dialogResult`).
|
|
61
|
+
* - The fields come from the `SliceCommandFieldsProvider`; the subtitle names the slice from the navigation model.
|
|
62
|
+
*
|
|
63
|
+
* Inside a navigation host it is the content of the dialog a toolbar button opens; `core:sliceDialog` registers it so a host can
|
|
64
|
+
* author the dialog template with it.
|
|
65
|
+
*/ function SliceDialogView({ slice: sliceProp, command: commandProp, title: titleProp, onClose }) {
|
|
66
|
+
const navigation = react.useContext(SceneNavigationContext.SceneNavigationContextInternal);
|
|
67
|
+
const opened = sliceDialogDestination.sliceDialogParameters(navigation?.currentDialogParameters);
|
|
68
|
+
const slice = sliceProp ?? opened?.slice ?? '';
|
|
69
|
+
const command = commandProp ?? opened?.command ?? slice;
|
|
70
|
+
const title = titleProp ?? opened?.title ?? command;
|
|
71
|
+
const invoker = SliceInvokerContext.useSliceInvoker();
|
|
72
|
+
const fieldsFor = SliceCommandFieldsContext.useSliceCommandFields();
|
|
73
|
+
const model = SceneNavigationModelContext.useSceneNavigationModel();
|
|
74
|
+
const fields = react.useMemo(()=>fieldsFor?.(command, slice) ?? [], [
|
|
75
|
+
command,
|
|
76
|
+
fieldsFor,
|
|
77
|
+
slice
|
|
78
|
+
]);
|
|
79
|
+
const sliceName = react.useMemo(()=>{
|
|
80
|
+
if (!model) return slice.split('.').pop() ?? slice;
|
|
81
|
+
const index = model.structure instanceof scene_engine.ApplicationStructureIndex ? model.structure : new scene_engine.ApplicationStructureIndex(model.structure);
|
|
82
|
+
return index.findSlice(slice)?.id ?? slice.split('.').pop() ?? slice;
|
|
83
|
+
}, [
|
|
84
|
+
model,
|
|
85
|
+
slice
|
|
86
|
+
]);
|
|
87
|
+
const [values, setValues] = react.useState({});
|
|
88
|
+
const [errors, setErrors] = react.useState([]);
|
|
89
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
90
|
+
const form = react.useRef(null);
|
|
91
|
+
const identity = react.useId();
|
|
92
|
+
const close = (result)=>onClose ? onClose(result) : navigation?.closeDialog(result);
|
|
93
|
+
// Focus the first field, and give focus back to whatever opened the dialog when it goes.
|
|
94
|
+
react.useEffect(()=>{
|
|
95
|
+
const opener = document.activeElement;
|
|
96
|
+
(form.current?.querySelector('input, select, textarea') ?? form.current?.querySelector('button'))?.focus();
|
|
97
|
+
return ()=>{
|
|
98
|
+
if (opener && opener.isConnected) opener.focus();
|
|
99
|
+
};
|
|
100
|
+
}, []);
|
|
101
|
+
const errorsOf = (name)=>errors.filter((error)=>error.field === name);
|
|
102
|
+
const fieldNames = new Set(fields.map((field)=>field.name));
|
|
103
|
+
const general = errors.filter((error)=>error.field === undefined || !fieldNames.has(error.field));
|
|
104
|
+
const submit = async (event)=>{
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
if (submitting) return;
|
|
107
|
+
const missing = fields.filter((field)=>field.required && (values[field.name] === undefined || values[field.name] === '')).map((field)=>({
|
|
108
|
+
field: field.name,
|
|
109
|
+
message: `${sliceFieldLabel(field)} is required.`
|
|
110
|
+
}));
|
|
111
|
+
setErrors(missing);
|
|
112
|
+
if (missing.length > 0) {
|
|
113
|
+
form.current?.querySelector('[aria-invalid="true"]')?.focus();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
setSubmitting(true);
|
|
117
|
+
try {
|
|
118
|
+
const result = await invoker.invoke(slice, slicePayload(fields, values), command);
|
|
119
|
+
if (result.succeeded) {
|
|
120
|
+
close(result);
|
|
121
|
+
} else {
|
|
122
|
+
const reported = result.validationErrors ?? [];
|
|
123
|
+
setErrors(reported.length > 0 || !result.message ? reported : [
|
|
124
|
+
{
|
|
125
|
+
message: result.message
|
|
126
|
+
}
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
} finally{
|
|
130
|
+
setSubmitting(false);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const onKeyDown = (event)=>{
|
|
134
|
+
if (event.key === 'Escape') {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
close();
|
|
137
|
+
} else if (event.key === 'Tab') {
|
|
138
|
+
const targets = Array.from(form.current?.querySelectorAll(focusable) ?? []);
|
|
139
|
+
if (targets.length === 0) return;
|
|
140
|
+
const first = targets[0];
|
|
141
|
+
const last = targets[targets.length - 1];
|
|
142
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
last.focus();
|
|
145
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
first.focus();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("form", {
|
|
152
|
+
ref: form,
|
|
153
|
+
className: "scene-slice-dialog",
|
|
154
|
+
"data-scene-slice-dialog": slice,
|
|
155
|
+
"data-scene-command": command,
|
|
156
|
+
noValidate: true,
|
|
157
|
+
onSubmit: submit,
|
|
158
|
+
onKeyDown: onKeyDown,
|
|
159
|
+
children: [
|
|
160
|
+
/*#__PURE__*/ jsxRuntime.jsxs("header", {
|
|
161
|
+
className: "scene-slice-dialog-header",
|
|
162
|
+
children: [
|
|
163
|
+
/*#__PURE__*/ jsxRuntime.jsx("h2", {
|
|
164
|
+
className: "scene-slice-dialog-title",
|
|
165
|
+
children: title
|
|
166
|
+
}),
|
|
167
|
+
/*#__PURE__*/ jsxRuntime.jsxs("p", {
|
|
168
|
+
className: "scene-slice-dialog-subtitle",
|
|
169
|
+
children: [
|
|
170
|
+
"State Change · ",
|
|
171
|
+
sliceName
|
|
172
|
+
]
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
}),
|
|
176
|
+
/*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
177
|
+
className: "scene-slice-dialog-fields",
|
|
178
|
+
children: fields.map((field)=>{
|
|
179
|
+
const id = `${identity}-${field.name}`;
|
|
180
|
+
const own = errorsOf(field.name);
|
|
181
|
+
const type = inputTypeOf(field);
|
|
182
|
+
const description = own.length > 0 ? `${id}-error` : undefined;
|
|
183
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("div", {
|
|
184
|
+
className: "scene-slice-dialog-field",
|
|
185
|
+
"data-type": type,
|
|
186
|
+
children: [
|
|
187
|
+
/*#__PURE__*/ jsxRuntime.jsxs("label", {
|
|
188
|
+
htmlFor: id,
|
|
189
|
+
children: [
|
|
190
|
+
sliceFieldLabel(field),
|
|
191
|
+
field.required ? /*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
192
|
+
"aria-hidden": "true",
|
|
193
|
+
children: " *"
|
|
194
|
+
}) : null
|
|
195
|
+
]
|
|
196
|
+
}),
|
|
197
|
+
/*#__PURE__*/ jsxRuntime.jsx("input", {
|
|
198
|
+
id: id,
|
|
199
|
+
name: field.name,
|
|
200
|
+
type: type,
|
|
201
|
+
required: field.required,
|
|
202
|
+
"aria-required": field.required || undefined,
|
|
203
|
+
"aria-invalid": own.length > 0 ? true : undefined,
|
|
204
|
+
"aria-describedby": description,
|
|
205
|
+
...type === 'checkbox' ? {
|
|
206
|
+
checked: values[field.name] === true
|
|
207
|
+
} : {
|
|
208
|
+
value: values[field.name] ?? ''
|
|
209
|
+
},
|
|
210
|
+
onChange: (event)=>setValues({
|
|
211
|
+
...values,
|
|
212
|
+
[field.name]: type === 'checkbox' ? event.target.checked : event.target.value
|
|
213
|
+
})
|
|
214
|
+
}),
|
|
215
|
+
own.length > 0 && /*#__PURE__*/ jsxRuntime.jsx("p", {
|
|
216
|
+
id: description,
|
|
217
|
+
className: "scene-slice-dialog-error",
|
|
218
|
+
role: "alert",
|
|
219
|
+
children: own.map((error)=>error.message).join(' ')
|
|
220
|
+
})
|
|
221
|
+
]
|
|
222
|
+
}, field.name);
|
|
223
|
+
})
|
|
224
|
+
}),
|
|
225
|
+
general.length > 0 && /*#__PURE__*/ jsxRuntime.jsx("p", {
|
|
226
|
+
className: "scene-slice-dialog-error scene-slice-dialog-summary",
|
|
227
|
+
role: "alert",
|
|
228
|
+
children: general.map((error)=>error.message).join(' ')
|
|
229
|
+
}),
|
|
230
|
+
/*#__PURE__*/ jsxRuntime.jsxs("footer", {
|
|
231
|
+
className: "scene-slice-dialog-footer",
|
|
232
|
+
children: [
|
|
233
|
+
/*#__PURE__*/ jsxRuntime.jsx("button", {
|
|
234
|
+
type: "button",
|
|
235
|
+
className: "scene-slice-dialog-cancel",
|
|
236
|
+
onClick: ()=>close(),
|
|
237
|
+
children: "Cancel"
|
|
238
|
+
}),
|
|
239
|
+
/*#__PURE__*/ jsxRuntime.jsx("button", {
|
|
240
|
+
type: "submit",
|
|
241
|
+
className: "scene-slice-dialog-submit",
|
|
242
|
+
disabled: submitting,
|
|
243
|
+
children: title
|
|
244
|
+
})
|
|
245
|
+
]
|
|
246
|
+
})
|
|
247
|
+
]
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
exports.SliceDialogView = SliceDialogView;
|
|
252
|
+
exports.sliceFieldLabel = sliceFieldLabel;
|
|
253
|
+
exports.slicePayload = slicePayload;
|
|
254
|
+
//# 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/** 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>>({});\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","slicePayload","fields","values","payload","value","undefined","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":"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"}
|
|
@@ -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;;;;"}
|