@cratis/scene.blueprint.default 4.35.0 → 4.37.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.
Files changed (175) hide show
  1. package/ComponentName.ts +21 -0
  2. package/defaultBlueprintComponents.ts +8 -1
  3. package/dist/cjs/ComponentName.js +7 -0
  4. package/dist/cjs/ComponentName.js.map +1 -1
  5. package/dist/cjs/defaultBlueprint.js +2 -0
  6. package/dist/cjs/defaultBlueprint.js.map +1 -1
  7. package/dist/cjs/defaultBlueprintComponentMetadata.js +2 -0
  8. package/dist/cjs/defaultBlueprintComponentMetadata.js.map +1 -1
  9. package/dist/cjs/defaultBlueprintComponents.js +15 -1
  10. package/dist/cjs/defaultBlueprintComponents.js.map +1 -1
  11. package/dist/cjs/foundation/ActivityFeed.js +122 -0
  12. package/dist/cjs/foundation/ActivityFeed.js.map +1 -0
  13. package/dist/cjs/foundation/ApprovalChainStepper.js +130 -0
  14. package/dist/cjs/foundation/ApprovalChainStepper.js.map +1 -0
  15. package/dist/cjs/foundation/AvatarStack.js +73 -0
  16. package/dist/cjs/foundation/AvatarStack.js.map +1 -0
  17. package/dist/cjs/foundation/ChecklistSection.js +174 -0
  18. package/dist/cjs/foundation/ChecklistSection.js.map +1 -0
  19. package/dist/cjs/foundation/FileUploader.js +218 -0
  20. package/dist/cjs/foundation/FileUploader.js.map +1 -0
  21. package/dist/cjs/foundation/FileUploaderHandler.js +17 -0
  22. package/dist/cjs/foundation/FileUploaderHandler.js.map +1 -0
  23. package/dist/cjs/foundation/OrgTree.js +188 -0
  24. package/dist/cjs/foundation/OrgTree.js.map +1 -0
  25. package/dist/cjs/foundation/PermissionMatrix.js +136 -0
  26. package/dist/cjs/foundation/PermissionMatrix.js.map +1 -0
  27. package/dist/cjs/foundation/activityModel.js +63 -0
  28. package/dist/cjs/foundation/activityModel.js.map +1 -0
  29. package/dist/cjs/foundation/approvalModel.js +87 -0
  30. package/dist/cjs/foundation/approvalModel.js.map +1 -0
  31. package/dist/cjs/foundation/avatarModel.js +42 -0
  32. package/dist/cjs/foundation/avatarModel.js.map +1 -0
  33. package/dist/cjs/foundation/checklistModel.js +59 -0
  34. package/dist/cjs/foundation/checklistModel.js.map +1 -0
  35. package/dist/cjs/foundation/formatting.js +11 -0
  36. package/dist/cjs/foundation/formatting.js.map +1 -1
  37. package/dist/cjs/foundation/foundationComponentMetadata.js +47 -0
  38. package/dist/cjs/foundation/foundationComponentMetadata.js.map +1 -1
  39. package/dist/cjs/foundation/foundationDescriptors.js +246 -0
  40. package/dist/cjs/foundation/foundationDescriptors.js.map +1 -1
  41. package/dist/cjs/foundation/foundationSamples.js +230 -0
  42. package/dist/cjs/foundation/foundationSamples.js.map +1 -1
  43. package/dist/cjs/foundation/foundationStrings.js +27 -1
  44. package/dist/cjs/foundation/foundationStrings.js.map +1 -1
  45. package/dist/cjs/foundation/orgModel.js +106 -0
  46. package/dist/cjs/foundation/orgModel.js.map +1 -0
  47. package/dist/cjs/foundation/permissionModel.js +84 -0
  48. package/dist/cjs/foundation/permissionModel.js.map +1 -0
  49. package/dist/cjs/foundation/uploadModel.js +71 -0
  50. package/dist/cjs/foundation/uploadModel.js.map +1 -0
  51. package/dist/cjs/index.js +59 -0
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/esm/ComponentName.d.ts +8 -1
  54. package/dist/esm/ComponentName.d.ts.map +1 -1
  55. package/dist/esm/ComponentName.js +7 -0
  56. package/dist/esm/ComponentName.js.map +1 -1
  57. package/dist/esm/defaultBlueprint.js +2 -0
  58. package/dist/esm/defaultBlueprint.js.map +1 -1
  59. package/dist/esm/defaultBlueprintComponentMetadata.js +2 -0
  60. package/dist/esm/defaultBlueprintComponentMetadata.js.map +1 -1
  61. package/dist/esm/defaultBlueprintComponents.d.ts.map +1 -1
  62. package/dist/esm/defaultBlueprintComponents.js +15 -1
  63. package/dist/esm/defaultBlueprintComponents.js.map +1 -1
  64. package/dist/esm/foundation/ActivityFeed.d.ts +3 -0
  65. package/dist/esm/foundation/ActivityFeed.d.ts.map +1 -0
  66. package/dist/esm/foundation/ActivityFeed.js +120 -0
  67. package/dist/esm/foundation/ActivityFeed.js.map +1 -0
  68. package/dist/esm/foundation/ApprovalChainStepper.d.ts +3 -0
  69. package/dist/esm/foundation/ApprovalChainStepper.d.ts.map +1 -0
  70. package/dist/esm/foundation/ApprovalChainStepper.js +128 -0
  71. package/dist/esm/foundation/ApprovalChainStepper.js.map +1 -0
  72. package/dist/esm/foundation/AvatarStack.d.ts +3 -0
  73. package/dist/esm/foundation/AvatarStack.d.ts.map +1 -0
  74. package/dist/esm/foundation/AvatarStack.js +71 -0
  75. package/dist/esm/foundation/AvatarStack.js.map +1 -0
  76. package/dist/esm/foundation/ChecklistSection.d.ts +3 -0
  77. package/dist/esm/foundation/ChecklistSection.d.ts.map +1 -0
  78. package/dist/esm/foundation/ChecklistSection.js +172 -0
  79. package/dist/esm/foundation/ChecklistSection.js.map +1 -0
  80. package/dist/esm/foundation/FileUploader.d.ts +3 -0
  81. package/dist/esm/foundation/FileUploader.d.ts.map +1 -0
  82. package/dist/esm/foundation/FileUploader.js +216 -0
  83. package/dist/esm/foundation/FileUploader.js.map +1 -0
  84. package/dist/esm/foundation/FileUploaderHandler.d.ts +8 -0
  85. package/dist/esm/foundation/FileUploaderHandler.d.ts.map +1 -0
  86. package/dist/esm/foundation/FileUploaderHandler.js +14 -0
  87. package/dist/esm/foundation/FileUploaderHandler.js.map +1 -0
  88. package/dist/esm/foundation/Foundation.stories.d.ts +16 -0
  89. package/dist/esm/foundation/Foundation.stories.d.ts.map +1 -1
  90. package/dist/esm/foundation/Foundation.stories.js +16 -0
  91. package/dist/esm/foundation/Foundation.stories.js.map +1 -1
  92. package/dist/esm/foundation/OrgTree.d.ts +3 -0
  93. package/dist/esm/foundation/OrgTree.d.ts.map +1 -0
  94. package/dist/esm/foundation/OrgTree.js +186 -0
  95. package/dist/esm/foundation/OrgTree.js.map +1 -0
  96. package/dist/esm/foundation/PermissionMatrix.d.ts +3 -0
  97. package/dist/esm/foundation/PermissionMatrix.d.ts.map +1 -0
  98. package/dist/esm/foundation/PermissionMatrix.js +134 -0
  99. package/dist/esm/foundation/PermissionMatrix.js.map +1 -0
  100. package/dist/esm/foundation/activityModel.d.ts +22 -0
  101. package/dist/esm/foundation/activityModel.d.ts.map +1 -0
  102. package/dist/esm/foundation/activityModel.js +58 -0
  103. package/dist/esm/foundation/activityModel.js.map +1 -0
  104. package/dist/esm/foundation/approvalModel.d.ts +27 -0
  105. package/dist/esm/foundation/approvalModel.d.ts.map +1 -0
  106. package/dist/esm/foundation/approvalModel.js +81 -0
  107. package/dist/esm/foundation/approvalModel.js.map +1 -0
  108. package/dist/esm/foundation/avatarModel.d.ts +11 -0
  109. package/dist/esm/foundation/avatarModel.d.ts.map +1 -0
  110. package/dist/esm/foundation/avatarModel.js +38 -0
  111. package/dist/esm/foundation/avatarModel.js.map +1 -0
  112. package/dist/esm/foundation/checklistModel.d.ts +17 -0
  113. package/dist/esm/foundation/checklistModel.d.ts.map +1 -0
  114. package/dist/esm/foundation/checklistModel.js +53 -0
  115. package/dist/esm/foundation/checklistModel.js.map +1 -0
  116. package/dist/esm/foundation/formatting.d.ts +1 -0
  117. package/dist/esm/foundation/formatting.d.ts.map +1 -1
  118. package/dist/esm/foundation/formatting.js +11 -1
  119. package/dist/esm/foundation/formatting.js.map +1 -1
  120. package/dist/esm/foundation/foundationComponentMetadata.d.ts.map +1 -1
  121. package/dist/esm/foundation/foundationComponentMetadata.js +47 -0
  122. package/dist/esm/foundation/foundationComponentMetadata.js.map +1 -1
  123. package/dist/esm/foundation/foundationDescriptors.d.ts.map +1 -1
  124. package/dist/esm/foundation/foundationDescriptors.js +246 -0
  125. package/dist/esm/foundation/foundationDescriptors.js.map +1 -1
  126. package/dist/esm/foundation/foundationSamples.d.ts.map +1 -1
  127. package/dist/esm/foundation/foundationSamples.js +230 -0
  128. package/dist/esm/foundation/foundationSamples.js.map +1 -1
  129. package/dist/esm/foundation/foundationStrings.d.ts +26 -0
  130. package/dist/esm/foundation/foundationStrings.d.ts.map +1 -1
  131. package/dist/esm/foundation/foundationStrings.js +27 -1
  132. package/dist/esm/foundation/foundationStrings.js.map +1 -1
  133. package/dist/esm/foundation/index.d.ts +15 -0
  134. package/dist/esm/foundation/index.d.ts.map +1 -1
  135. package/dist/esm/foundation/index.js +15 -0
  136. package/dist/esm/foundation/index.js.map +1 -1
  137. package/dist/esm/foundation/orgModel.d.ts +20 -0
  138. package/dist/esm/foundation/orgModel.d.ts.map +1 -0
  139. package/dist/esm/foundation/orgModel.js +99 -0
  140. package/dist/esm/foundation/orgModel.js.map +1 -0
  141. package/dist/esm/foundation/permissionModel.d.ts +24 -0
  142. package/dist/esm/foundation/permissionModel.d.ts.map +1 -0
  143. package/dist/esm/foundation/permissionModel.js +77 -0
  144. package/dist/esm/foundation/permissionModel.js.map +1 -0
  145. package/dist/esm/foundation/uploadModel.d.ts +31 -0
  146. package/dist/esm/foundation/uploadModel.d.ts.map +1 -0
  147. package/dist/esm/foundation/uploadModel.js +65 -0
  148. package/dist/esm/foundation/uploadModel.js.map +1 -0
  149. package/dist/esm/index.js +16 -1
  150. package/dist/esm/index.js.map +1 -1
  151. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  152. package/foundation/ActivityFeed.tsx +61 -0
  153. package/foundation/ApprovalChainStepper.tsx +66 -0
  154. package/foundation/AvatarStack.tsx +43 -0
  155. package/foundation/ChecklistSection.tsx +80 -0
  156. package/foundation/FileUploader.tsx +122 -0
  157. package/foundation/FileUploaderHandler.tsx +22 -0
  158. package/foundation/Foundation.stories.tsx +16 -0
  159. package/foundation/OrgTree.tsx +101 -0
  160. package/foundation/PermissionMatrix.tsx +82 -0
  161. package/foundation/activityModel.ts +65 -0
  162. package/foundation/approvalModel.ts +88 -0
  163. package/foundation/avatarModel.ts +36 -0
  164. package/foundation/checklistModel.ts +62 -0
  165. package/foundation/formatting.ts +9 -0
  166. package/foundation/foundationComponentMetadata.ts +7 -0
  167. package/foundation/foundationDescriptors.ts +153 -0
  168. package/foundation/foundationSamples.ts +59 -0
  169. package/foundation/foundationStrings.ts +26 -0
  170. package/foundation/index.ts +15 -0
  171. package/foundation/orgModel.ts +85 -0
  172. package/foundation/permissionModel.ts +85 -0
  173. package/foundation/uploadModel.ts +66 -0
  174. package/package.json +8 -8
  175. package/shell/layout.css +418 -0
