@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.
- package/ComponentName.ts +21 -0
- package/defaultBlueprintComponents.ts +8 -1
- package/dist/cjs/ComponentName.js +7 -0
- package/dist/cjs/ComponentName.js.map +1 -1
- package/dist/cjs/defaultBlueprint.js +2 -0
- package/dist/cjs/defaultBlueprint.js.map +1 -1
- package/dist/cjs/defaultBlueprintComponentMetadata.js +2 -0
- package/dist/cjs/defaultBlueprintComponentMetadata.js.map +1 -1
- package/dist/cjs/defaultBlueprintComponents.js +15 -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/ApprovalChainStepper.js +130 -0
- package/dist/cjs/foundation/ApprovalChainStepper.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/OrgTree.js +188 -0
- package/dist/cjs/foundation/OrgTree.js.map +1 -0
- package/dist/cjs/foundation/PermissionMatrix.js +136 -0
- package/dist/cjs/foundation/PermissionMatrix.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/approvalModel.js +87 -0
- package/dist/cjs/foundation/approvalModel.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 +47 -0
- package/dist/cjs/foundation/foundationComponentMetadata.js.map +1 -1
- package/dist/cjs/foundation/foundationDescriptors.js +246 -0
- package/dist/cjs/foundation/foundationDescriptors.js.map +1 -1
- package/dist/cjs/foundation/foundationSamples.js +230 -0
- package/dist/cjs/foundation/foundationSamples.js.map +1 -1
- package/dist/cjs/foundation/foundationStrings.js +27 -1
- package/dist/cjs/foundation/foundationStrings.js.map +1 -1
- package/dist/cjs/foundation/orgModel.js +106 -0
- package/dist/cjs/foundation/orgModel.js.map +1 -0
- package/dist/cjs/foundation/permissionModel.js +84 -0
- package/dist/cjs/foundation/permissionModel.js.map +1 -0
- package/dist/cjs/foundation/uploadModel.js +71 -0
- package/dist/cjs/foundation/uploadModel.js.map +1 -0
- package/dist/cjs/index.js +59 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/ComponentName.d.ts +8 -1
- package/dist/esm/ComponentName.d.ts.map +1 -1
- package/dist/esm/ComponentName.js +7 -0
- package/dist/esm/ComponentName.js.map +1 -1
- package/dist/esm/defaultBlueprint.js +2 -0
- package/dist/esm/defaultBlueprint.js.map +1 -1
- package/dist/esm/defaultBlueprintComponentMetadata.js +2 -0
- package/dist/esm/defaultBlueprintComponentMetadata.js.map +1 -1
- package/dist/esm/defaultBlueprintComponents.d.ts.map +1 -1
- package/dist/esm/defaultBlueprintComponents.js +15 -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/ApprovalChainStepper.d.ts +3 -0
- package/dist/esm/foundation/ApprovalChainStepper.d.ts.map +1 -0
- package/dist/esm/foundation/ApprovalChainStepper.js +128 -0
- package/dist/esm/foundation/ApprovalChainStepper.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 +16 -0
- package/dist/esm/foundation/Foundation.stories.d.ts.map +1 -1
- package/dist/esm/foundation/Foundation.stories.js +16 -0
- package/dist/esm/foundation/Foundation.stories.js.map +1 -1
- package/dist/esm/foundation/OrgTree.d.ts +3 -0
- package/dist/esm/foundation/OrgTree.d.ts.map +1 -0
- package/dist/esm/foundation/OrgTree.js +186 -0
- package/dist/esm/foundation/OrgTree.js.map +1 -0
- package/dist/esm/foundation/PermissionMatrix.d.ts +3 -0
- package/dist/esm/foundation/PermissionMatrix.d.ts.map +1 -0
- package/dist/esm/foundation/PermissionMatrix.js +134 -0
- package/dist/esm/foundation/PermissionMatrix.js.map +1 -0
- 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/approvalModel.d.ts +27 -0
- package/dist/esm/foundation/approvalModel.d.ts.map +1 -0
- package/dist/esm/foundation/approvalModel.js +81 -0
- package/dist/esm/foundation/approvalModel.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 +47 -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 +246 -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 +230 -0
- package/dist/esm/foundation/foundationSamples.js.map +1 -1
- package/dist/esm/foundation/foundationStrings.d.ts +26 -0
- package/dist/esm/foundation/foundationStrings.d.ts.map +1 -1
- package/dist/esm/foundation/foundationStrings.js +27 -1
- package/dist/esm/foundation/foundationStrings.js.map +1 -1
- package/dist/esm/foundation/index.d.ts +15 -0
- package/dist/esm/foundation/index.d.ts.map +1 -1
- package/dist/esm/foundation/index.js +15 -0
- package/dist/esm/foundation/index.js.map +1 -1
- package/dist/esm/foundation/orgModel.d.ts +20 -0
- package/dist/esm/foundation/orgModel.d.ts.map +1 -0
- package/dist/esm/foundation/orgModel.js +99 -0
- package/dist/esm/foundation/orgModel.js.map +1 -0
- package/dist/esm/foundation/permissionModel.d.ts +24 -0
- package/dist/esm/foundation/permissionModel.d.ts.map +1 -0
- package/dist/esm/foundation/permissionModel.js +77 -0
- package/dist/esm/foundation/permissionModel.js.map +1 -0
- 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 +16 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/foundation/ActivityFeed.tsx +61 -0
- package/foundation/ApprovalChainStepper.tsx +66 -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 +16 -0
- package/foundation/OrgTree.tsx +101 -0
- package/foundation/PermissionMatrix.tsx +82 -0
- package/foundation/activityModel.ts +65 -0
- package/foundation/approvalModel.ts +88 -0
- package/foundation/avatarModel.ts +36 -0
- package/foundation/checklistModel.ts +62 -0
- package/foundation/formatting.ts +9 -0
- package/foundation/foundationComponentMetadata.ts +7 -0
- package/foundation/foundationDescriptors.ts +153 -0
- package/foundation/foundationSamples.ts +59 -0
- package/foundation/foundationStrings.ts +26 -0
- package/foundation/index.ts +15 -0
- package/foundation/orgModel.ts +85 -0
- package/foundation/permissionModel.ts +85 -0
- package/foundation/uploadModel.ts +66 -0
- package/package.json +8 -8
- package/shell/layout.css +418 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var scene_react = require('@cratis/scene.react');
|
|
6
|
+
var ComponentState = require('./ComponentState.js');
|
|
7
|
+
var FoundationIcon = require('./FoundationIcon.js');
|
|
8
|
+
var foundationStrings = require('./foundationStrings.js');
|
|
9
|
+
var orgModel = require('./orgModel.js');
|
|
10
|
+
var StateFrame = require('./StateFrame.js');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The `orgTree` component: an organization as an expandable tree of people and units.
|
|
14
|
+
*
|
|
15
|
+
* `nodes` are flat rows - `id`, `label`, `title`, `parentId` - as a query returns them; the tree is built from them, so someone whose manager is
|
|
16
|
+
* missing becomes a root and a reporting cycle is cut instead of looping. It follows the ARIA tree pattern: one tab stop, Up and Down move,
|
|
17
|
+
* Right expands or enters, Left collapses or goes to the parent, Home and End jump, Enter or Space selects. Each item says how many reports it
|
|
18
|
+
* has in words. The selected id is published as the `value` output; `expandedDepth` is how many levels are open at first.
|
|
19
|
+
*/ function OrgTree({ element, interactions, bindingOutputs }) {
|
|
20
|
+
const nodes = react.useMemo(()=>orgModel.readOrgNodes(element.properties.nodes), [
|
|
21
|
+
element.properties.nodes
|
|
22
|
+
]);
|
|
23
|
+
const roots = react.useMemo(()=>orgModel.buildOrgTree(nodes), [
|
|
24
|
+
nodes
|
|
25
|
+
]);
|
|
26
|
+
const depth = typeof element.properties.expandedDepth === 'number' ? element.properties.expandedDepth : 2;
|
|
27
|
+
const initial = react.useMemo(()=>orgModel.expandedToDepth(roots, depth), [
|
|
28
|
+
roots,
|
|
29
|
+
depth
|
|
30
|
+
]);
|
|
31
|
+
const [expanded, setExpanded] = react.useState(initial);
|
|
32
|
+
const [selected, setSelected] = react.useState(typeof element.properties.selected === 'string' ? element.properties.selected : undefined);
|
|
33
|
+
const [focused, setFocused] = react.useState(undefined);
|
|
34
|
+
const strings = foundationStrings.readFoundationStrings(element);
|
|
35
|
+
const state = ComponentState.readComponentState(element, nodes.length > 0);
|
|
36
|
+
const visible = orgModel.visibleNodes(roots, expanded);
|
|
37
|
+
const placed = react.useMemo(()=>orgModel.placements(roots), [
|
|
38
|
+
roots
|
|
39
|
+
]);
|
|
40
|
+
const tabStop = visible.find((node)=>node.id === focused)?.id ?? visible.find((node)=>node.id === selected)?.id ?? visible[0]?.id;
|
|
41
|
+
const emptyElement = state === ComponentState.ComponentState.Empty ? {
|
|
42
|
+
...element,
|
|
43
|
+
properties: {
|
|
44
|
+
emptyHeadline: strings.orgNone,
|
|
45
|
+
...element.properties
|
|
46
|
+
}
|
|
47
|
+
} : element;
|
|
48
|
+
react.useEffect(()=>setExpanded(initial), [
|
|
49
|
+
initial
|
|
50
|
+
]);
|
|
51
|
+
scene_react.useValueOutput(bindingOutputs, selected);
|
|
52
|
+
const move = (node, event)=>{
|
|
53
|
+
const index = visible.indexOf(node);
|
|
54
|
+
const focus = (target)=>{
|
|
55
|
+
if (!target) return;
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
setFocused(target.id);
|
|
58
|
+
requestAnimationFrame(()=>document.getElementById(`${element.id}-${target.id}`)?.focus());
|
|
59
|
+
};
|
|
60
|
+
const toggle = (open)=>setExpanded((current)=>{
|
|
61
|
+
const next = new Set(current);
|
|
62
|
+
if (open) next.add(node.id);
|
|
63
|
+
else next.delete(node.id);
|
|
64
|
+
return next;
|
|
65
|
+
});
|
|
66
|
+
const parent = placed.get(node.id)?.parent;
|
|
67
|
+
switch(event.key){
|
|
68
|
+
case 'ArrowDown':
|
|
69
|
+
focus(visible[index + 1]);
|
|
70
|
+
break;
|
|
71
|
+
case 'ArrowUp':
|
|
72
|
+
focus(visible[index - 1]);
|
|
73
|
+
break;
|
|
74
|
+
case 'Home':
|
|
75
|
+
focus(visible[0]);
|
|
76
|
+
break;
|
|
77
|
+
case 'End':
|
|
78
|
+
focus(visible[visible.length - 1]);
|
|
79
|
+
break;
|
|
80
|
+
case 'ArrowRight':
|
|
81
|
+
if (node.children.length > 0 && !expanded.has(node.id)) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
toggle(true);
|
|
84
|
+
} else focus(node.children[0]);
|
|
85
|
+
break;
|
|
86
|
+
case 'ArrowLeft':
|
|
87
|
+
if (node.children.length > 0 && expanded.has(node.id)) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
toggle(false);
|
|
90
|
+
} else focus(parent);
|
|
91
|
+
break;
|
|
92
|
+
case 'Enter':
|
|
93
|
+
case ' ':
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
setSelected(node.id);
|
|
96
|
+
interactions?.onSelect?.();
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
return /*#__PURE__*/ jsxRuntime.jsx(StateFrame.FoundationFrame, {
|
|
101
|
+
element: emptyElement,
|
|
102
|
+
interactions: interactions,
|
|
103
|
+
name: "org-tree",
|
|
104
|
+
state: state,
|
|
105
|
+
skeleton: [
|
|
106
|
+
50,
|
|
107
|
+
70,
|
|
108
|
+
70,
|
|
109
|
+
60
|
|
110
|
+
],
|
|
111
|
+
children: (state === ComponentState.ComponentState.Ready || state === ComponentState.ComponentState.Partial) && /*#__PURE__*/ jsxRuntime.jsx("ul", {
|
|
112
|
+
role: "tree",
|
|
113
|
+
"aria-label": strings.orgTree,
|
|
114
|
+
className: "foundation-tree",
|
|
115
|
+
children: visible.map((node)=>{
|
|
116
|
+
const siblings = placed.get(node.id)?.siblings ?? [
|
|
117
|
+
node
|
|
118
|
+
];
|
|
119
|
+
const reports = orgModel.descendantCount(node);
|
|
120
|
+
const open = expanded.has(node.id);
|
|
121
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("li", {
|
|
122
|
+
id: `${element.id}-${node.id}`,
|
|
123
|
+
role: "treeitem",
|
|
124
|
+
tabIndex: node.id === tabStop ? 0 : -1,
|
|
125
|
+
"aria-level": node.depth,
|
|
126
|
+
"aria-setsize": siblings.length,
|
|
127
|
+
"aria-posinset": siblings.findIndex((sibling)=>sibling.id === node.id) + 1,
|
|
128
|
+
"aria-expanded": node.children.length > 0 ? open : undefined,
|
|
129
|
+
"aria-selected": node.id === selected,
|
|
130
|
+
className: "foundation-tree-item",
|
|
131
|
+
"data-selected": node.id === selected,
|
|
132
|
+
style: {
|
|
133
|
+
paddingInlineStart: `calc(var(--foundation-space-4) * ${node.depth - 1})`
|
|
134
|
+
},
|
|
135
|
+
onKeyDown: (event)=>move(node, event),
|
|
136
|
+
onFocus: ()=>setFocused(node.id),
|
|
137
|
+
onClick: ()=>{
|
|
138
|
+
setSelected(node.id);
|
|
139
|
+
interactions?.onSelect?.();
|
|
140
|
+
},
|
|
141
|
+
children: [
|
|
142
|
+
node.children.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx("button", {
|
|
143
|
+
type: "button",
|
|
144
|
+
tabIndex: -1,
|
|
145
|
+
className: "foundation-icon-button",
|
|
146
|
+
"aria-hidden": "true",
|
|
147
|
+
onClick: (event)=>{
|
|
148
|
+
event.stopPropagation();
|
|
149
|
+
setExpanded((current)=>{
|
|
150
|
+
const next = new Set(current);
|
|
151
|
+
if (open) next.delete(node.id);
|
|
152
|
+
else next.add(node.id);
|
|
153
|
+
return next;
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(FoundationIcon.FoundationIcon, {
|
|
157
|
+
name: open ? 'chevron-down' : 'chevron-right'
|
|
158
|
+
})
|
|
159
|
+
}) : /*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
160
|
+
className: "foundation-tree-spacer"
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
163
|
+
className: "foundation-tree-text",
|
|
164
|
+
children: [
|
|
165
|
+
/*#__PURE__*/ jsxRuntime.jsx("strong", {
|
|
166
|
+
children: node.label
|
|
167
|
+
}),
|
|
168
|
+
node.title && /*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
169
|
+
className: "foundation-count",
|
|
170
|
+
children: node.title
|
|
171
|
+
})
|
|
172
|
+
]
|
|
173
|
+
}),
|
|
174
|
+
reports > 0 && /*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
175
|
+
className: "foundation-count",
|
|
176
|
+
children: foundationStrings.formatText(strings.orgReports, {
|
|
177
|
+
count: reports
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
]
|
|
181
|
+
}, node.id);
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
exports.OrgTree = OrgTree;
|
|
188
|
+
//# sourceMappingURL=OrgTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrgTree.js","sources":["../../../foundation/OrgTree.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 { KeyboardEvent, useEffect, useMemo, useState } from 'react';\nimport { RegisteredComponentProps, useValueOutput } from '@cratis/scene.react';\nimport { ComponentState, readComponentState } from './ComponentState';\nimport { FoundationIcon } from './FoundationIcon';\nimport { formatText, readFoundationStrings } from './foundationStrings';\nimport { descendantCount, expandedToDepth, OrgTreeNode, buildOrgTree, placements, readOrgNodes, visibleNodes } from './orgModel';\nimport { FoundationFrame } from './StateFrame';\n\n/**\n * The `orgTree` component: an organization as an expandable tree of people and units.\n *\n * `nodes` are flat rows - `id`, `label`, `title`, `parentId` - as a query returns them; the tree is built from them, so someone whose manager is\n * missing becomes a root and a reporting cycle is cut instead of looping. It follows the ARIA tree pattern: one tab stop, Up and Down move,\n * Right expands or enters, Left collapses or goes to the parent, Home and End jump, Enter or Space selects. Each item says how many reports it\n * has in words. The selected id is published as the `value` output; `expandedDepth` is how many levels are open at first.\n */\nexport function OrgTree({ element, interactions, bindingOutputs }: RegisteredComponentProps) {\n const nodes = useMemo(() => readOrgNodes(element.properties.nodes), [element.properties.nodes]);\n const roots = useMemo(() => buildOrgTree(nodes), [nodes]);\n const depth = typeof element.properties.expandedDepth === 'number' ? element.properties.expandedDepth : 2;\n const initial = useMemo(() => expandedToDepth(roots, depth), [roots, depth]);\n const [expanded, setExpanded] = useState<Set<string>>(initial);\n const [selected, setSelected] = useState<string | undefined>(typeof element.properties.selected === 'string' ? element.properties.selected : undefined);\n const [focused, setFocused] = useState<string | undefined>(undefined);\n const strings = readFoundationStrings(element);\n const state = readComponentState(element, nodes.length > 0);\n const visible = visibleNodes(roots, expanded);\n const placed = useMemo(() => placements(roots), [roots]);\n const tabStop = visible.find(node => node.id === focused)?.id ?? visible.find(node => node.id === selected)?.id ?? visible[0]?.id;\n const emptyElement = state === ComponentState.Empty ? { ...element, properties: { emptyHeadline: strings.orgNone, ...element.properties } } : element;\n\n useEffect(() => setExpanded(initial), [initial]);\n useValueOutput(bindingOutputs, selected);\n\n const move = (node: OrgTreeNode, event: KeyboardEvent) => {\n const index = visible.indexOf(node);\n const focus = (target: OrgTreeNode | undefined) => {\n if (!target) return;\n event.preventDefault();\n setFocused(target.id);\n requestAnimationFrame(() => document.getElementById(`${element.id}-${target.id}`)?.focus());\n };\n const toggle = (open: boolean) => setExpanded(current => { const next = new Set(current); if (open) next.add(node.id); else next.delete(node.id); return next; });\n const parent = placed.get(node.id)?.parent;\n\n switch (event.key) {\n case 'ArrowDown': focus(visible[index + 1]); break;\n case 'ArrowUp': focus(visible[index - 1]); break;\n case 'Home': focus(visible[0]); break;\n case 'End': focus(visible[visible.length - 1]); break;\n case 'ArrowRight':\n if (node.children.length > 0 && !expanded.has(node.id)) { event.preventDefault(); toggle(true); } else focus(node.children[0]);\n break;\n case 'ArrowLeft':\n if (node.children.length > 0 && expanded.has(node.id)) { event.preventDefault(); toggle(false); } else focus(parent);\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n setSelected(node.id);\n interactions?.onSelect?.();\n break;\n default: break;\n }\n };\n\n return (\n <FoundationFrame element={emptyElement} interactions={interactions} name='org-tree' state={state} skeleton={[50, 70, 70, 60]}>\n {(state === ComponentState.Ready || state === ComponentState.Partial) && (\n <ul role='tree' aria-label={strings.orgTree} className='foundation-tree'>\n {visible.map(node => {\n const siblings = placed.get(node.id)?.siblings ?? [node];\n const reports = descendantCount(node);\n const open = expanded.has(node.id);\n return (\n <li key={node.id} id={`${element.id}-${node.id}`} role='treeitem' tabIndex={node.id === tabStop ? 0 : -1} aria-level={node.depth}\n aria-setsize={siblings.length} aria-posinset={siblings.findIndex(sibling => sibling.id === node.id) + 1} aria-expanded={node.children.length > 0 ? open : undefined} aria-selected={node.id === selected}\n className='foundation-tree-item' data-selected={node.id === selected} style={{ paddingInlineStart: `calc(var(--foundation-space-4) * ${node.depth - 1})` }}\n onKeyDown={event => move(node, event)} onFocus={() => setFocused(node.id)} onClick={() => { setSelected(node.id); interactions?.onSelect?.(); }}>\n {node.children.length > 0 ? (\n <button type='button' tabIndex={-1} className='foundation-icon-button' aria-hidden='true'\n onClick={event => { event.stopPropagation(); setExpanded(current => { const next = new Set(current); if (open) next.delete(node.id); else next.add(node.id); return next; }); }}>\n <FoundationIcon name={open ? 'chevron-down' : 'chevron-right'} />\n </button>\n ) : <span className='foundation-tree-spacer' />}\n <span className='foundation-tree-text'>\n <strong>{node.label}</strong>\n {node.title && <span className='foundation-count'>{node.title}</span>}\n </span>\n {reports > 0 && <span className='foundation-count'>{formatText(strings.orgReports, { count: reports })}</span>}\n </li>\n );\n })}\n </ul>\n )}\n </FoundationFrame>\n );\n}\n"],"names":["OrgTree","element","interactions","bindingOutputs","nodes","useMemo","readOrgNodes","properties","roots","buildOrgTree","depth","expandedDepth","initial","expandedToDepth","expanded","setExpanded","useState","selected","setSelected","undefined","focused","setFocused","strings","readFoundationStrings","state","readComponentState","length","visible","visibleNodes","placed","placements","tabStop","find","node","id","emptyElement","ComponentState","Empty","emptyHeadline","orgNone","useEffect","useValueOutput","move","event","index","indexOf","focus","target","preventDefault","requestAnimationFrame","document","getElementById","toggle","open","current","next","Set","add","delete","parent","get","key","children","has","onSelect","_jsx","FoundationFrame","name","skeleton","Ready","Partial","ul","role","aria-label","orgTree","className","map","siblings","reports","descendantCount","_jsxs","li","tabIndex","aria-level","aria-setsize","aria-posinset","findIndex","sibling","aria-expanded","aria-selected","data-selected","style","paddingInlineStart","onKeyDown","onFocus","onClick","button","type","aria-hidden","stopPropagation","FoundationIcon","span","strong","label","title","formatText","orgReports","count"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var scene_react = require('@cratis/scene.react');
|
|
6
|
+
var ComponentState = require('./ComponentState.js');
|
|
7
|
+
var FoundationIcon = require('./FoundationIcon.js');
|
|
8
|
+
var foundationStrings = require('./foundationStrings.js');
|
|
9
|
+
var permissionModel = require('./permissionModel.js');
|
|
10
|
+
var PermissionRule = require('./PermissionRule.js');
|
|
11
|
+
var StateFrame = require('./StateFrame.js');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The `permissionMatrix` component: roles across, permissions down, a checkbox where they meet.
|
|
15
|
+
*
|
|
16
|
+
* `roles` and `permissions` (optionally grouped) give the axes and `grants` - rows of `role` and `permission` - what is held; a grant of a role
|
|
17
|
+
* or permission that does not exist is ignored. Each box is named in words ("Grant Edit deals to Sales") and every header is a real header
|
|
18
|
+
* cell, so a screen reader announces the row and column of the box it is on. Changes are published as the `value` output - the grants,
|
|
19
|
+
* in the same shape - for the screen to save. With `editPermission` not enabled the matrix is read-only and each cell says "Allowed" or
|
|
20
|
+
* "Not allowed" in words and by icon. Wide matrices scroll sideways inside a keyboard-focusable region, keeping the permission column in view.
|
|
21
|
+
*/ function PermissionMatrix({ element, interactions, bindingOutputs }) {
|
|
22
|
+
const roles = react.useMemo(()=>permissionModel.readRoles(element.properties.roles), [
|
|
23
|
+
element.properties.roles
|
|
24
|
+
]);
|
|
25
|
+
const permissions = react.useMemo(()=>permissionModel.readPermissions(element.properties.permissions), [
|
|
26
|
+
element.properties.permissions
|
|
27
|
+
]);
|
|
28
|
+
const supplied = react.useMemo(()=>permissionModel.readGrants(element.properties.grants, roles, permissions), [
|
|
29
|
+
element.properties.grants,
|
|
30
|
+
roles,
|
|
31
|
+
permissions
|
|
32
|
+
]);
|
|
33
|
+
const suppliedKey = JSON.stringify(supplied);
|
|
34
|
+
const [grants, setGrants] = react.useState(supplied);
|
|
35
|
+
const strings = foundationStrings.readFoundationStrings(element);
|
|
36
|
+
const state = ComponentState.readComponentState(element, roles.length > 0 && permissions.length > 0);
|
|
37
|
+
const readOnly = PermissionRule.readPermissionRule(element, 'editPermission') !== 'enabled';
|
|
38
|
+
const sections = react.useMemo(()=>permissionModel.sectionsOf(permissions), [
|
|
39
|
+
permissions
|
|
40
|
+
]);
|
|
41
|
+
react.useEffect(()=>setGrants(supplied), [
|
|
42
|
+
suppliedKey
|
|
43
|
+
]);
|
|
44
|
+
scene_react.useValueOutput(bindingOutputs, grants);
|
|
45
|
+
return /*#__PURE__*/ jsxRuntime.jsx(StateFrame.FoundationFrame, {
|
|
46
|
+
element: element,
|
|
47
|
+
interactions: interactions,
|
|
48
|
+
name: "permission-matrix",
|
|
49
|
+
state: state,
|
|
50
|
+
skeleton: [
|
|
51
|
+
100,
|
|
52
|
+
100,
|
|
53
|
+
100,
|
|
54
|
+
100
|
|
55
|
+
],
|
|
56
|
+
children: (state === ComponentState.ComponentState.Ready || state === ComponentState.ComponentState.Partial) && /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
57
|
+
className: "foundation-matrix-scroll",
|
|
58
|
+
role: "region",
|
|
59
|
+
tabIndex: 0,
|
|
60
|
+
"aria-label": strings.matrixScroll,
|
|
61
|
+
children: /*#__PURE__*/ jsxRuntime.jsxs("table", {
|
|
62
|
+
className: "foundation-matrix",
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ jsxRuntime.jsx("thead", {
|
|
65
|
+
children: /*#__PURE__*/ jsxRuntime.jsxs("tr", {
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ jsxRuntime.jsx("th", {
|
|
68
|
+
scope: "col",
|
|
69
|
+
children: strings.matrixPermission
|
|
70
|
+
}),
|
|
71
|
+
roles.map((role)=>/*#__PURE__*/ jsxRuntime.jsx("th", {
|
|
72
|
+
scope: "col",
|
|
73
|
+
children: role.label
|
|
74
|
+
}, role.id))
|
|
75
|
+
]
|
|
76
|
+
})
|
|
77
|
+
}),
|
|
78
|
+
/*#__PURE__*/ jsxRuntime.jsx("tbody", {
|
|
79
|
+
children: sections.map((section, index)=>/*#__PURE__*/ jsxRuntime.jsxs(react.Fragment, {
|
|
80
|
+
children: [
|
|
81
|
+
section.group && /*#__PURE__*/ jsxRuntime.jsx("tr", {
|
|
82
|
+
className: "foundation-matrix-group",
|
|
83
|
+
children: /*#__PURE__*/ jsxRuntime.jsx("th", {
|
|
84
|
+
scope: "colgroup",
|
|
85
|
+
colSpan: roles.length + 1,
|
|
86
|
+
children: section.group
|
|
87
|
+
})
|
|
88
|
+
}),
|
|
89
|
+
section.permissions.map((permission)=>/*#__PURE__*/ jsxRuntime.jsxs("tr", {
|
|
90
|
+
children: [
|
|
91
|
+
/*#__PURE__*/ jsxRuntime.jsx("th", {
|
|
92
|
+
scope: "row",
|
|
93
|
+
children: permission.label
|
|
94
|
+
}),
|
|
95
|
+
roles.map((role)=>{
|
|
96
|
+
const granted = permissionModel.isGranted(grants, role.id, permission.id);
|
|
97
|
+
return /*#__PURE__*/ jsxRuntime.jsx("td", {
|
|
98
|
+
"data-granted": granted,
|
|
99
|
+
children: readOnly ? /*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
100
|
+
className: "foundation-matrix-state",
|
|
101
|
+
children: [
|
|
102
|
+
/*#__PURE__*/ jsxRuntime.jsx(FoundationIcon.FoundationIcon, {
|
|
103
|
+
name: granted ? 'check' : 'minus'
|
|
104
|
+
}),
|
|
105
|
+
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
106
|
+
className: "foundation-visually-hidden",
|
|
107
|
+
children: granted ? strings.matrixAllowed : strings.matrixDenied
|
|
108
|
+
})
|
|
109
|
+
]
|
|
110
|
+
}) : /*#__PURE__*/ jsxRuntime.jsx("input", {
|
|
111
|
+
type: "checkbox",
|
|
112
|
+
checked: granted,
|
|
113
|
+
"aria-label": foundationStrings.formatText(strings.matrixGrant, {
|
|
114
|
+
permission: permission.label,
|
|
115
|
+
role: role.label
|
|
116
|
+
}),
|
|
117
|
+
onChange: (event)=>{
|
|
118
|
+
setGrants(permissionModel.setGrant(grants, role.id, permission.id, event.target.checked));
|
|
119
|
+
interactions?.onChange?.();
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}, role.id);
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
}, permission.id))
|
|
126
|
+
]
|
|
127
|
+
}, section.group ?? `ungrouped-${index}`))
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
exports.PermissionMatrix = PermissionMatrix;
|
|
136
|
+
//# sourceMappingURL=PermissionMatrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionMatrix.js","sources":["../../../foundation/PermissionMatrix.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 { Fragment, useEffect, useMemo, useState } from 'react';\nimport { RegisteredComponentProps, useValueOutput } from '@cratis/scene.react';\nimport { ComponentState, readComponentState } from './ComponentState';\nimport { FoundationIcon } from './FoundationIcon';\nimport { formatText, readFoundationStrings } from './foundationStrings';\nimport { isGranted, MatrixGrant, readGrants, readPermissions, readRoles, sectionsOf, setGrant } from './permissionModel';\nimport { readPermissionRule } from './PermissionRule';\nimport { FoundationFrame } from './StateFrame';\n\n/**\n * The `permissionMatrix` component: roles across, permissions down, a checkbox where they meet.\n *\n * `roles` and `permissions` (optionally grouped) give the axes and `grants` - rows of `role` and `permission` - what is held; a grant of a role\n * or permission that does not exist is ignored. Each box is named in words (\"Grant Edit deals to Sales\") and every header is a real header\n * cell, so a screen reader announces the row and column of the box it is on. Changes are published as the `value` output - the grants,\n * in the same shape - for the screen to save. With `editPermission` not enabled the matrix is read-only and each cell says \"Allowed\" or\n * \"Not allowed\" in words and by icon. Wide matrices scroll sideways inside a keyboard-focusable region, keeping the permission column in view.\n */\nexport function PermissionMatrix({ element, interactions, bindingOutputs }: RegisteredComponentProps) {\n const roles = useMemo(() => readRoles(element.properties.roles), [element.properties.roles]);\n const permissions = useMemo(() => readPermissions(element.properties.permissions), [element.properties.permissions]);\n const supplied = useMemo(() => readGrants(element.properties.grants, roles, permissions), [element.properties.grants, roles, permissions]);\n const suppliedKey = JSON.stringify(supplied);\n const [grants, setGrants] = useState<MatrixGrant[]>(supplied);\n const strings = readFoundationStrings(element);\n const state = readComponentState(element, roles.length > 0 && permissions.length > 0);\n const readOnly = readPermissionRule(element, 'editPermission') !== 'enabled';\n const sections = useMemo(() => sectionsOf(permissions), [permissions]);\n\n useEffect(() => setGrants(supplied), [suppliedKey]);\n useValueOutput(bindingOutputs, grants);\n\n return (\n <FoundationFrame element={element} interactions={interactions} name='permission-matrix' state={state} skeleton={[100, 100, 100, 100]}>\n {(state === ComponentState.Ready || state === ComponentState.Partial) && (\n <div className='foundation-matrix-scroll' role='region' tabIndex={0} aria-label={strings.matrixScroll}>\n <table className='foundation-matrix'>\n <thead>\n <tr>\n <th scope='col'>{strings.matrixPermission}</th>\n {roles.map(role => <th key={role.id} scope='col'>{role.label}</th>)}\n </tr>\n </thead>\n <tbody>\n {sections.map((section, index) => (\n <Fragment key={section.group ?? `ungrouped-${index}`}>\n {section.group && (\n <tr className='foundation-matrix-group'><th scope='colgroup' colSpan={roles.length + 1}>{section.group}</th></tr>\n )}\n {section.permissions.map(permission => (\n <tr key={permission.id}>\n <th scope='row'>{permission.label}</th>\n {roles.map(role => {\n const granted = isGranted(grants, role.id, permission.id);\n return (\n <td key={role.id} data-granted={granted}>\n {readOnly ? (\n <span className='foundation-matrix-state'>\n <FoundationIcon name={granted ? 'check' : 'minus'} />\n <span className='foundation-visually-hidden'>{granted ? strings.matrixAllowed : strings.matrixDenied}</span>\n </span>\n ) : (\n <input type='checkbox' checked={granted} aria-label={formatText(strings.matrixGrant, { permission: permission.label, role: role.label })}\n onChange={event => { setGrants(setGrant(grants, role.id, permission.id, event.target.checked)); interactions?.onChange?.(); }} />\n )}\n </td>\n );\n })}\n </tr>\n ))}\n </Fragment>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </FoundationFrame>\n );\n}\n"],"names":["PermissionMatrix","element","interactions","bindingOutputs","roles","useMemo","readRoles","properties","permissions","readPermissions","supplied","readGrants","grants","suppliedKey","JSON","stringify","setGrants","useState","strings","readFoundationStrings","state","readComponentState","length","readOnly","readPermissionRule","sections","sectionsOf","useEffect","useValueOutput","_jsx","FoundationFrame","name","skeleton","ComponentState","Ready","Partial","div","className","role","tabIndex","aria-label","matrixScroll","_jsxs","table","thead","tr","th","scope","matrixPermission","map","label","id","tbody","section","index","Fragment","group","colSpan","permission","granted","isGranted","td","data-granted","span","FoundationIcon","matrixAllowed","matrixDenied","input","type","checked","formatText","matrixGrant","onChange","event","setGrant","target"],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
4
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
|
+
/** What kind of thing happened, which decides the icon and the word that tells it apart without color. */ var ActivityKind = /*#__PURE__*/ function(ActivityKind) {
|
|
6
|
+
ActivityKind["Comment"] = "comment";
|
|
7
|
+
ActivityKind["Change"] = "change";
|
|
8
|
+
ActivityKind["Status"] = "status";
|
|
9
|
+
ActivityKind["System"] = "system";
|
|
10
|
+
return ActivityKind;
|
|
11
|
+
}({});
|
|
12
|
+
/** Reads entries out of a property value, dropping those without an id or a time that parses. */ function readActivity(value) {
|
|
13
|
+
if (!Array.isArray(value)) return [];
|
|
14
|
+
return value.flatMap((entry)=>{
|
|
15
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];
|
|
16
|
+
const { id, actor, action, kind, at, body } = entry;
|
|
17
|
+
if (typeof id !== 'string' || typeof at !== 'string' || Number.isNaN(new Date(at).getTime())) return [];
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
id,
|
|
21
|
+
at,
|
|
22
|
+
actor: typeof actor === 'string' ? actor : '',
|
|
23
|
+
action: typeof action === 'string' ? action : '',
|
|
24
|
+
kind: Object.values(ActivityKind).includes(kind) ? kind : "system",
|
|
25
|
+
...typeof body === 'string' && body.length > 0 ? {
|
|
26
|
+
body
|
|
27
|
+
} : {}
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** The calendar day of an instant in a time zone, as `YYYY-MM-DD`. */ function dayOf(at, timeZone) {
|
|
33
|
+
return new Intl.DateTimeFormat('en-CA', {
|
|
34
|
+
timeZone,
|
|
35
|
+
year: 'numeric',
|
|
36
|
+
month: '2-digit',
|
|
37
|
+
day: '2-digit'
|
|
38
|
+
}).format(new Date(at));
|
|
39
|
+
}
|
|
40
|
+
/** Sorts the entries by time (newest first unless `ascending`) and groups them by calendar day, so a long feed has headings to scan by. */ function groupByDay(entries, ascending = false, timeZone) {
|
|
41
|
+
const sorted = [
|
|
42
|
+
...entries
|
|
43
|
+
].sort((first, second)=>(new Date(first.at).getTime() - new Date(second.at).getTime()) * (ascending ? 1 : -1));
|
|
44
|
+
const days = [];
|
|
45
|
+
for (const entry of sorted){
|
|
46
|
+
const day = dayOf(entry.at, timeZone);
|
|
47
|
+
const last = days[days.length - 1];
|
|
48
|
+
if (last?.day === day) last.entries.push(entry);
|
|
49
|
+
else days.push({
|
|
50
|
+
day,
|
|
51
|
+
entries: [
|
|
52
|
+
entry
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return days;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.ActivityKind = ActivityKind;
|
|
60
|
+
exports.dayOf = dayOf;
|
|
61
|
+
exports.groupByDay = groupByDay;
|
|
62
|
+
exports.readActivity = readActivity;
|
|
63
|
+
//# sourceMappingURL=activityModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activityModel.js","sources":["../../../foundation/activityModel.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\n/** What kind of thing happened, which decides the icon and the word that tells it apart without color. */\nexport enum ActivityKind {\n Comment = 'comment',\n Change = 'change',\n Status = 'status',\n System = 'system',\n}\n\n/** One thing that happened to a record. */\nexport interface ActivityEntry {\n id: string;\n actor: string;\n action: string;\n kind: ActivityKind;\n at: string;\n body?: string;\n}\n\n/** The entries of one day, in the order the feed shows them. */\nexport interface ActivityDay {\n day: string;\n entries: ActivityEntry[];\n}\n\n/** Reads entries out of a property value, dropping those without an id or a time that parses. */\nexport function readActivity(value: unknown): ActivityEntry[] {\n if (!Array.isArray(value)) return [];\n\n return value.flatMap((entry): ActivityEntry[] => {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];\n const { id, actor, action, kind, at, body } = entry as Record<string, unknown>;\n if (typeof id !== 'string' || typeof at !== 'string' || Number.isNaN(new Date(at).getTime())) return [];\n\n return [{\n id, at,\n actor: typeof actor === 'string' ? actor : '',\n action: typeof action === 'string' ? action : '',\n kind: (Object.values(ActivityKind) as string[]).includes(kind as string) ? (kind as ActivityKind) : ActivityKind.System,\n ...(typeof body === 'string' && body.length > 0 ? { body } : {}),\n }];\n });\n}\n\n/** The calendar day of an instant in a time zone, as `YYYY-MM-DD`. */\nexport function dayOf(at: string, timeZone?: string): string {\n return new Intl.DateTimeFormat('en-CA', { timeZone, year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date(at));\n}\n\n/** Sorts the entries by time (newest first unless `ascending`) and groups them by calendar day, so a long feed has headings to scan by. */\nexport function groupByDay(entries: ActivityEntry[], ascending = false, timeZone?: string): ActivityDay[] {\n const sorted = [...entries].sort((first, second) => (new Date(first.at).getTime() - new Date(second.at).getTime()) * (ascending ? 1 : -1));\n const days: ActivityDay[] = [];\n\n for (const entry of sorted) {\n const day = dayOf(entry.at, timeZone);\n const last = days[days.length - 1];\n if (last?.day === day) last.entries.push(entry);\n else days.push({ day, entries: [entry] });\n }\n\n return days;\n}\n"],"names":["ActivityKind","readActivity","value","Array","isArray","flatMap","entry","id","actor","action","kind","at","body","Number","isNaN","Date","getTime","Object","values","includes","length","dayOf","timeZone","Intl","DateTimeFormat","year","month","day","format","groupByDay","entries","ascending","sorted","sort","first","second","days","last","push"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
4
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
|
+
/** Where one step of an approval chain stands. */ var ApprovalState = /*#__PURE__*/ function(ApprovalState) {
|
|
6
|
+
ApprovalState["Pending"] = "pending";
|
|
7
|
+
ApprovalState["Current"] = "current";
|
|
8
|
+
ApprovalState["Approved"] = "approved";
|
|
9
|
+
ApprovalState["Rejected"] = "rejected";
|
|
10
|
+
ApprovalState["Skipped"] = "skipped";
|
|
11
|
+
return ApprovalState;
|
|
12
|
+
}({});
|
|
13
|
+
/** How the chain as a whole stands. */ var ChainOutcome = /*#__PURE__*/ function(ChainOutcome) {
|
|
14
|
+
/** Every step approved. */ ChainOutcome["Approved"] = "approved";
|
|
15
|
+
/** A step rejected: the chain stops there. */ ChainOutcome["Rejected"] = "rejected";
|
|
16
|
+
/** At least one step is decided and the rest are waiting. */ ChainOutcome["InProgress"] = "inProgress";
|
|
17
|
+
/** Nothing decided yet. */ ChainOutcome["Pending"] = "pending";
|
|
18
|
+
return ChainOutcome;
|
|
19
|
+
}({});
|
|
20
|
+
/** Reads steps out of a property value, dropping entries without a label; an unknown state is pending. */ function readSteps(value) {
|
|
21
|
+
if (!Array.isArray(value)) return [];
|
|
22
|
+
return value.flatMap((entry)=>{
|
|
23
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];
|
|
24
|
+
const { label, approver, state, at, comment } = entry;
|
|
25
|
+
if (typeof label !== 'string' || label.length === 0) return [];
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
label,
|
|
29
|
+
state: Object.values(ApprovalState).includes(state) ? state : "pending",
|
|
30
|
+
...typeof approver === 'string' ? {
|
|
31
|
+
approver
|
|
32
|
+
} : {},
|
|
33
|
+
...typeof at === 'string' ? {
|
|
34
|
+
at
|
|
35
|
+
} : {},
|
|
36
|
+
...typeof comment === 'string' && comment.length > 0 ? {
|
|
37
|
+
comment
|
|
38
|
+
} : {}
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Applies the chain's rules to the steps as written.
|
|
45
|
+
*
|
|
46
|
+
* A rejection stops the chain: every step after the first rejected one has not been reached, so it is drawn skipped however it was written.
|
|
47
|
+
* With no step marked current and no rejection, the first step that is not yet decided is the current one - the person the chain is waiting
|
|
48
|
+
* for - so a chain written only as approved and pending steps still says whose turn it is. Only one step is ever current.
|
|
49
|
+
*/ function effectiveSteps(steps) {
|
|
50
|
+
const rejected = steps.findIndex((step)=>step.state === "rejected");
|
|
51
|
+
const explicitCurrent = steps.findIndex((step)=>step.state === "current");
|
|
52
|
+
const waiting = rejected >= 0 ? -1 : explicitCurrent >= 0 ? explicitCurrent : steps.findIndex((step)=>step.state === "pending");
|
|
53
|
+
return steps.map((step, index)=>{
|
|
54
|
+
if (rejected >= 0 && index > rejected) return {
|
|
55
|
+
...step,
|
|
56
|
+
state: "skipped",
|
|
57
|
+
notReached: true
|
|
58
|
+
};
|
|
59
|
+
if (step.state === "current" && index !== waiting) return {
|
|
60
|
+
...step,
|
|
61
|
+
state: "pending",
|
|
62
|
+
notReached: false
|
|
63
|
+
};
|
|
64
|
+
if (index === waiting && step.state === "pending") return {
|
|
65
|
+
...step,
|
|
66
|
+
state: "current",
|
|
67
|
+
notReached: false
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
...step,
|
|
71
|
+
notReached: false
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** The state of the chain as a whole. A skipped step counts as decided, because it was never going to be asked. */ function chainOutcome(steps) {
|
|
76
|
+
const effective = effectiveSteps(steps);
|
|
77
|
+
if (effective.some((step)=>step.state === "rejected")) return "rejected";
|
|
78
|
+
if (effective.length > 0 && effective.every((step)=>step.state === "approved" || step.state === "skipped")) return "approved";
|
|
79
|
+
return effective.some((step)=>step.state === "approved") ? "inProgress" : "pending";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
exports.ApprovalState = ApprovalState;
|
|
83
|
+
exports.ChainOutcome = ChainOutcome;
|
|
84
|
+
exports.chainOutcome = chainOutcome;
|
|
85
|
+
exports.effectiveSteps = effectiveSteps;
|
|
86
|
+
exports.readSteps = readSteps;
|
|
87
|
+
//# sourceMappingURL=approvalModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalModel.js","sources":["../../../foundation/approvalModel.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\n/** Where one step of an approval chain stands. */\nexport enum ApprovalState {\n Pending = 'pending',\n Current = 'current',\n Approved = 'approved',\n Rejected = 'rejected',\n Skipped = 'skipped',\n}\n\n/** One step of an approval chain: who decides, and what they decided. */\nexport interface ApprovalStep {\n label: string;\n approver?: string;\n state: ApprovalState;\n at?: string;\n comment?: string;\n}\n\n/** A step as it is drawn, after the chain's own rules are applied. */\nexport interface EffectiveStep extends ApprovalStep {\n /** True when an earlier rejection stopped the chain before this step was reached. */\n notReached: boolean;\n}\n\n/** How the chain as a whole stands. */\nexport enum ChainOutcome {\n /** Every step approved. */\n Approved = 'approved',\n\n /** A step rejected: the chain stops there. */\n Rejected = 'rejected',\n\n /** At least one step is decided and the rest are waiting. */\n InProgress = 'inProgress',\n\n /** Nothing decided yet. */\n Pending = 'pending',\n}\n\n/** Reads steps out of a property value, dropping entries without a label; an unknown state is pending. */\nexport function readSteps(value: unknown): ApprovalStep[] {\n if (!Array.isArray(value)) return [];\n\n return value.flatMap((entry): ApprovalStep[] => {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];\n const { label, approver, state, at, comment } = entry as Record<string, unknown>;\n if (typeof label !== 'string' || label.length === 0) return [];\n\n return [{\n label,\n state: (Object.values(ApprovalState) as string[]).includes(state as string) ? (state as ApprovalState) : ApprovalState.Pending,\n ...(typeof approver === 'string' ? { approver } : {}),\n ...(typeof at === 'string' ? { at } : {}),\n ...(typeof comment === 'string' && comment.length > 0 ? { comment } : {}),\n }];\n });\n}\n\n/**\n * Applies the chain's rules to the steps as written.\n *\n * A rejection stops the chain: every step after the first rejected one has not been reached, so it is drawn skipped however it was written.\n * With no step marked current and no rejection, the first step that is not yet decided is the current one - the person the chain is waiting\n * for - so a chain written only as approved and pending steps still says whose turn it is. Only one step is ever current.\n */\nexport function effectiveSteps(steps: ApprovalStep[]): EffectiveStep[] {\n const rejected = steps.findIndex(step => step.state === ApprovalState.Rejected);\n const explicitCurrent = steps.findIndex(step => step.state === ApprovalState.Current);\n const waiting = rejected >= 0 ? -1 : explicitCurrent >= 0 ? explicitCurrent : steps.findIndex(step => step.state === ApprovalState.Pending);\n\n return steps.map((step, index) => {\n if (rejected >= 0 && index > rejected) return { ...step, state: ApprovalState.Skipped, notReached: true };\n if (step.state === ApprovalState.Current && index !== waiting) return { ...step, state: ApprovalState.Pending, notReached: false };\n if (index === waiting && step.state === ApprovalState.Pending) return { ...step, state: ApprovalState.Current, notReached: false };\n return { ...step, notReached: false };\n });\n}\n\n/** The state of the chain as a whole. A skipped step counts as decided, because it was never going to be asked. */\nexport function chainOutcome(steps: ApprovalStep[]): ChainOutcome {\n const effective = effectiveSteps(steps);\n if (effective.some(step => step.state === ApprovalState.Rejected)) return ChainOutcome.Rejected;\n if (effective.length > 0 && effective.every(step => step.state === ApprovalState.Approved || step.state === ApprovalState.Skipped)) return ChainOutcome.Approved;\n return effective.some(step => step.state === ApprovalState.Approved) ? ChainOutcome.InProgress : ChainOutcome.Pending;\n}\n"],"names":["ApprovalState","ChainOutcome","readSteps","value","Array","isArray","flatMap","entry","label","approver","state","at","comment","length","Object","values","includes","effectiveSteps","steps","rejected","findIndex","step","explicitCurrent","waiting","map","index","notReached","chainOutcome","effective","some","every"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
4
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
|
+
/** A person shown as an avatar. */ /** Reads people out of a property value, dropping entries without a name. */ function readPeople(value) {
|
|
6
|
+
if (!Array.isArray(value)) return [];
|
|
7
|
+
return value.flatMap((entry)=>{
|
|
8
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];
|
|
9
|
+
const { name, imageUrl } = entry;
|
|
10
|
+
if (typeof name !== 'string' || name.trim().length === 0) return [];
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
name: name.trim(),
|
|
14
|
+
...typeof imageUrl === 'string' && imageUrl.length > 0 ? {
|
|
15
|
+
imageUrl
|
|
16
|
+
} : {}
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** The initials of a name: the first letter of the first and last words, so "Ada King Lovelace" is AL and "Ada" is A. */ function initialsOf(name) {
|
|
22
|
+
const words = name.split(/\s+/).filter((word)=>word.length > 0);
|
|
23
|
+
if (words.length === 0) return '';
|
|
24
|
+
const first = Array.from(words[0])[0] ?? '';
|
|
25
|
+
const last = words.length > 1 ? Array.from(words[words.length - 1])[0] ?? '' : '';
|
|
26
|
+
return `${first}${last}`.toLocaleUpperCase();
|
|
27
|
+
}
|
|
28
|
+
/** Splits people into those shown and how many are folded into the "+N" chip; at least one is always shown when there are any. */ function visiblePeople(people, maximum) {
|
|
29
|
+
const limit = Math.max(1, Math.floor(maximum));
|
|
30
|
+
return people.length <= limit ? {
|
|
31
|
+
shown: people,
|
|
32
|
+
hidden: 0
|
|
33
|
+
} : {
|
|
34
|
+
shown: people.slice(0, limit),
|
|
35
|
+
hidden: people.length - limit
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.initialsOf = initialsOf;
|
|
40
|
+
exports.readPeople = readPeople;
|
|
41
|
+
exports.visiblePeople = visiblePeople;
|
|
42
|
+
//# sourceMappingURL=avatarModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatarModel.js","sources":["../../../foundation/avatarModel.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\n/** A person shown as an avatar. */\nexport interface AvatarPerson {\n name: string;\n imageUrl?: string;\n}\n\n/** Reads people out of a property value, dropping entries without a name. */\nexport function readPeople(value: unknown): AvatarPerson[] {\n if (!Array.isArray(value)) return [];\n\n return value.flatMap((entry): AvatarPerson[] => {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) return [];\n const { name, imageUrl } = entry as Record<string, unknown>;\n if (typeof name !== 'string' || name.trim().length === 0) return [];\n return [{ name: name.trim(), ...(typeof imageUrl === 'string' && imageUrl.length > 0 ? { imageUrl } : {}) }];\n });\n}\n\n/** The initials of a name: the first letter of the first and last words, so \"Ada King Lovelace\" is AL and \"Ada\" is A. */\nexport function initialsOf(name: string): string {\n const words = name.split(/\\s+/).filter(word => word.length > 0);\n if (words.length === 0) return '';\n\n const first = Array.from(words[0])[0] ?? '';\n const last = words.length > 1 ? Array.from(words[words.length - 1])[0] ?? '' : '';\n return `${first}${last}`.toLocaleUpperCase();\n}\n\n/** Splits people into those shown and how many are folded into the \"+N\" chip; at least one is always shown when there are any. */\nexport function visiblePeople(people: AvatarPerson[], maximum: number): { shown: AvatarPerson[]; hidden: number } {\n const limit = Math.max(1, Math.floor(maximum));\n return people.length <= limit ? { shown: people, hidden: 0 } : { shown: people.slice(0, limit), hidden: people.length - limit };\n}\n"],"names":["readPeople","value","Array","isArray","flatMap","entry","name","imageUrl","trim","length","initialsOf","words","split","filter","word","first","from","last","toLocaleUpperCase","visiblePeople","people","maximum","limit","Math","max","floor","shown","hidden","slice"],"mappings":";;AAAA;AACA;AAEA,kHAOO,SAASA,WAAWC,KAAc,EAAA;AACrC,IAAA,IAAI,CAACC,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,EAAQ,OAAO,EAAE;IAEpC,OAAOA,KAAAA,CAAMG,OAAO,CAAC,CAACC,KAAAA,GAAAA;QAClB,IAAI,OAAOA,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQH,MAAMC,OAAO,CAACE,KAAAA,CAAAA,EAAQ,OAAO,EAAE;AAClF,QAAA,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGF,KAAAA;QAC3B,IAAI,OAAOC,IAAAA,KAAS,QAAA,IAAYA,IAAAA,CAAKE,IAAI,GAAGC,MAAM,KAAK,CAAA,EAAG,OAAO,EAAE;QACnE,OAAO;AAAC,YAAA;AAAEH,gBAAAA,IAAAA,EAAMA,KAAKE,IAAI,EAAA;AAAI,gBAAA,GAAI,OAAOD,QAAAA,KAAa,QAAA,IAAYA,QAAAA,CAASE,MAAM,GAAG,CAAA,GAAI;AAAEF,oBAAAA;AAAS,iBAAA,GAAI;AAAI;AAAE,SAAA;AAChH,IAAA,CAAA,CAAA;AACJ;AAEA,0HACO,SAASG,UAAAA,CAAWJ,IAAY,EAAA;IACnC,MAAMK,KAAAA,GAAQL,IAAAA,CAAKM,KAAK,CAAC,KAAA,CAAA,CAAOC,MAAM,CAACC,CAAAA,IAAAA,GAAQA,IAAAA,CAAKL,MAAM,GAAG,CAAA,CAAA;AAC7D,IAAA,IAAIE,KAAAA,CAAMF,MAAM,KAAK,CAAA,EAAG,OAAO,EAAA;IAE/B,MAAMM,KAAAA,GAAQb,KAAAA,CAAMc,IAAI,CAACL,KAAK,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAE,IAAI,EAAA;AACzC,IAAA,MAAMM,OAAON,KAAAA,CAAMF,MAAM,GAAG,CAAA,GAAIP,KAAAA,CAAMc,IAAI,CAACL,KAAK,CAACA,KAAAA,CAAMF,MAAM,GAAG,CAAA,CAAE,CAAC,CAAC,CAAA,CAAE,IAAI,EAAA,GAAK,EAAA;AAC/E,IAAA,OAAO,CAAA,EAAGM,KAAAA,CAAAA,EAAQE,IAAAA,CAAAA,CAAM,CAACC,iBAAiB,EAAA;AAC9C;AAEA,mIACO,SAASC,aAAAA,CAAcC,MAAsB,EAAEC,OAAe,EAAA;AACjE,IAAA,MAAMC,QAAQC,IAAAA,CAAKC,GAAG,CAAC,CAAA,EAAGD,IAAAA,CAAKE,KAAK,CAACJ,OAAAA,CAAAA,CAAAA;IACrC,OAAOD,MAAAA,CAAOX,MAAM,IAAIa,KAAAA,GAAQ;QAAEI,KAAAA,EAAON,MAAAA;QAAQO,MAAAA,EAAQ;KAAE,GAAI;QAAED,KAAAA,EAAON,MAAAA,CAAOQ,KAAK,CAAC,CAAA,EAAGN,KAAAA,CAAAA;QAAQK,MAAAA,EAAQP,MAAAA,CAAOX,MAAM,GAAGa;AAAM,KAAA;AAClI;;;;"}
|