@cratis/scene.blueprint.default 4.29.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/dist/esm/shell/ToolbarSliceDialog.stories.d.ts +16 -0
- package/dist/esm/shell/ToolbarSliceDialog.stories.d.ts.map +1 -0
- package/dist/esm/shell/ToolbarSliceDialog.stories.js +31 -0
- package/dist/esm/shell/ToolbarSliceDialog.stories.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/shell/ToolbarSliceDialog.stories.tsx +56 -0
- package/shell/layout.css +173 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/scene.blueprint.default",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.30.0",
|
|
4
4
|
"description": "The default blueprint: application shell layouts, the screen and dialog templates that go inside them, the components that fill their slots, and two themes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cratis",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"build-storybook": "storybook build"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@cratis/scene.engine": "4.
|
|
58
|
-
"@cratis/scene.model": "4.
|
|
59
|
-
"@cratis/scene.react": "4.
|
|
57
|
+
"@cratis/scene.engine": "4.30.0",
|
|
58
|
+
"@cratis/scene.model": "4.30.0",
|
|
59
|
+
"@cratis/scene.react": "4.30.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@cratis/scene.engine": "4.
|
|
63
|
-
"@cratis/scene.model": "4.
|
|
64
|
-
"@cratis/scene.primereact": "4.
|
|
65
|
-
"@cratis/scene.react": "4.
|
|
62
|
+
"@cratis/scene.engine": "4.30.0",
|
|
63
|
+
"@cratis/scene.model": "4.30.0",
|
|
64
|
+
"@cratis/scene.primereact": "4.30.0",
|
|
65
|
+
"@cratis/scene.react": "4.30.0",
|
|
66
66
|
"@primereact/core": "11.2.0",
|
|
67
67
|
"@primereact/headless": "11.2.0",
|
|
68
68
|
"@primeuix/themes": "^3.0.1",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
5
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
6
|
+
import { ApplicationStructure, ExternalComponent, SceneElement } from '@cratis/scene.model';
|
|
7
|
+
import { ApplicationStructureIndex } from '@cratis/scene.engine';
|
|
8
|
+
import { SceneNavigationHost, SliceCommandFieldsProvider, SliceInvokerProvider, coreComponents, createDryRunInvoker } from '@cratis/scene.react';
|
|
9
|
+
|
|
10
|
+
const structure = {
|
|
11
|
+
modules: [{ id: 'sales', name: 'Sales', features: [{ id: 'orders', name: 'Orders', features: [], slices: [{ id: 'PlaceOrder', name: 'Place order', kind: 'StateChange', command: 'PlaceOrder' }] }] }],
|
|
12
|
+
} as unknown as ApplicationStructure;
|
|
13
|
+
|
|
14
|
+
const element = (id: string, componentName: string, properties: Record<string, unknown> = {}, slots: Record<string, SceneElement[]> = {}) =>
|
|
15
|
+
({ id, componentName, properties, slots }) as unknown as ExternalComponent;
|
|
16
|
+
|
|
17
|
+
const toolbar = element('toolbar', 'core:toolbar', { name: 'orders' }, { items: [
|
|
18
|
+
element('new', 'core:toolbarItem', { name: 'new', kind: 'Action', label: 'New order', slice: 'sales.orders.PlaceOrder', emphasis: 'primary' }),
|
|
19
|
+
element('cancel', 'core:toolbarItem', { name: 'cancel', kind: 'Action', label: 'Cancel order', slice: 'sales.orders.PlaceOrder', emphasis: 'secondary' }),
|
|
20
|
+
element('more', 'core:toolbarItem', { name: 'more', kind: 'Action', label: 'More', emphasis: 'quiet' }),
|
|
21
|
+
] });
|
|
22
|
+
|
|
23
|
+
function OrdersToolbar({ warnEmptySlices }: { warnEmptySlices: boolean }) {
|
|
24
|
+
return <SliceInvokerProvider invoker={createDryRunInvoker({ onInvoked: invocation => console.info('Dry run', invocation) })} warnEmptySlices={warnEmptySlices}>
|
|
25
|
+
<SliceCommandFieldsProvider fieldsFor={() => [{ name: 'customer', type: 'string', required: true }, { name: 'product', type: 'string' }, { name: 'quantity', type: 'number' }]}>
|
|
26
|
+
<SceneNavigationHost screens={{ Orders: toolbar }} initialScreen='Orders' registry={coreComponents}
|
|
27
|
+
navigationModel={{ structure: new ApplicationStructureIndex(structure), navigation: { application: { regions: [] }, modules: [] } }} />
|
|
28
|
+
</SliceCommandFieldsProvider>
|
|
29
|
+
</SliceInvokerProvider>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Toolbar buttons that start State Change slices: emphasis maps to primary, secondary and quiet buttons, and a button opens its
|
|
34
|
+
* slice as a task dialog whose form comes from the command and whose submit goes through the invoker in scope - here a dry run.
|
|
35
|
+
*/
|
|
36
|
+
const meta = {
|
|
37
|
+
title: 'Blueprint/Toolbar slice dialogs',
|
|
38
|
+
component: OrdersToolbar,
|
|
39
|
+
parameters: { layout: 'padded' },
|
|
40
|
+
} satisfies Meta<typeof OrdersToolbar>;
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
|
|
44
|
+
type Story = StoryObj<typeof meta>;
|
|
45
|
+
|
|
46
|
+
/** A running application: no warning badge, the dialog opens from the button. */
|
|
47
|
+
export const Running: Story = {
|
|
48
|
+
args: { warnEmptySlices: false },
|
|
49
|
+
play: async ({ canvasElement }) => {
|
|
50
|
+
await userEvent.click(within(canvasElement).getByRole('button', { name: 'New order' }));
|
|
51
|
+
await expect(await within(document.body).findByRole('dialog', { name: 'New order' })).toBeTruthy();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** A design or preview host flags the button that has no slice yet. */
|
|
56
|
+
export const Designing: Story = { args: { warnEmptySlices: true } };
|
package/shell/layout.css
CHANGED
|
@@ -886,3 +886,176 @@
|
|
|
886
886
|
outline: none;
|
|
887
887
|
box-shadow: var(--layout-focus-ring);
|
|
888
888
|
}
|
|
889
|
+
|
|
890
|
+
/* ---------------------------------------------------------------------------
|
|
891
|
+
* Toolbar slice buttons and their task dialog. The dialog is portalled by the
|
|
892
|
+
* navigation host outside the layout wrapper, so it reads the Scene tokens
|
|
893
|
+
* directly rather than the --layout-* aliases.
|
|
894
|
+
* ------------------------------------------------------------------------ */
|
|
895
|
+
|
|
896
|
+
.scene-toolbar-button {
|
|
897
|
+
display: inline-flex;
|
|
898
|
+
align-items: center;
|
|
899
|
+
gap: 0.4rem;
|
|
900
|
+
padding: 0.5rem 1rem;
|
|
901
|
+
font: inherit;
|
|
902
|
+
color: var(--scene-text-color, #1f2430);
|
|
903
|
+
cursor: pointer;
|
|
904
|
+
background: var(--scene-surface-card, #ffffff);
|
|
905
|
+
border: 1px solid var(--scene-surface-border, #d4d8de);
|
|
906
|
+
border-radius: var(--scene-content-border-radius, 0.5rem);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.scene-toolbar-button:hover {
|
|
910
|
+
background: var(--scene-surface-hover, #eceef1);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.scene-toolbar-button:focus-visible {
|
|
914
|
+
outline: none;
|
|
915
|
+
box-shadow: var(--scene-focus-ring, 0 0 0 2px rgb(79 70 229 / 0.4));
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.scene-toolbar-button-primary {
|
|
919
|
+
color: var(--scene-primary-contrast-color, #ffffff);
|
|
920
|
+
background: var(--scene-primary-color, #4f46e5);
|
|
921
|
+
border-color: var(--scene-primary-color, #4f46e5);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.scene-toolbar-button-primary:hover {
|
|
925
|
+
background: color-mix(in srgb, var(--scene-primary-color, #4f46e5) 88%, black);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.scene-toolbar-button-quiet {
|
|
929
|
+
background: transparent;
|
|
930
|
+
border-color: transparent;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.scene-toolbar-button [data-scene-badge='warning'] {
|
|
934
|
+
display: inline-grid;
|
|
935
|
+
place-items: center;
|
|
936
|
+
width: 1.1rem;
|
|
937
|
+
height: 1.1rem;
|
|
938
|
+
font-size: 0.75rem;
|
|
939
|
+
font-weight: 700;
|
|
940
|
+
color: #7a4b00;
|
|
941
|
+
background: #fcd34d;
|
|
942
|
+
border-radius: 999px;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
[data-scene-dialog]:has(> .scene-slice-dialog) {
|
|
946
|
+
position: fixed;
|
|
947
|
+
inset: 0;
|
|
948
|
+
z-index: 1200;
|
|
949
|
+
display: grid;
|
|
950
|
+
place-items: center;
|
|
951
|
+
background: rgb(15 20 30 / 0.5);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.scene-slice-dialog {
|
|
955
|
+
display: flex;
|
|
956
|
+
flex-direction: column;
|
|
957
|
+
gap: 1rem;
|
|
958
|
+
width: min(30rem, calc(100vw - 2rem));
|
|
959
|
+
padding: 1.5rem;
|
|
960
|
+
color: var(--scene-text-color, #1f2430);
|
|
961
|
+
background: var(--scene-surface-overlay, #ffffff);
|
|
962
|
+
border-radius: 1rem;
|
|
963
|
+
box-shadow: 0 1.5rem 3rem rgb(0 0 0 / 0.3);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.scene-slice-dialog-title {
|
|
967
|
+
margin: 0;
|
|
968
|
+
font-size: 1.35rem;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.scene-slice-dialog-subtitle {
|
|
972
|
+
margin: 0.25rem 0 0;
|
|
973
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
974
|
+
font-size: 0.8rem;
|
|
975
|
+
color: var(--scene-text-muted-color, #6b7280);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.scene-slice-dialog-fields {
|
|
979
|
+
display: flex;
|
|
980
|
+
flex-direction: column;
|
|
981
|
+
gap: 0.75rem;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.scene-slice-dialog-field {
|
|
985
|
+
display: flex;
|
|
986
|
+
flex-direction: column;
|
|
987
|
+
gap: 0.25rem;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.scene-slice-dialog-field label {
|
|
991
|
+
font-size: 0.875rem;
|
|
992
|
+
color: var(--scene-text-muted-color, #6b7280);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.scene-slice-dialog-field input:not([type='checkbox']) {
|
|
996
|
+
padding: 0.6rem 0.75rem;
|
|
997
|
+
font: inherit;
|
|
998
|
+
color: inherit;
|
|
999
|
+
background: var(--scene-surface-background, #f4f5f7);
|
|
1000
|
+
border: 1px solid var(--scene-surface-border, #d4d8de);
|
|
1001
|
+
border-radius: var(--scene-content-border-radius, 0.5rem);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.scene-slice-dialog-field input:focus-visible {
|
|
1005
|
+
outline: 2px solid var(--scene-primary-color, #4f46e5);
|
|
1006
|
+
outline-offset: 1px;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.scene-slice-dialog-field input[aria-invalid='true'] {
|
|
1010
|
+
border-color: #b42318;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.scene-slice-dialog-error {
|
|
1014
|
+
margin: 0;
|
|
1015
|
+
font-size: 0.8rem;
|
|
1016
|
+
color: #b42318;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.scene-slice-dialog-footer {
|
|
1020
|
+
display: flex;
|
|
1021
|
+
justify-content: flex-end;
|
|
1022
|
+
gap: 0.5rem;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
.scene-slice-dialog-cancel,
|
|
1026
|
+
.scene-slice-dialog-submit {
|
|
1027
|
+
padding: 0.55rem 1.1rem;
|
|
1028
|
+
font: inherit;
|
|
1029
|
+
cursor: pointer;
|
|
1030
|
+
border: 1px solid var(--scene-surface-border, #d4d8de);
|
|
1031
|
+
border-radius: var(--scene-content-border-radius, 0.5rem);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.scene-slice-dialog-cancel {
|
|
1035
|
+
color: inherit;
|
|
1036
|
+
background: transparent;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
.scene-slice-dialog-submit {
|
|
1040
|
+
color: var(--scene-primary-contrast-color, #ffffff);
|
|
1041
|
+
background: var(--scene-primary-color, #4f46e5);
|
|
1042
|
+
border-color: var(--scene-primary-color, #4f46e5);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.scene-slice-dialog-submit:disabled {
|
|
1046
|
+
opacity: 0.6;
|
|
1047
|
+
cursor: progress;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.scene-slice-dialog-cancel:focus-visible,
|
|
1051
|
+
.scene-slice-dialog-submit:focus-visible {
|
|
1052
|
+
outline: none;
|
|
1053
|
+
box-shadow: var(--scene-focus-ring, 0 0 0 2px rgb(79 70 229 / 0.4));
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
[data-scene-toolbar] {
|
|
1057
|
+
display: flex;
|
|
1058
|
+
flex-wrap: wrap;
|
|
1059
|
+
align-items: center;
|
|
1060
|
+
gap: 0.5rem;
|
|
1061
|
+
}
|