@@ -0,0 +1,85 @@
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
+ /** A person or unit in an organization, with who they report to. */
5
+ export interface OrgNode {
6
+ id: string;
7
+ label: string;
8
+ title?: string;
9
+ parentId?: string;
10
+ }
11
+
12
+ /** A node placed in the tree. */
13
+ export interface OrgTreeNode extends OrgNode {
14
+ children: OrgTreeNode[];
15
+ depth: number;
16
+ }
17
+
18
+ /** Reads nodes out of a property value, dropping entries without an id and keeping the first of a repeated id. */
19
+ export function readOrgNodes(value: unknown): OrgNode[] {
20
+ if (!Array.isArray(value)) return [];
21
+
22
+ const seen = new Set<string>();
23
+ return value.flatMap((entry): OrgNode[] => {
24
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];
25
+ const { id, label, title, parentId } = entry as Record<string, unknown>;
26
+ if (typeof id !== 'string' || id.length === 0 || seen.has(id)) return [];
27
+ seen.add(id);
28
+ return [{ id, label: typeof label === 'string' && label.length > 0 ? label : id, ...(typeof title === 'string' ? { title } : {}), ...(typeof parentId === 'string' && parentId.length > 0 ? { parentId } : {}) }];
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Builds the tree from the flat rows a query returns.
34
+ *
35
+ * Someone whose manager is not in the data becomes a root rather than vanishing, and a reporting cycle is broken at the node that closes it
36
+ * - which becomes a root - so malformed data draws something and never loops. Siblings keep the order of the rows.
37
+ */
38
+ export function buildOrgTree(nodes: OrgNode[]): OrgTreeNode[] {
39
+ const known = new Set(nodes.map(node => node.id));
40
+ const parentOf = new Map<string, string | undefined>(nodes.map(node => [node.id, node.parentId !== undefined && known.has(node.parentId) && node.parentId !== node.id ? node.parentId : undefined]));
41
+
42
+ for (const node of nodes) {
43
+ const path = new Set<string>([node.id]);
44
+ for (let current = parentOf.get(node.id); current !== undefined; current = parentOf.get(current)) {
45
+ if (path.has(current)) { parentOf.set(node.id, undefined); break; }
46
+ path.add(current);
47
+ }
48
+ }
49
+
50
+ const placed = new Map<string, OrgTreeNode>(nodes.map(node => [node.id, { ...node, children: [], depth: 0 }]));
51
+ const roots: OrgTreeNode[] = [];
52
+ for (const node of nodes) {
53
+ const parent = parentOf.get(node.id);
54
+ if (parent === undefined) roots.push(placed.get(node.id)!);
55
+ else placed.get(parent)!.children.push(placed.get(node.id)!);
56
+ }
57
+
58
+ const assignDepth = (level: OrgTreeNode[], depth: number) => level.forEach(node => { node.depth = depth; assignDepth(node.children, depth + 1); });
59
+ assignDepth(roots, 1);
60
+ return roots;
61
+ }
62
+
63
+ /** The nodes that can be reached from the roots with the given expansion, in the order they are drawn. */
64
+ export function visibleNodes(roots: OrgTreeNode[], expanded: ReadonlySet<string>): OrgTreeNode[] {
65
+ return roots.flatMap(node => [node, ...(expanded.has(node.id) ? visibleNodes(node.children, expanded) : [])]);
66
+ }
67
+
68
+ /** The ids of every node down to a depth: what is expanded when the tree first opens. */
69
+ export function expandedToDepth(roots: OrgTreeNode[], depth: number): Set<string> {
70
+ const result = new Set<string>();
71
+ const walk = (level: OrgTreeNode[]) => level.forEach(node => { if (node.depth < depth && node.children.length > 0) result.add(node.id); walk(node.children); });
72
+ walk(roots);
73
+ return result;
74
+ }
75
+
76
+ /** The number of people below a node, at any depth. */
77
+ export const descendantCount = (node: OrgTreeNode): number => node.children.reduce((sum, child) => sum + 1 + descendantCount(child), 0);
78
+
79
+ /** For each node, the nodes that share its parent (itself included, in order) and its parent: what a tree needs for its set size and position, and for Left to go up. */
80
+ export function placements(roots: OrgTreeNode[]): Map<string, { siblings: OrgTreeNode[]; parent?: OrgTreeNode }> {
81
+ const result = new Map<string, { siblings: OrgTreeNode[]; parent?: OrgTreeNode }>();
82
+ const walk = (level: OrgTreeNode[], parent?: OrgTreeNode) => level.forEach(node => { result.set(node.id, { siblings: level, parent }); walk(node.children, node); });
83
+ walk(roots);
84
+ return result;
85
+ }
@@ -0,0 +1,85 @@
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
+ /** A role: a column of the matrix. */
5
+ export interface MatrixRole {
6
+ id: string;
7
+ label: string;
8
+ }
9
+
10
+ /** A permission: a row of the matrix, optionally in a named group. */
11
+ export interface MatrixPermission {
12
+ id: string;
13
+ label: string;
14
+ group?: string;
15
+ }
16
+
17
+ /** One role holding one permission. */
18
+ export interface MatrixGrant {
19
+ role: string;
20
+ permission: string;
21
+ }
22
+
23
+ /** A named run of permissions, for the group heading rows. */
24
+ export interface MatrixSection {
25
+ group?: string;
26
+ permissions: MatrixPermission[];
27
+ }
28
+
29
+ const records = (value: unknown): Record<string, unknown>[] =>
30
+ Array.isArray(value) ? value.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null && !Array.isArray(entry)) : [];
31
+
32
+ /** Reads roles, dropping those without an id and repeated ids. */
33
+ export function readRoles(value: unknown): MatrixRole[] {
34
+ const seen = new Set<string>();
35
+ return records(value).flatMap(entry => {
36
+ if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id)) return [];
37
+ seen.add(entry.id);
38
+ return [{ id: entry.id, label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.id }];
39
+ });
40
+ }
41
+
42
+ /** Reads permissions, dropping those without an id and repeated ids. */
43
+ export function readPermissions(value: unknown): MatrixPermission[] {
44
+ const seen = new Set<string>();
45
+ return records(value).flatMap(entry => {
46
+ if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id)) return [];
47
+ seen.add(entry.id);
48
+ return [{ id: entry.id, label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.id, ...(typeof entry.group === 'string' && entry.group.length > 0 ? { group: entry.group } : {}) }];
49
+ });
50
+ }
51
+
52
+ /** Reads grants, keeping each pair once and only for a role and a permission that exist: a grant of something that was deleted is not shown as held. */
53
+ export function readGrants(value: unknown, roles: MatrixRole[], permissions: MatrixPermission[]): MatrixGrant[] {
54
+ const roleIds = new Set(roles.map(role => role.id));
55
+ const permissionIds = new Set(permissions.map(permission => permission.id));
56
+ const seen = new Set<string>();
57
+
58
+ return records(value).flatMap(entry => {
59
+ const { role, permission } = entry;
60
+ if (typeof role !== 'string' || typeof permission !== 'string' || !roleIds.has(role) || !permissionIds.has(permission)) return [];
61
+ const key = `${role}\u0000${permission}`;
62
+ if (seen.has(key)) return [];
63
+ seen.add(key);
64
+ return [{ role, permission }];
65
+ });
66
+ }
67
+
68
+ /** Whether a role holds a permission. */
69
+ export const isGranted = (grants: MatrixGrant[], role: string, permission: string): boolean => grants.some(grant => grant.role === role && grant.permission === permission);
70
+
71
+ /** Grants a permission to a role, or takes it away, leaving every other grant as it was. */
72
+ export const setGrant = (grants: MatrixGrant[], role: string, permission: string, granted: boolean): MatrixGrant[] =>
73
+ granted ? (isGranted(grants, role, permission) ? grants : [...grants, { role, permission }]) : grants.filter(grant => !(grant.role === role && grant.permission === permission));
74
+
75
+ /** Groups the permissions into runs by their group, in the order first seen, with the ungrouped ones in a run of their own. */
76
+ export function sectionsOf(permissions: MatrixPermission[]): MatrixSection[] {
77
+ const sections: MatrixSection[] = [];
78
+ for (const permission of permissions) {
79
+ const section = sections.find(candidate => candidate.group === permission.group);
80
+ if (section) section.permissions.push(permission);
81
+ else sections.push({ ...(permission.group === undefined ? {} : { group: permission.group }), permissions: [permission] });
82
+ }
83
+
84
+ return sections;
85
+ }
@@ -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.35.0",
3
+ "version": "4.37.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.35.0",
59
- "@cratis/scene.model": "4.35.0",
60
- "@cratis/scene.react": "4.35.0"
58
+ "@cratis/scene.engine": "4.37.0",
59
+ "@cratis/scene.model": "4.37.0",
60
+ "@cratis/scene.react": "4.37.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@cratis/scene.engine": "4.35.0",
64
- "@cratis/scene.model": "4.35.0",
65
- "@cratis/scene.primereact": "4.35.0",
66
- "@cratis/scene.react": "4.35.0",
63
+ "@cratis/scene.engine": "4.37.0",
64
+ "@cratis/scene.model": "4.37.0",
65
+ "@cratis/scene.primereact": "4.37.0",
66
+ "@cratis/scene.react": "4.37.0",
67
67
  "@primereact/core": "11.2.0",
