@cratis/scene.blueprint.default 4.34.0 → 4.36.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/ComponentName.ts +12 -0
- package/defaultBlueprintComponents.ts +5 -1
- package/dist/cjs/ComponentName.js +4 -0
- package/dist/cjs/ComponentName.js.map +1 -1
- package/dist/cjs/defaultBlueprint.js +1 -0
- package/dist/cjs/defaultBlueprint.js.map +1 -1
- package/dist/cjs/defaultBlueprintComponentMetadata.js +1 -0
- package/dist/cjs/defaultBlueprintComponentMetadata.js.map +1 -1
- package/dist/cjs/defaultBlueprintComponents.js +9 -1
- package/dist/cjs/defaultBlueprintComponents.js.map +1 -1
- package/dist/cjs/foundation/ActivityFeed.js +122 -0
- package/dist/cjs/foundation/ActivityFeed.js.map +1 -0
- package/dist/cjs/foundation/AvatarStack.js +73 -0
- package/dist/cjs/foundation/AvatarStack.js.map +1 -0
- package/dist/cjs/foundation/ChecklistSection.js +174 -0
- package/dist/cjs/foundation/ChecklistSection.js.map +1 -0
- package/dist/cjs/foundation/FileUploader.js +218 -0
- package/dist/cjs/foundation/FileUploader.js.map +1 -0
- package/dist/cjs/foundation/FileUploaderHandler.js +17 -0
- package/dist/cjs/foundation/FileUploaderHandler.js.map +1 -0
- package/dist/cjs/foundation/activityModel.js +63 -0
- package/dist/cjs/foundation/activityModel.js.map +1 -0
- package/dist/cjs/foundation/avatarModel.js +42 -0
- package/dist/cjs/foundation/avatarModel.js.map +1 -0
- package/dist/cjs/foundation/checklistModel.js +59 -0
- package/dist/cjs/foundation/checklistModel.js.map +1 -0
- package/dist/cjs/foundation/formatting.js +11 -0
- package/dist/cjs/foundation/formatting.js.map +1 -1
- package/dist/cjs/foundation/foundationComponentMetadata.js +26 -0
- package/dist/cjs/foundation/foundationComponentMetadata.js.map +1 -1
- package/dist/cjs/foundation/foundationDescriptors.js +128 -0
- package/dist/cjs/foundation/foundationDescriptors.js.map +1 -1
- package/dist/cjs/foundation/foundationSamples.js +114 -0
- package/dist/cjs/foundation/foundationSamples.js.map +1 -1
- package/dist/cjs/foundation/foundationStrings.js +15 -0
- package/dist/cjs/foundation/foundationStrings.js.map +1 -1
- package/dist/cjs/foundation/uploadModel.js +71 -0
- package/dist/cjs/foundation/uploadModel.js.map +1 -0
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/preview-host/ApplicationHost.js +48 -27
- package/dist/cjs/preview-host/ApplicationHost.js.map +1 -1
- package/dist/esm/ComponentName.d.ts +5 -1
- package/dist/esm/ComponentName.d.ts.map +1 -1
- package/dist/esm/ComponentName.js +4 -0
- package/dist/esm/ComponentName.js.map +1 -1
- package/dist/esm/defaultBlueprint.js +1 -0
- package/dist/esm/defaultBlueprint.js.map +1 -1
- package/dist/esm/defaultBlueprintComponentMetadata.js +1 -0
- package/dist/esm/defaultBlueprintComponentMetadata.js.map +1 -1
- package/dist/esm/defaultBlueprintComponents.d.ts.map +1 -1
- package/dist/esm/defaultBlueprintComponents.js +9 -1
- package/dist/esm/defaultBlueprintComponents.js.map +1 -1
- package/dist/esm/foundation/ActivityFeed.d.ts +3 -0
- package/dist/esm/foundation/ActivityFeed.d.ts.map +1 -0
- package/dist/esm/foundation/ActivityFeed.js +120 -0
- package/dist/esm/foundation/ActivityFeed.js.map +1 -0
- package/dist/esm/foundation/AvatarStack.d.ts +3 -0
- package/dist/esm/foundation/AvatarStack.d.ts.map +1 -0
- package/dist/esm/foundation/AvatarStack.js +71 -0
- package/dist/esm/foundation/AvatarStack.js.map +1 -0
- package/dist/esm/foundation/ChecklistSection.d.ts +3 -0
- package/dist/esm/foundation/ChecklistSection.d.ts.map +1 -0
- package/dist/esm/foundation/ChecklistSection.js +172 -0
- package/dist/esm/foundation/ChecklistSection.js.map +1 -0
- package/dist/esm/foundation/FileUploader.d.ts +3 -0
- package/dist/esm/foundation/FileUploader.d.ts.map +1 -0
- package/dist/esm/foundation/FileUploader.js +216 -0
- package/dist/esm/foundation/FileUploader.js.map +1 -0
- package/dist/esm/foundation/FileUploaderHandler.d.ts +8 -0
- package/dist/esm/foundation/FileUploaderHandler.d.ts.map +1 -0
- package/dist/esm/foundation/FileUploaderHandler.js +14 -0
- package/dist/esm/foundation/FileUploaderHandler.js.map +1 -0
- package/dist/esm/foundation/Foundation.stories.d.ts +8 -0
- package/dist/esm/foundation/Foundation.stories.d.ts.map +1 -1
- package/dist/esm/foundation/Foundation.stories.js +8 -0
- package/dist/esm/foundation/Foundation.stories.js.map +1 -1
- package/dist/esm/foundation/activityModel.d.ts +22 -0
- package/dist/esm/foundation/activityModel.d.ts.map +1 -0
- package/dist/esm/foundation/activityModel.js +58 -0
- package/dist/esm/foundation/activityModel.js.map +1 -0
- package/dist/esm/foundation/avatarModel.d.ts +11 -0
- package/dist/esm/foundation/avatarModel.d.ts.map +1 -0
- package/dist/esm/foundation/avatarModel.js +38 -0
- package/dist/esm/foundation/avatarModel.js.map +1 -0
- package/dist/esm/foundation/checklistModel.d.ts +17 -0
- package/dist/esm/foundation/checklistModel.d.ts.map +1 -0
- package/dist/esm/foundation/checklistModel.js +53 -0
- package/dist/esm/foundation/checklistModel.js.map +1 -0
- package/dist/esm/foundation/formatting.d.ts +1 -0
- package/dist/esm/foundation/formatting.d.ts.map +1 -1
- package/dist/esm/foundation/formatting.js +11 -1
- package/dist/esm/foundation/formatting.js.map +1 -1
- package/dist/esm/foundation/foundationComponentMetadata.d.ts.map +1 -1
- package/dist/esm/foundation/foundationComponentMetadata.js +26 -0
- package/dist/esm/foundation/foundationComponentMetadata.js.map +1 -1
- package/dist/esm/foundation/foundationDescriptors.d.ts.map +1 -1
- package/dist/esm/foundation/foundationDescriptors.js +128 -0
- package/dist/esm/foundation/foundationDescriptors.js.map +1 -1
- package/dist/esm/foundation/foundationSamples.d.ts.map +1 -1
- package/dist/esm/foundation/foundationSamples.js +114 -0
- package/dist/esm/foundation/foundationSamples.js.map +1 -1
- package/dist/esm/foundation/foundationStrings.d.ts +15 -0
- package/dist/esm/foundation/foundationStrings.d.ts.map +1 -1
- package/dist/esm/foundation/foundationStrings.js +15 -0
- package/dist/esm/foundation/foundationStrings.js.map +1 -1
- package/dist/esm/foundation/index.d.ts +9 -0
- package/dist/esm/foundation/index.d.ts.map +1 -1
- package/dist/esm/foundation/index.js +9 -0
- package/dist/esm/foundation/index.js.map +1 -1
- package/dist/esm/foundation/uploadModel.d.ts +31 -0
- package/dist/esm/foundation/uploadModel.d.ts.map +1 -0
- package/dist/esm/foundation/uploadModel.js +65 -0
- package/dist/esm/foundation/uploadModel.js.map +1 -0
- package/dist/esm/index.js +10 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/preview-host/ApplicationHost.d.ts +5 -2
- package/dist/esm/preview-host/ApplicationHost.d.ts.map +1 -1
- package/dist/esm/preview-host/ApplicationHost.js +50 -29
- package/dist/esm/preview-host/ApplicationHost.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/foundation/ActivityFeed.tsx +61 -0
- package/foundation/AvatarStack.tsx +43 -0
- package/foundation/ChecklistSection.tsx +80 -0
- package/foundation/FileUploader.tsx +122 -0
- package/foundation/FileUploaderHandler.tsx +22 -0
- package/foundation/Foundation.stories.tsx +8 -0
- package/foundation/activityModel.ts +65 -0
- package/foundation/avatarModel.ts +36 -0
- package/foundation/checklistModel.ts +62 -0
- package/foundation/formatting.ts +9 -0
- package/foundation/foundationComponentMetadata.ts +4 -0
- package/foundation/foundationDescriptors.ts +79 -0
- package/foundation/foundationSamples.ts +29 -0
- package/foundation/foundationStrings.ts +15 -0
- package/foundation/index.ts +9 -0
- package/foundation/uploadModel.ts +66 -0
- package/package.json +8 -8
- package/preview-host/ApplicationHost.tsx +57 -16
- package/shell/layout.css +234 -0
|
@@ -49,6 +49,35 @@ export const foundationSamples: Record<string, FoundationSample> = {
|
|
|
49
49
|
properties: { views: [{ id: 'all', label: 'All leads' }, { id: 'mine', label: 'Mine' }, { id: 'overdue', label: 'Overdue' }], activeView: 'mine' },
|
|
50
50
|
data: ['Mine'], stateful: true,
|
|
51
51
|
},
|
|
52
|
+
[ComponentName.ChecklistSection]: {
|
|
53
|
+
properties: {
|
|
54
|
+
today: '2026-03-10', locale: 'en-US',
|
|
55
|
+
sections: [
|
|
56
|
+
{ title: 'Before day one', items: [{ id: 't1', label: 'Send the contract', owner: 'Grace', due: '2026-03-01', done: true }, { id: 't2', label: 'Order a laptop', owner: 'Linus', due: '2026-03-05' }] },
|
|
57
|
+
{ title: 'First week', items: [{ id: 't3', label: 'Security training', owner: 'Ada', due: '2026-03-20' }] },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
data: ['Send the contract', '1 of 2 complete'], stateful: true,
|
|
61
|
+
},
|
|
62
|
+
[ComponentName.ActivityFeed]: {
|
|
63
|
+
properties: {
|
|
64
|
+
locale: 'en-US', timeZone: 'UTC',
|
|
65
|
+
entries: [
|
|
66
|
+
{ id: 'a1', actor: 'Ada Lovelace', action: 'commented', kind: 'comment', at: '2026-03-10T09:30:00Z', body: 'Sent the revised quote.' },
|
|
67
|
+
{ id: 'a2', actor: 'Grace Hopper', action: 'changed the owner to Linus', kind: 'change', at: '2026-03-09T16:00:00Z' },
|
|
68
|
+
{ id: 'a3', actor: 'System', action: 'marked the deal as qualified', kind: 'status', at: '2026-03-09T08:00:00Z' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
data: ['Sent the revised quote.'], stateful: true,
|
|
72
|
+
},
|
|
73
|
+
[ComponentName.AvatarStack]: {
|
|
74
|
+
properties: { people: [{ name: 'Ada Lovelace' }, { name: 'Grace Hopper' }, { name: 'Linus Torvalds' }, { name: 'Margaret Hamilton' }, { name: 'Alan Turing' }, { name: 'Barbara Liskov' }], max: 4 },
|
|
75
|
+
data: ['+2'], stateful: true,
|
|
76
|
+
},
|
|
77
|
+
[ComponentName.FileUploader]: {
|
|
78
|
+
properties: { accept: '.pdf, image/*', maxSizeMb: 10, multiple: true },
|
|
79
|
+
data: [], stateful: true,
|
|
80
|
+
},
|
|
52
81
|
};
|
|
53
82
|
|
|
54
83
|
/** A scene element for a foundation component, populated with its sample unless properties are given, with the state forced when asked. */
|
|
@@ -80,11 +80,26 @@ export const foundationStrings = {
|
|
|
80
80
|
viewsNone: 'There are no saved views',
|
|
81
81
|
checklistProgress: '{done} of {total} complete',
|
|
82
82
|
checklistOwner: 'Owner',
|
|
83
|
+
checklistOverdue: 'Overdue',
|
|
84
|
+
checklistDone: 'Done',
|
|
85
|
+
checklistOpen: 'Open',
|
|
86
|
+
checklistItemProgress: 'Progress of {title}',
|
|
87
|
+
checklistNone: 'This checklist has no tasks',
|
|
83
88
|
checklistDue: 'Due',
|
|
84
89
|
activityEmpty: 'No activity yet',
|
|
90
|
+
activityComment: 'Comment',
|
|
91
|
+
activityChange: 'Change',
|
|
92
|
+
activityStatus: 'Status',
|
|
93
|
+
activitySystem: 'System',
|
|
94
|
+
activityBy: 'by {actor}',
|
|
85
95
|
avatarOverflow: '{count} more',
|
|
86
96
|
avatarPeople: '{count} people',
|
|
87
97
|
uploadDrop: 'Drop files here or',
|
|
98
|
+
uploadLimits: 'Up to {limit} each',
|
|
99
|
+
uploadAccepts: 'Accepts {types}',
|
|
100
|
+
uploadSelected: 'Selected files',
|
|
101
|
+
uploadQueued: 'Ready to upload',
|
|
102
|
+
uploadSingle: 'Only one file can be uploaded at a time',
|
|
88
103
|
uploadBrowse: 'Browse files',
|
|
89
104
|
uploadRemove: 'Remove {name}',
|
|
90
105
|
uploadTooLarge: '{name} is larger than {limit}',
|
package/foundation/index.ts
CHANGED
|
@@ -21,6 +21,15 @@ export * from './columnModel';
|
|
|
21
21
|
export * from './ColumnChooser';
|
|
22
22
|
export * from './viewModel';
|
|
23
23
|
export * from './SavedViewsMenu';
|
|
24
|
+
export * from './checklistModel';
|
|
25
|
+
export * from './ChecklistSection';
|
|
26
|
+
export * from './activityModel';
|
|
27
|
+
export * from './ActivityFeed';
|
|
28
|
+
export * from './avatarModel';
|
|
29
|
+
export * from './AvatarStack';
|
|
30
|
+
export * from './uploadModel';
|
|
31
|
+
export * from './FileUploaderHandler';
|
|
32
|
+
export * from './FileUploader';
|
|
24
33
|
export * from './foundationDescriptors';
|
|
25
34
|
export * from './foundationComponentMetadata';
|
|
26
35
|
export * from './foundationSamples';
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
/** Where a file is in being uploaded. */
|
|
5
|
+
export enum UploadStatus {
|
|
6
|
+
Queued = 'queued',
|
|
7
|
+
Uploading = 'uploading',
|
|
8
|
+
Done = 'done',
|
|
9
|
+
Failed = 'failed',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** The rules a selection is checked against before anything is sent. */
|
|
13
|
+
export interface UploadRules {
|
|
14
|
+
/** A comma-separated `accept` list: extensions (`.pdf`), MIME types (`image/png`) and wildcards (`image/*`). */
|
|
15
|
+
accept: string;
|
|
16
|
+
maximumBytes: number;
|
|
17
|
+
multiple: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** What the checker needs of a file. */
|
|
21
|
+
export interface SelectedFile {
|
|
22
|
+
name: string;
|
|
23
|
+
size: number;
|
|
24
|
+
type: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Why a file was refused. */
|
|
28
|
+
export enum UploadRefusal {
|
|
29
|
+
WrongType = 'wrongType',
|
|
30
|
+
TooLarge = 'tooLarge',
|
|
31
|
+
Single = 'single',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Whether a file matches an `accept` list; an empty list accepts everything, and matching ignores case. */
|
|
35
|
+
export function matchesAccept(file: SelectedFile, accept: string): boolean {
|
|
36
|
+
const patterns = accept.split(',').map(pattern => pattern.trim().toLowerCase()).filter(pattern => pattern.length > 0);
|
|
37
|
+
if (patterns.length === 0) return true;
|
|
38
|
+
|
|
39
|
+
const name = file.name.toLowerCase();
|
|
40
|
+
const type = file.type.toLowerCase();
|
|
41
|
+
return patterns.some(pattern => pattern.startsWith('.') ? name.endsWith(pattern) : pattern.endsWith('/*') ? type.startsWith(pattern.slice(0, -1)) : type === pattern);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Sorts a selection into the files to take and the reasons for refusing the others. Only the first is taken when one file is allowed. */
|
|
45
|
+
export function checkSelection<T extends SelectedFile>(files: T[], rules: UploadRules, alreadySelected = 0): { accepted: T[]; refused: { file: T; reason: UploadRefusal }[] } {
|
|
46
|
+
const accepted: T[] = [];
|
|
47
|
+
const refused: { file: T; reason: UploadRefusal }[] = [];
|
|
48
|
+
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
if (!matchesAccept(file, rules.accept)) refused.push({ file, reason: UploadRefusal.WrongType });
|
|
51
|
+
else if (rules.maximumBytes > 0 && file.size > rules.maximumBytes) refused.push({ file, reason: UploadRefusal.TooLarge });
|
|
52
|
+
else if (!rules.multiple && alreadySelected + accepted.length >= 1) refused.push({ file, reason: UploadRefusal.Single });
|
|
53
|
+
else accepted.push(file);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { accepted, refused };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A file size in the largest unit that keeps it under 1024, for the locale. */
|
|
60
|
+
export function formatBytes(bytes: number, locale: string): string {
|
|
61
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
62
|
+
let value = bytes;
|
|
63
|
+
let unit = 0;
|
|
64
|
+
while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit++; }
|
|
65
|
+
return `${new Intl.NumberFormat(locale, { maximumFractionDigits: unit === 0 ? 0 : 1 }).format(value)} ${units[unit]}`;
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/scene.blueprint.default",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.36.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",
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
"build-storybook": "storybook build"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@cratis/scene.engine": "4.
|
|
59
|
-
"@cratis/scene.model": "4.
|
|
60
|
-
"@cratis/scene.react": "4.
|
|
58
|
+
"@cratis/scene.engine": "4.36.0",
|
|
59
|
+
"@cratis/scene.model": "4.36.0",
|
|
60
|
+
"@cratis/scene.react": "4.36.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@cratis/scene.engine": "4.
|
|
64
|
-
"@cratis/scene.model": "4.
|
|
65
|
-
"@cratis/scene.primereact": "4.
|
|
66
|
-
"@cratis/scene.react": "4.
|
|
63
|
+
"@cratis/scene.engine": "4.36.0",
|
|
64
|
+
"@cratis/scene.model": "4.36.0",
|
|
65
|
+
"@cratis/scene.primereact": "4.36.0",
|
|
66
|
+
"@cratis/scene.react": "4.36.0",
|
|
67
67
|
"@primereact/core": "11.2.0",
|
|
68
68
|
"@primereact/headless": "11.2.0",
|
|
69
69
|
"@primeuix/themes": "^3.0.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { ReactNode, useMemo, useState } from 'react';
|
|
5
|
-
import { SceneElement, type EffectiveConfiguration } from '@cratis/scene.model';
|
|
5
|
+
import { DialogTemplate, SceneElement, type EffectiveConfiguration } from '@cratis/scene.model';
|
|
6
6
|
import { BindingResolver, BindingScope } from '@cratis/scene.engine';
|
|
7
7
|
import {
|
|
8
8
|
ComponentRegistry, SceneElementView, SceneHistory, SceneNavigationContextValue, SceneNavigationModel, SceneNavigationModelProvider,
|
|
@@ -50,6 +50,30 @@ export interface ApplicationHostProps {
|
|
|
50
50
|
*/
|
|
51
51
|
renderDialog?(navigation: SceneNavigationContextValue): ReactNode;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Draws a dialog the document declares, in place of the host's plain one - a host that wants its own backdrop, a close button
|
|
55
|
+
* and keyboard handling around the template's content. Called inside {@link scope}.
|
|
56
|
+
* @param template The dialog template that is open.
|
|
57
|
+
* @param content The template's composed content.
|
|
58
|
+
* @param navigation Where the user is.
|
|
59
|
+
*/
|
|
60
|
+
renderDialogTemplate?(template: DialogTemplate, content: SceneElement, navigation: SceneNavigationContextValue): ReactNode;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* What a host provides around everything that renders - the screen on the page and the dialog over it: its data, its command
|
|
64
|
+
* dispatcher, its chrome state. Inside the navigation providers, so it can read where the user is. Stage puts its data and
|
|
65
|
+
* interaction scope here, so the screen, a dialog template and a slice dialog all share them.
|
|
66
|
+
* @param navigation Where the user is.
|
|
67
|
+
* @param children The screen and the dialog.
|
|
68
|
+
*/
|
|
69
|
+
scope?(navigation: SceneNavigationContextValue, children: ReactNode): ReactNode;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Where the shell keeps its layout configuration - the sidebar's state, the chosen theme. A preview keeps none (null, the default);
|
|
73
|
+
* a running application passes its storage so what a user chose is still chosen on the next visit.
|
|
74
|
+
*/
|
|
75
|
+
layoutConfigStorage?: Storage | null;
|
|
76
|
+
|
|
53
77
|
/** Rendered inside the host's providers, after the application. */
|
|
54
78
|
children?: ReactNode;
|
|
55
79
|
}
|
|
@@ -61,7 +85,7 @@ export interface ApplicationHostProps {
|
|
|
61
85
|
* button or a dialog destination opened shown over it - one place for all of it (spec PRV9), so a behaviour that differs between
|
|
62
86
|
* the two hosts is a bug in the host, not a second implementation.
|
|
63
87
|
*/
|
|
64
|
-
export function ApplicationHost({ application, composition = {}, navigationModel, registry: hostRegistry, initialScreen, history, resolveBinding, queryResults, renderScreen, renderDialog, children }: ApplicationHostProps) {
|
|
88
|
+
export function ApplicationHost({ application, composition = {}, navigationModel, registry: hostRegistry, initialScreen, history, resolveBinding, queryResults, renderScreen, renderDialog, renderDialogTemplate, scope, layoutConfigStorage = null, children }: ApplicationHostProps) {
|
|
65
89
|
const [memoryHistory] = useState(() => createMemorySceneHistory());
|
|
66
90
|
const registry = useMemo(() => ({ ...previewComponents, ...hostRegistry }), [hostRegistry]);
|
|
67
91
|
const hostHistory = history ?? memoryHistory;
|
|
@@ -86,25 +110,37 @@ export function ApplicationHost({ application, composition = {}, navigationModel
|
|
|
86
110
|
const sliceDialog = !dialogTemplate && navigation.currentDialog && sliceDialogParameters(navigation.currentDialogParameters) !== undefined;
|
|
87
111
|
const dialogName = sliceDialogParameters(navigation.currentDialogParameters)?.title ?? navigation.currentDialog;
|
|
88
112
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
const dialogContent = dialogTemplate ? composeDialog(application, dialogTemplate, composition) : undefined;
|
|
114
|
+
|
|
115
|
+
const body = (
|
|
116
|
+
<>
|
|
117
|
+
<div data-scene-outlet='primary' data-scene-url={navigation.currentUrl} data-scene-screen={navigation.primaryScreen}>
|
|
118
|
+
{composed && <Drawn>{renderScreen
|
|
119
|
+
? () => renderScreen(composed.element, composed.configuration, navigation)
|
|
120
|
+
: () => <SceneElementView element={composed.element} registry={registry} resolveBinding={resolveBinding} queryResults={queryResults} configuration={composed.configuration} />}</Drawn>}
|
|
121
|
+
</div>
|
|
122
|
+
{dialogTemplate && dialogContent && (renderDialogTemplate
|
|
123
|
+
? <Drawn>{() => renderDialogTemplate(dialogTemplate, dialogContent, navigation)}</Drawn>
|
|
124
|
+
: (
|
|
125
|
+
<div role='dialog' aria-modal='true' aria-label={dialogName} data-scene-dialog={navigation.currentDialog}>
|
|
126
|
+
<SceneElementView element={dialogContent} registry={registry} resolveBinding={resolveBinding} queryResults={queryResults} />
|
|
127
|
+
</div>
|
|
128
|
+
))}
|
|
129
|
+
{sliceDialog && (
|
|
130
|
+
<div role='dialog' aria-modal='true' aria-label={dialogName} data-scene-dialog={navigation.currentDialog}>
|
|
131
|
+
{renderDialog?.(navigation) ?? <SliceDialogView />}
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
93
136
|
|
|
94
137
|
return (
|
|
95
138
|
<SceneNavigationProvider value={navigation}>
|
|
96
139
|
<SceneOutletContext.Provider value={outletContext}>
|
|
97
140
|
<SceneNavigationModelProvider model={navigationModel}>
|
|
98
|
-
{/* The shell reads its layout configuration from a provider; nothing is saved
|
|
99
|
-
<LayoutConfigProvider storage={
|
|
100
|
-
|
|
101
|
-
{(dialogTemplate || sliceDialog) && (
|
|
102
|
-
<div role='dialog' aria-modal='true' aria-label={dialogName} data-scene-dialog={navigation.currentDialog}>
|
|
103
|
-
{dialogTemplate
|
|
104
|
-
? <SceneElementView element={composeDialog(application, dialogTemplate, composition)} registry={registry} resolveBinding={resolveBinding} queryResults={queryResults} />
|
|
105
|
-
: renderDialog?.(navigation) ?? <SliceDialogView />}
|
|
106
|
-
</div>
|
|
107
|
-
)}
|
|
141
|
+
{/* The shell reads its layout configuration from a provider; nothing is saved unless the host gives it somewhere to. */}
|
|
142
|
+
<LayoutConfigProvider storage={layoutConfigStorage}>
|
|
143
|
+
{scope ? scope(navigation, body) : body}
|
|
108
144
|
{children}
|
|
109
145
|
</LayoutConfigProvider>
|
|
110
146
|
</SceneNavigationModelProvider>
|
|
@@ -112,3 +148,8 @@ export function ApplicationHost({ application, composition = {}, navigationModel
|
|
|
112
148
|
</SceneNavigationProvider>
|
|
113
149
|
);
|
|
114
150
|
}
|
|
151
|
+
|
|
152
|
+
/** Calls what a host draws as part of this component, so a hook it uses runs under the providers the host put in scope. */
|
|
153
|
+
function Drawn({ children }: { children(): ReactNode }) {
|
|
154
|
+
return <>{children()}</>;
|
|
155
|
+
}
|
package/shell/layout.css
CHANGED
|
@@ -1607,3 +1607,237 @@
|
|
|
1607
1607
|
grid-row: 1;
|
|
1608
1608
|
}
|
|
1609
1609
|
}
|
|
1610
|
+
|
|
1611
|
+
/* Checklist. */
|
|
1612
|
+
.foundation-checklist {
|
|
1613
|
+
display: flex;
|
|
1614
|
+
flex-direction: column;
|
|
1615
|
+
gap: var(--foundation-space-4);
|
|
1616
|
+
padding: var(--foundation-space-4);
|
|
1617
|
+
border: 1px solid var(--foundation-border);
|
|
1618
|
+
border-radius: var(--foundation-radius);
|
|
1619
|
+
background: var(--foundation-surface);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.foundation-checklist-header {
|
|
1623
|
+
display: flex;
|
|
1624
|
+
align-items: baseline;
|
|
1625
|
+
justify-content: space-between;
|
|
1626
|
+
gap: var(--foundation-space-2);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.foundation-checklist-header h3 {
|
|
1630
|
+
margin: 0;
|
|
1631
|
+
font-size: 1rem;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
.foundation-progress {
|
|
1635
|
+
overflow: hidden;
|
|
1636
|
+
block-size: 0.5rem;
|
|
1637
|
+
margin-block: var(--foundation-space-2);
|
|
1638
|
+
border-radius: 999px;
|
|
1639
|
+
background: var(--foundation-hover);
|
|
1640
|
+
border: 1px solid var(--foundation-border);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
.foundation-progress-bar {
|
|
1644
|
+
display: block;
|
|
1645
|
+
block-size: 100%;
|
|
1646
|
+
background: var(--foundation-primary);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.foundation-checklist-items {
|
|
1650
|
+
display: flex;
|
|
1651
|
+
flex-direction: column;
|
|
1652
|
+
margin: 0;
|
|
1653
|
+
padding: 0;
|
|
1654
|
+
list-style: none;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
.foundation-checklist-item {
|
|
1658
|
+
display: flex;
|
|
1659
|
+
flex-wrap: wrap;
|
|
1660
|
+
align-items: center;
|
|
1661
|
+
justify-content: space-between;
|
|
1662
|
+
gap: var(--foundation-space-1) var(--foundation-space-4);
|
|
1663
|
+
min-block-size: var(--foundation-row-height);
|
|
1664
|
+
border-block-end: 1px solid var(--foundation-border);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
.foundation-checklist-item[data-done='true'] .foundation-checklist-label {
|
|
1668
|
+
color: var(--foundation-muted);
|
|
1669
|
+
text-decoration: line-through;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
.foundation-checklist-meta {
|
|
1673
|
+
display: inline-flex;
|
|
1674
|
+
flex-wrap: wrap;
|
|
1675
|
+
gap: var(--foundation-space-1) var(--foundation-space-4);
|
|
1676
|
+
color: var(--foundation-muted);
|
|
1677
|
+
font-size: 0.875rem;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
.foundation-overdue {
|
|
1681
|
+
color: var(--foundation-danger);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
/* Activity feed. */
|
|
1685
|
+
.foundation-activity-feed {
|
|
1686
|
+
padding: var(--foundation-space-4);
|
|
1687
|
+
border: 1px solid var(--foundation-border);
|
|
1688
|
+
border-radius: var(--foundation-radius);
|
|
1689
|
+
background: var(--foundation-surface);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
.foundation-activity-heading {
|
|
1693
|
+
margin: var(--foundation-space-3) 0;
|
|
1694
|
+
color: var(--foundation-muted);
|
|
1695
|
+
font-size: 0.875rem;
|
|
1696
|
+
font-weight: 600;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
.foundation-activity-list {
|
|
1700
|
+
display: flex;
|
|
1701
|
+
flex-direction: column;
|
|
1702
|
+
gap: var(--foundation-space-3);
|
|
1703
|
+
margin: 0;
|
|
1704
|
+
padding: 0;
|
|
1705
|
+
list-style: none;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.foundation-activity-entry {
|
|
1709
|
+
display: flex;
|
|
1710
|
+
gap: var(--foundation-space-3);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
.foundation-activity-icon {
|
|
1714
|
+
display: inline-flex;
|
|
1715
|
+
flex: none;
|
|
1716
|
+
align-items: center;
|
|
1717
|
+
justify-content: center;
|
|
1718
|
+
inline-size: 2rem;
|
|
1719
|
+
block-size: 2rem;
|
|
1720
|
+
border-radius: 50%;
|
|
1721
|
+
background: var(--foundation-highlight);
|
|
1722
|
+
color: var(--foundation-highlight-text);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
.foundation-activity-content {
|
|
1726
|
+
display: flex;
|
|
1727
|
+
flex-direction: column;
|
|
1728
|
+
gap: var(--foundation-space-1);
|
|
1729
|
+
min-inline-size: 0;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
.foundation-activity-summary,
|
|
1733
|
+
.foundation-activity-body {
|
|
1734
|
+
margin: 0;
|
|
1735
|
+
overflow-wrap: anywhere;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
.foundation-activity-body {
|
|
1739
|
+
padding: var(--foundation-space-2) var(--foundation-space-3);
|
|
1740
|
+
border-radius: var(--foundation-radius);
|
|
1741
|
+
background: var(--foundation-background);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/* Avatar stack. */
|
|
1745
|
+
.foundation-avatars {
|
|
1746
|
+
display: inline-flex;
|
|
1747
|
+
margin: 0;
|
|
1748
|
+
padding: 0;
|
|
1749
|
+
list-style: none;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
.foundation-avatar {
|
|
1753
|
+
display: inline-flex;
|
|
1754
|
+
flex: none;
|
|
1755
|
+
align-items: center;
|
|
1756
|
+
justify-content: center;
|
|
1757
|
+
overflow: hidden;
|
|
1758
|
+
inline-size: 2.5rem;
|
|
1759
|
+
block-size: 2.5rem;
|
|
1760
|
+
border: 2px solid var(--foundation-surface);
|
|
1761
|
+
border-radius: 50%;
|
|
1762
|
+
background: var(--foundation-highlight);
|
|
1763
|
+
color: var(--foundation-highlight-text);
|
|
1764
|
+
font-size: 0.875rem;
|
|
1765
|
+
font-weight: 700;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
.foundation-avatar + .foundation-avatar {
|
|
1769
|
+
margin-inline-start: calc(var(--foundation-space-3) * -1);
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
.foundation-avatar img {
|
|
1773
|
+
inline-size: 100%;
|
|
1774
|
+
block-size: 100%;
|
|
1775
|
+
object-fit: cover;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.foundation-avatar-more {
|
|
1779
|
+
background: var(--foundation-hover);
|
|
1780
|
+
color: var(--foundation-text);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
.foundation-avatars[data-size='small'] .foundation-avatar {
|
|
1784
|
+
inline-size: 1.75rem;
|
|
1785
|
+
block-size: 1.75rem;
|
|
1786
|
+
font-size: 0.75rem;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/* File uploader. */
|
|
1790
|
+
.foundation-file-uploader {
|
|
1791
|
+
display: flex;
|
|
1792
|
+
flex-direction: column;
|
|
1793
|
+
gap: var(--foundation-space-3);
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
.foundation-dropzone {
|
|
1797
|
+
display: flex;
|
|
1798
|
+
flex-direction: column;
|
|
1799
|
+
align-items: center;
|
|
1800
|
+
gap: var(--foundation-space-2);
|
|
1801
|
+
padding: var(--foundation-space-6);
|
|
1802
|
+
border: 2px dashed var(--foundation-border);
|
|
1803
|
+
border-radius: var(--foundation-radius);
|
|
1804
|
+
background: var(--foundation-surface);
|
|
1805
|
+
text-align: center;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
.foundation-dropzone[data-dragging='true'] {
|
|
1809
|
+
border-color: var(--foundation-primary);
|
|
1810
|
+
background: var(--foundation-highlight);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
.foundation-upload-problems {
|
|
1814
|
+
color: var(--foundation-danger);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
.foundation-upload-list {
|
|
1818
|
+
display: flex;
|
|
1819
|
+
flex-direction: column;
|
|
1820
|
+
margin: 0;
|
|
1821
|
+
padding: 0;
|
|
1822
|
+
list-style: none;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
.foundation-upload-item {
|
|
1826
|
+
display: flex;
|
|
1827
|
+
flex-wrap: wrap;
|
|
1828
|
+
align-items: center;
|
|
1829
|
+
gap: var(--foundation-space-2);
|
|
1830
|
+
min-block-size: var(--foundation-row-height);
|
|
1831
|
+
border-block-end: 1px solid var(--foundation-border);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
.foundation-upload-name {
|
|
1835
|
+
flex: 1;
|
|
1836
|
+
min-inline-size: 0;
|
|
1837
|
+
overflow-wrap: anywhere;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
.foundation-upload-item[data-status='failed'] .foundation-upload-status {
|
|
1841
|
+
color: var(--foundation-danger);
|
|
1842
|
+
font-weight: 600;
|
|
1843
|
+
}
|