68
68
  "@primereact/headless": "11.2.0",
69
69
  "@primeuix/themes": "^3.0.1",
package/shell/layout.css CHANGED
@@ -1607,3 +1607,421 @@
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
+ }
1844
+
1845
+ /* Approval chain. */
1846
+ .foundation-approval-chain {
1847
+ padding: var(--foundation-space-4);
1848
+ border: 1px solid var(--foundation-border);
1849
+ border-radius: var(--foundation-radius);
1850
+ background: var(--foundation-surface);
1851
+ }
1852
+
1853
+ .foundation-approval-outcome {
1854
+ margin-block: 0 var(--foundation-space-3);
1855
+ font-weight: 700;
1856
+ }
1857
+
1858
+ .foundation-approval-steps {
1859
+ display: flex;
1860
+ flex-wrap: wrap;
1861
+ gap: var(--foundation-space-4);
1862
+ margin: 0;
1863
+ padding: 0;
1864
+ list-style: none;
1865
+ }
1866
+
1867
+ .foundation-approval-step {
1868
+ position: relative;
1869
+ display: flex;
1870
+ flex: 1 1 10rem;
1871
+ flex-direction: column;
1872
+ align-items: flex-start;
1873
+ gap: var(--foundation-space-1);
1874
+ min-inline-size: 0;
1875
+ padding-block-start: var(--foundation-space-3);
1876
+ border-block-start: 3px solid var(--foundation-border);
1877
+ }
1878
+
1879
+ .foundation-approval-step[data-state='approved'] { border-block-start-color: var(--foundation-success); }
1880
+ .foundation-approval-step[data-state='current'] { border-block-start-color: var(--foundation-info); }
1881
+ .foundation-approval-step[data-state='rejected'] { border-block-start-color: var(--foundation-danger); }
1882
+
1883
+ .foundation-approval-comment {
1884
+ overflow-wrap: anywhere;
1885
+ font-style: italic;
1886
+ }
1887
+
1888
+ .foundation-approval-steps[data-orientation='vertical'] {
1889
+ flex-direction: column;
1890
+ }
1891
+
1892
+ .foundation-approval-steps[data-orientation='vertical'] .foundation-approval-step {
1893
+ flex-basis: auto;
1894
+ padding-block-start: 0;
1895
+ padding-inline-start: var(--foundation-space-3);
1896
+ border-block-start: 0;
1897
+ border-inline-start: 3px solid var(--foundation-border);
1898
+ }
1899
+
1900
+ .foundation-approval-steps[data-orientation='vertical'] .foundation-approval-step[data-state='approved'] { border-inline-start-color: var(--foundation-success); }
1901
+ .foundation-approval-steps[data-orientation='vertical'] .foundation-approval-step[data-state='current'] { border-inline-start-color: var(--foundation-info); }
1902
+ .foundation-approval-steps[data-orientation='vertical'] .foundation-approval-step[data-state='rejected'] { border-inline-start-color: var(--foundation-danger); }
1903
+
1904
+ @media (max-width: 40rem) {
1905
+ .foundation-approval-steps {
1906
+ flex-direction: column;
1907
+ }
1908
+
1909
+ .foundation-approval-step {
1910
+ flex-basis: auto;
1911
+ padding-block-start: 0;
1912
+ padding-inline-start: var(--foundation-space-3);
1913
+ border-block-start: 0;
1914
+ border-inline-start: 3px solid var(--foundation-border);
1915
+ }
1916
+
1917
+ .foundation-approval-step[data-state='approved'] { border-inline-start-color: var(--foundation-success); }
1918
+ .foundation-approval-step[data-state='current'] { border-inline-start-color: var(--foundation-info); }
1919
+ .foundation-approval-step[data-state='rejected'] { border-inline-start-color: var(--foundation-danger); }
1920
+ }
1921
+
1922
+ /* Org tree. */
1923
+ .foundation-org-tree {
1924
+ padding: var(--foundation-space-2);
1925
+ border: 1px solid var(--foundation-border);
1926
+ border-radius: var(--foundation-radius);
1927
+ background: var(--foundation-surface);
1928
+ }
1929
+
1930
+ .foundation-tree {
1931
+ display: flex;
1932
+ flex-direction: column;
1933
+ margin: 0;
1934
+ padding: 0;
1935
+ list-style: none;
1936
+ }
1937
+
1938
+ .foundation-tree-item {
1939
+ display: flex;
1940
+ align-items: center;
1941
+ gap: var(--foundation-space-2);
1942
+ min-block-size: var(--foundation-row-height);
1943
+ padding-inline-end: var(--foundation-space-2);
1944
+ border-radius: var(--foundation-radius);
1945
+ cursor: pointer;
1946
+ }
1947
+
1948
+ .foundation-tree-item:hover {
1949
+ background: var(--foundation-hover);
1950
+ }
1951
+
1952
+ .foundation-tree-item[data-selected='true'] {
1953
+ background: var(--foundation-highlight);
1954
+ color: var(--foundation-highlight-text);
1955
+ box-shadow: inset 3px 0 0 var(--foundation-primary);
1956
+ }
1957
+
1958
+ .foundation-tree-text {
1959
+ display: flex;
1960
+ flex: 1;
1961
+ flex-direction: column;
1962
+ min-inline-size: 0;
1963
+ }
1964
+
1965
+ .foundation-tree-spacer {
1966
+ flex: none;
1967
+ inline-size: 2.25rem;
1968
+ }
1969
+
1970
+ .foundation-tree-item[data-selected='true'] .foundation-count {
1971
+ color: inherit;
1972
+ }
1973
+
1974
+ /* Permission matrix. */
1975
+ .foundation-permission-matrix {
1976
+ border: 1px solid var(--foundation-border);
1977
+ border-radius: var(--foundation-radius);
1978
+ background: var(--foundation-surface);
1979
+ }
1980
+
1981
+ .foundation-matrix-scroll {
1982
+ overflow-x: auto;
1983
+ }
1984
+
1985
+ .foundation-matrix {
1986
+ inline-size: 100%;
1987
+ border-collapse: collapse;
1988
+ }
1989
+
1990
+ .foundation-matrix th,
1991
+ .foundation-matrix td {
1992
+ min-block-size: var(--foundation-row-height);
1993
+ padding: var(--foundation-space-2) var(--foundation-space-3);
1994
+ border-block-end: 1px solid var(--foundation-border);
1995
+ text-align: center;
1996
+ white-space: nowrap;
1997
+ }
1998
+
1999
+ .foundation-matrix th[scope='row'],
2000
+ .foundation-matrix thead th:first-child {
2001
+ position: sticky;
2002
+ inset-inline-start: 0;
2003
+ background: var(--foundation-surface);
2004
+ text-align: start;
2005
+ }
2006
+
2007
+ .foundation-matrix-group th {
2008
+ background: var(--foundation-background);
2009
+ color: var(--foundation-muted);
2010
+ font-size: 0.875rem;
2011
+ text-align: start;
2012
+ }
2013
+
2014
+ .foundation-matrix input[type='checkbox'] {
2015
+ inline-size: 1.25rem;
2016
+ block-size: 1.25rem;
2017
+ }
2018
+
2019
+ .foundation-matrix td[data-granted='true'] .foundation-matrix-state {
2020
+ color: var(--foundation-success);
2021
+ }
2022
+
2023
+ .foundation-matrix-state {
2024
+ display: inline-flex;
2025
+ align-items: center;
2026
+ justify-content: center;
2027
+ }