@cratis/scene.blueprint.default 4.38.0 → 4.40.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 (167) hide show
  1. package/ComponentName.ts +27 -0
  2. package/defaultBlueprintComponents.ts +10 -1
  3. package/dist/cjs/ComponentName.js +9 -0
  4. package/dist/cjs/ComponentName.js.map +1 -1
  5. package/dist/cjs/defaultBlueprintComponents.js +19 -1
  6. package/dist/cjs/defaultBlueprintComponents.js.map +1 -1
  7. package/dist/cjs/foundation/BulkBar.js +62 -0
  8. package/dist/cjs/foundation/BulkBar.js.map +1 -0
  9. package/dist/cjs/foundation/DataList.js +436 -0
  10. package/dist/cjs/foundation/DataList.js.map +1 -0
  11. package/dist/cjs/foundation/FormScreenBody.js +447 -0
  12. package/dist/cjs/foundation/FormScreenBody.js.map +1 -0
  13. package/dist/cjs/foundation/FoundationStates.js +2 -1
  14. package/dist/cjs/foundation/FoundationStates.js.map +1 -1
  15. package/dist/cjs/foundation/GlobalSearch.js +54 -0
  16. package/dist/cjs/foundation/GlobalSearch.js.map +1 -0
  17. package/dist/cjs/foundation/KanbanBoard.js +222 -0
  18. package/dist/cjs/foundation/KanbanBoard.js.map +1 -0
  19. package/dist/cjs/foundation/NotificationsButton.js +45 -0
  20. package/dist/cjs/foundation/NotificationsButton.js.map +1 -0
  21. package/dist/cjs/foundation/SubNavigation.js +72 -0
  22. package/dist/cjs/foundation/SubNavigation.js.map +1 -0
  23. package/dist/cjs/foundation/TabSet.js +101 -0
  24. package/dist/cjs/foundation/TabSet.js.map +1 -0
  25. package/dist/cjs/foundation/WizardProgress.js +185 -0
  26. package/dist/cjs/foundation/WizardProgress.js.map +1 -0
  27. package/dist/cjs/foundation/boardModel.js +67 -0
  28. package/dist/cjs/foundation/boardModel.js.map +1 -0
  29. package/dist/cjs/foundation/formModel.js +158 -0
  30. package/dist/cjs/foundation/formModel.js.map +1 -0
  31. package/dist/cjs/foundation/foundationComponentMetadata.js +54 -0
  32. package/dist/cjs/foundation/foundationComponentMetadata.js.map +1 -1
  33. package/dist/cjs/foundation/foundationDescriptors.js +400 -0
  34. package/dist/cjs/foundation/foundationDescriptors.js.map +1 -1
  35. package/dist/cjs/foundation/foundationSamples.js +303 -0
  36. package/dist/cjs/foundation/foundationSamples.js.map +1 -1
  37. package/dist/cjs/foundation/foundationStrings.js +72 -0
  38. package/dist/cjs/foundation/foundationStrings.js.map +1 -1
  39. package/dist/cjs/foundation/listModel.js +200 -0
  40. package/dist/cjs/foundation/listModel.js.map +1 -0
  41. package/dist/cjs/foundation/tabModel.js +41 -0
  42. package/dist/cjs/foundation/tabModel.js.map +1 -0
  43. package/dist/cjs/foundation/urlState.js +57 -0
  44. package/dist/cjs/foundation/urlState.js.map +1 -0
  45. package/dist/cjs/foundation/useCompactWidth.js +23 -0
  46. package/dist/cjs/foundation/useCompactWidth.js.map +1 -0
  47. package/dist/cjs/foundation/wizardModel.js +50 -0
  48. package/dist/cjs/foundation/wizardModel.js.map +1 -0
  49. package/dist/cjs/index.js +71 -0
  50. package/dist/cjs/index.js.map +1 -1
  51. package/dist/esm/ComponentName.d.ts +10 -1
  52. package/dist/esm/ComponentName.d.ts.map +1 -1
  53. package/dist/esm/ComponentName.js +9 -0
  54. package/dist/esm/ComponentName.js.map +1 -1
  55. package/dist/esm/defaultBlueprintComponents.d.ts.map +1 -1
  56. package/dist/esm/defaultBlueprintComponents.js +19 -1
  57. package/dist/esm/defaultBlueprintComponents.js.map +1 -1
  58. package/dist/esm/foundation/BulkBar.d.ts +4 -0
  59. package/dist/esm/foundation/BulkBar.d.ts.map +1 -0
  60. package/dist/esm/foundation/BulkBar.js +59 -0
  61. package/dist/esm/foundation/BulkBar.js.map +1 -0
  62. package/dist/esm/foundation/DataList.d.ts +3 -0
  63. package/dist/esm/foundation/DataList.d.ts.map +1 -0
  64. package/dist/esm/foundation/DataList.js +434 -0
  65. package/dist/esm/foundation/DataList.js.map +1 -0
  66. package/dist/esm/foundation/FormScreenBody.d.ts +3 -0
  67. package/dist/esm/foundation/FormScreenBody.d.ts.map +1 -0
  68. package/dist/esm/foundation/FormScreenBody.js +445 -0
  69. package/dist/esm/foundation/FormScreenBody.js.map +1 -0
  70. package/dist/esm/foundation/FoundationStates.js +2 -1
  71. package/dist/esm/foundation/FoundationStates.js.map +1 -1
  72. package/dist/esm/foundation/GlobalSearch.d.ts +3 -0
  73. package/dist/esm/foundation/GlobalSearch.d.ts.map +1 -0
  74. package/dist/esm/foundation/GlobalSearch.js +52 -0
  75. package/dist/esm/foundation/GlobalSearch.js.map +1 -0
  76. package/dist/esm/foundation/KanbanBoard.d.ts +3 -0
  77. package/dist/esm/foundation/KanbanBoard.d.ts.map +1 -0
  78. package/dist/esm/foundation/KanbanBoard.js +220 -0
  79. package/dist/esm/foundation/KanbanBoard.js.map +1 -0
  80. package/dist/esm/foundation/NotificationsButton.d.ts +3 -0
  81. package/dist/esm/foundation/NotificationsButton.d.ts.map +1 -0
  82. package/dist/esm/foundation/NotificationsButton.js +43 -0
  83. package/dist/esm/foundation/NotificationsButton.js.map +1 -0
  84. package/dist/esm/foundation/SubNavigation.d.ts +3 -0
  85. package/dist/esm/foundation/SubNavigation.d.ts.map +1 -0
  86. package/dist/esm/foundation/SubNavigation.js +70 -0
  87. package/dist/esm/foundation/SubNavigation.js.map +1 -0
  88. package/dist/esm/foundation/TabSet.d.ts +3 -0
  89. package/dist/esm/foundation/TabSet.d.ts.map +1 -0
  90. package/dist/esm/foundation/TabSet.js +99 -0
  91. package/dist/esm/foundation/TabSet.js.map +1 -0
  92. package/dist/esm/foundation/WizardProgress.d.ts +3 -0
  93. package/dist/esm/foundation/WizardProgress.d.ts.map +1 -0
  94. package/dist/esm/foundation/WizardProgress.js +183 -0
  95. package/dist/esm/foundation/WizardProgress.js.map +1 -0
  96. package/dist/esm/foundation/boardModel.d.ts +22 -0
  97. package/dist/esm/foundation/boardModel.d.ts.map +1 -0
  98. package/dist/esm/foundation/boardModel.js +61 -0
  99. package/dist/esm/foundation/boardModel.js.map +1 -0
  100. package/dist/esm/foundation/formModel.d.ts +48 -0
  101. package/dist/esm/foundation/formModel.d.ts.map +1 -0
  102. package/dist/esm/foundation/formModel.js +147 -0
  103. package/dist/esm/foundation/formModel.js.map +1 -0
  104. package/dist/esm/foundation/foundationComponentMetadata.d.ts.map +1 -1
  105. package/dist/esm/foundation/foundationComponentMetadata.js +54 -0
  106. package/dist/esm/foundation/foundationComponentMetadata.js.map +1 -1
  107. package/dist/esm/foundation/foundationDescriptors.d.ts.map +1 -1
  108. package/dist/esm/foundation/foundationDescriptors.js +400 -0
  109. package/dist/esm/foundation/foundationDescriptors.js.map +1 -1
  110. package/dist/esm/foundation/foundationSamples.d.ts.map +1 -1
  111. package/dist/esm/foundation/foundationSamples.js +303 -0
  112. package/dist/esm/foundation/foundationSamples.js.map +1 -1
  113. package/dist/esm/foundation/foundationStrings.d.ts +72 -0
  114. package/dist/esm/foundation/foundationStrings.d.ts.map +1 -1
  115. package/dist/esm/foundation/foundationStrings.js +72 -0
  116. package/dist/esm/foundation/foundationStrings.js.map +1 -1
  117. package/dist/esm/foundation/index.d.ts +16 -0
  118. package/dist/esm/foundation/index.d.ts.map +1 -1
  119. package/dist/esm/foundation/index.js +16 -0
  120. package/dist/esm/foundation/index.js.map +1 -1
  121. package/dist/esm/foundation/listModel.d.ts +49 -0
  122. package/dist/esm/foundation/listModel.d.ts.map +1 -0
  123. package/dist/esm/foundation/listModel.js +183 -0
  124. package/dist/esm/foundation/listModel.js.map +1 -0
  125. package/dist/esm/foundation/tabModel.d.ts +10 -0
  126. package/dist/esm/foundation/tabModel.d.ts.map +1 -0
  127. package/dist/esm/foundation/tabModel.js +37 -0
  128. package/dist/esm/foundation/tabModel.js.map +1 -0
  129. package/dist/esm/foundation/urlState.d.ts +4 -0
  130. package/dist/esm/foundation/urlState.d.ts.map +1 -0
  131. package/dist/esm/foundation/urlState.js +53 -0
  132. package/dist/esm/foundation/urlState.js.map +1 -0
  133. package/dist/esm/foundation/useCompactWidth.d.ts +3 -0
  134. package/dist/esm/foundation/useCompactWidth.d.ts.map +1 -0
  135. package/dist/esm/foundation/useCompactWidth.js +20 -0
  136. package/dist/esm/foundation/useCompactWidth.js.map +1 -0
  137. package/dist/esm/foundation/wizardModel.d.ts +16 -0
  138. package/dist/esm/foundation/wizardModel.d.ts.map +1 -0
  139. package/dist/esm/foundation/wizardModel.js +43 -0
  140. package/dist/esm/foundation/wizardModel.js.map +1 -0
  141. package/dist/esm/index.js +16 -0
  142. package/dist/esm/index.js.map +1 -1
  143. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  144. package/foundation/BulkBar.tsx +38 -0
  145. package/foundation/DataList.tsx +208 -0
  146. package/foundation/FormScreenBody.tsx +196 -0
  147. package/foundation/FoundationStates.tsx +1 -1
  148. package/foundation/GlobalSearch.tsx +33 -0
  149. package/foundation/KanbanBoard.tsx +97 -0
  150. package/foundation/NotificationsButton.tsx +28 -0
  151. package/foundation/SubNavigation.tsx +49 -0
  152. package/foundation/TabSet.tsx +65 -0
  153. package/foundation/WizardProgress.tsx +109 -0
  154. package/foundation/boardModel.ts +64 -0
  155. package/foundation/formModel.ts +146 -0
  156. package/foundation/foundationComponentMetadata.ts +9 -0
  157. package/foundation/foundationDescriptors.ts +195 -0
  158. package/foundation/foundationSamples.ts +56 -0
  159. package/foundation/foundationStrings.ts +72 -0
  160. package/foundation/index.ts +16 -0
  161. package/foundation/listModel.ts +190 -0
  162. package/foundation/tabModel.ts +41 -0
  163. package/foundation/urlState.ts +52 -0
  164. package/foundation/useCompactWidth.ts +23 -0
  165. package/foundation/wizardModel.ts +54 -0
  166. package/package.json +8 -8
  167. package/shell/layout.css +513 -0
@@ -0,0 +1,183 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { useMemo, useState, useEffect } from 'react';
3
+ import { useValueOutput } from '@cratis/scene.react';
4
+ import { readOptionalString } from '../shell/elementProperties.js';
5
+ import { readComponentState, ComponentState } from './ComponentState.js';
6
+ import { FoundationIcon } from './FoundationIcon.js';
7
+ import { readFoundationStrings, formatText } from './foundationStrings.js';
8
+ import { FoundationFrame } from './StateFrame.js';
9
+ import { readWizardSteps, reachableStep, statusOf, canOpen, StepStatus, neighbour } from './wizardModel.js';
10
+ import { ActionButton } from './ActionButton.js';
11
+ import { useUrlState } from './urlState.js';
12
+
13
+ const storageKey = (element, key)=>`scene.wizard.${key ?? element.id}`;
14
+ /**
15
+ * The completed steps saved in the session, or none. Storage can be absent or refuse access - server rendering, a sandboxed frame, a browser in a
16
+ * private mode - and a wizard that cannot remember its progress is still a wizard, so every storage access is guarded and a failure only loses the memory.
17
+ */ function readSaved(key) {
18
+ try {
19
+ const saved = JSON.parse(window.sessionStorage.getItem(key) ?? '[]');
20
+ return new Set(Array.isArray(saved) ? saved.filter((id)=>typeof id === 'string') : []);
21
+ } catch {
22
+ return new Set();
23
+ }
24
+ }
25
+ /** Saves the completed steps for the session; a refusal by the browser is ignored (see {@link readSaved}). */ function writeSaved(key, completed) {
26
+ try {
27
+ window.sessionStorage.setItem(key, JSON.stringify([
28
+ ...completed
29
+ ]));
30
+ } catch {
31
+ // The progress is simply not remembered across a refresh.
32
+ }
33
+ }
34
+ /**
35
+ * The `wizardProgress` component: the steps of a linear wizard, the back/next/finish controls and the draft save, with the step's own content in
36
+ * the slot named by its id.
37
+ *
38
+ * `steps` (`id`, `label`, `valid`) are linear. Next is blocked while the current step is not `valid`, and says so. The final step is the review step: Finish
39
+ * replaces Next. Completed steps stay in a progress list and can be revisited; a step cannot be opened before the ones ahead of it are done. The current step
40
+ * (in the address under `urlKey`) and the completed steps (in the session) are restored on refresh, and a refresh can never land past a step that was not
41
+ * done. The id of the current step is the `value` output; "Save draft" runs the behaviors attached to the element and says so.
42
+ */ function WizardProgress({ element, interactions, slots, bindingOutputs }) {
43
+ const steps = useMemo(()=>readWizardSteps(element.properties.steps), [
44
+ element.properties.steps
45
+ ]);
46
+ const urlKey = readOptionalString(element, 'urlKey');
47
+ const [requested, setRequested] = useUrlState(urlKey, readOptionalString(element, 'currentStep'));
48
+ const key = storageKey(element, urlKey);
49
+ const [completed, setCompleted] = useState(()=>readSaved(key));
50
+ const [draftSaved, setDraftSaved] = useState(false);
51
+ const strings = readFoundationStrings(element);
52
+ const state = readComponentState(element, steps.length > 0);
53
+ const current = reachableStep(steps, completed, requested);
54
+ const index = steps.findIndex((step)=>step.id === current);
55
+ const step = steps[index];
56
+ const last = index === steps.length - 1;
57
+ useEffect(()=>writeSaved(key, completed), [
58
+ key,
59
+ completed
60
+ ]);
61
+ useValueOutput(bindingOutputs, current);
62
+ const go = (id)=>{
63
+ if (id === undefined) return;
64
+ setRequested(id);
65
+ setDraftSaved(false);
66
+ interactions?.onSelect?.();
67
+ };
68
+ const next = ()=>{
69
+ if (!step?.valid) return;
70
+ setCompleted((current_)=>new Set(current_).add(step.id));
71
+ go(neighbour(steps, current, 1)?.id);
72
+ };
73
+ const finish = ()=>{
74
+ if (!step?.valid) return;
75
+ setCompleted((current_)=>new Set(current_).add(step.id));
76
+ interactions?.onClick?.({
77
+ stopPropagation: ()=>undefined
78
+ });
79
+ };
80
+ return /*#__PURE__*/ jsx(FoundationFrame, {
81
+ element: element,
82
+ interactions: interactions,
83
+ name: "wizard",
84
+ state: state,
85
+ skeleton: [
86
+ 100,
87
+ 60,
88
+ 100
89
+ ],
90
+ children: state !== ComponentState.Loading && step && /*#__PURE__*/ jsxs(Fragment, {
91
+ children: [
92
+ /*#__PURE__*/ jsx("nav", {
93
+ "aria-label": strings.wizardProgress,
94
+ children: /*#__PURE__*/ jsx("ol", {
95
+ className: "foundation-wizard-steps",
96
+ children: steps.map((candidate, position)=>{
97
+ const status = statusOf(completed, current, candidate.id);
98
+ const open = canOpen(steps, completed, candidate.id);
99
+ const text = status === StepStatus.Completed ? strings.wizardComplete : status === StepStatus.Current ? strings.wizardCurrent : strings.wizardUpcoming;
100
+ return /*#__PURE__*/ jsx("li", {
101
+ className: "foundation-wizard-step",
102
+ "data-status": status,
103
+ children: /*#__PURE__*/ jsxs("button", {
104
+ type: "button",
105
+ "aria-current": status === StepStatus.Current ? 'step' : undefined,
106
+ disabled: !open,
107
+ onClick: ()=>go(candidate.id),
108
+ children: [
109
+ /*#__PURE__*/ jsx("span", {
110
+ className: "foundation-wizard-marker",
111
+ children: status === StepStatus.Completed ? /*#__PURE__*/ jsx(FoundationIcon, {
112
+ name: "check"
113
+ }) : position + 1
114
+ }),
115
+ /*#__PURE__*/ jsx("span", {
116
+ children: candidate.label
117
+ }),
118
+ /*#__PURE__*/ jsx("span", {
119
+ className: "foundation-visually-hidden",
120
+ children: `, ${formatText(strings.wizardStep, {
121
+ n: position + 1,
122
+ total: steps.length
123
+ })}, ${text}`
124
+ })
125
+ ]
126
+ })
127
+ }, candidate.id);
128
+ })
129
+ })
130
+ }),
131
+ /*#__PURE__*/ jsx("div", {
132
+ className: "foundation-wizard-body",
133
+ role: "group",
134
+ "aria-label": `${formatText(strings.wizardStep, {
135
+ n: index + 1,
136
+ total: steps.length
137
+ })}: ${step.label}`,
138
+ children: slots?.[step.id]
139
+ }),
140
+ /*#__PURE__*/ jsxs("div", {
141
+ className: "foundation-wizard-actions",
142
+ children: [
143
+ /*#__PURE__*/ jsx(ActionButton, {
144
+ emphasis: "quiet",
145
+ disabledReason: strings.disabledNoPermission,
146
+ onClick: ()=>{
147
+ setDraftSaved(true);
148
+ interactions?.onClick?.({
149
+ stopPropagation: ()=>undefined
150
+ });
151
+ },
152
+ children: strings.wizardSaveDraft
153
+ }),
154
+ /*#__PURE__*/ jsx("span", {
155
+ role: "status",
156
+ className: "foundation-count",
157
+ children: draftSaved ? strings.wizardDraftSaved : ''
158
+ }),
159
+ /*#__PURE__*/ jsx("span", {
160
+ className: "foundation-wizard-spacer"
161
+ }),
162
+ index > 0 && /*#__PURE__*/ jsx(ActionButton, {
163
+ emphasis: "secondary",
164
+ disabledReason: strings.disabledNoPermission,
165
+ onClick: ()=>go(neighbour(steps, current, -1)?.id),
166
+ children: strings.wizardBack
167
+ }),
168
+ /*#__PURE__*/ jsx("button", {
169
+ type: "button",
170
+ className: "foundation-button",
171
+ "aria-disabled": !step.valid || undefined,
172
+ onClick: last ? finish : next,
173
+ children: last ? strings.wizardFinish : strings.wizardNext
174
+ })
175
+ ]
176
+ })
177
+ ]
178
+ })
179
+ });
180
+ }
181
+
182
+ export { WizardProgress };
183
+ //# sourceMappingURL=WizardProgress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WizardProgress.js","sources":["../../../foundation/WizardProgress.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 { useEffect, useMemo, useState } from 'react';\nimport { RegisteredComponentProps, useValueOutput } from '@cratis/scene.react';\nimport { readOptionalString } from '../shell/elementProperties';\nimport { ComponentState, readComponentState } from './ComponentState';\nimport { FoundationIcon } from './FoundationIcon';\nimport { formatText, readFoundationStrings } from './foundationStrings';\nimport { FoundationFrame } from './StateFrame';\nimport { StepStatus, canOpen, neighbour, readWizardSteps, reachableStep, statusOf } from './wizardModel';\nimport { ActionButton } from './ActionButton';\nimport { useUrlState } from './urlState';\n\nconst storageKey = (element: { id: string }, key: string | undefined) => `scene.wizard.${key ?? element.id}`;\n\n/**\n * The completed steps saved in the session, or none. Storage can be absent or refuse access - server rendering, a sandboxed frame, a browser in a\n * private mode - and a wizard that cannot remember its progress is still a wizard, so every storage access is guarded and a failure only loses the memory.\n */\nfunction readSaved(key: string): Set<string> {\n try {\n const saved = JSON.parse(window.sessionStorage.getItem(key) ?? '[]') as unknown;\n return new Set(Array.isArray(saved) ? saved.filter((id): id is string => typeof id === 'string') : []);\n } catch {\n return new Set();\n }\n}\n\n/** Saves the completed steps for the session; a refusal by the browser is ignored (see {@link readSaved}). */\nfunction writeSaved(key: string, completed: Set<string>): void {\n try {\n window.sessionStorage.setItem(key, JSON.stringify([...completed]));\n } catch {\n // The progress is simply not remembered across a refresh.\n }\n}\n\n/**\n * The `wizardProgress` component: the steps of a linear wizard, the back/next/finish controls and the draft save, with the step's own content in\n * the slot named by its id.\n *\n * `steps` (`id`, `label`, `valid`) are linear. Next is blocked while the current step is not `valid`, and says so. The final step is the review step: Finish\n * replaces Next. Completed steps stay in a progress list and can be revisited; a step cannot be opened before the ones ahead of it are done. The current step\n * (in the address under `urlKey`) and the completed steps (in the session) are restored on refresh, and a refresh can never land past a step that was not\n * done. The id of the current step is the `value` output; \"Save draft\" runs the behaviors attached to the element and says so.\n */\nexport function WizardProgress({ element, interactions, slots, bindingOutputs }: RegisteredComponentProps) {\n const steps = useMemo(() => readWizardSteps(element.properties.steps), [element.properties.steps]);\n const urlKey = readOptionalString(element, 'urlKey');\n const [requested, setRequested] = useUrlState(urlKey, readOptionalString(element, 'currentStep'));\n const key = storageKey(element, urlKey);\n const [completed, setCompleted] = useState<Set<string>>(() => readSaved(key));\n const [draftSaved, setDraftSaved] = useState(false);\n const strings = readFoundationStrings(element);\n const state = readComponentState(element, steps.length > 0);\n const current = reachableStep(steps, completed, requested);\n const index = steps.findIndex(step => step.id === current);\n const step = steps[index];\n const last = index === steps.length - 1;\n\n useEffect(() => writeSaved(key, completed), [key, completed]);\n useValueOutput(bindingOutputs, current);\n\n const go = (id: string | undefined) => { if (id === undefined) return; setRequested(id); setDraftSaved(false); interactions?.onSelect?.(); };\n const next = () => {\n if (!step?.valid) return;\n setCompleted(current_ => new Set(current_).add(step.id));\n go(neighbour(steps, current, 1)?.id);\n };\n const finish = () => { if (!step?.valid) return; setCompleted(current_ => new Set(current_).add(step.id)); interactions?.onClick?.({ stopPropagation: () => undefined }); };\n\n return (\n <FoundationFrame element={element} interactions={interactions} name='wizard' state={state} skeleton={[100, 60, 100]}>\n {state !== ComponentState.Loading && step && (\n <>\n <nav aria-label={strings.wizardProgress}>\n <ol className='foundation-wizard-steps'>\n {steps.map((candidate, position) => {\n const status = statusOf(completed, current, candidate.id);\n const open = canOpen(steps, completed, candidate.id);\n const text = status === StepStatus.Completed ? strings.wizardComplete : status === StepStatus.Current ? strings.wizardCurrent : strings.wizardUpcoming;\n return (\n <li key={candidate.id} className='foundation-wizard-step' data-status={status}>\n <button type='button' aria-current={status === StepStatus.Current ? 'step' : undefined} disabled={!open} onClick={() => go(candidate.id)}>\n <span className='foundation-wizard-marker'>{status === StepStatus.Completed ? <FoundationIcon name='check' /> : position + 1}</span>\n <span>{candidate.label}</span>\n <span className='foundation-visually-hidden'>{`, ${formatText(strings.wizardStep, { n: position + 1, total: steps.length })}, ${text}`}</span>\n </button>\n </li>\n );\n })}\n </ol>\n </nav>\n <div className='foundation-wizard-body' role='group' aria-label={`${formatText(strings.wizardStep, { n: index + 1, total: steps.length })}: ${step.label}`}>\n {slots?.[step.id]}\n </div>\n <div className='foundation-wizard-actions'>\n <ActionButton emphasis='quiet' disabledReason={strings.disabledNoPermission} onClick={() => { setDraftSaved(true); interactions?.onClick?.({ stopPropagation: () => undefined }); }}>{strings.wizardSaveDraft}</ActionButton>\n <span role='status' className='foundation-count'>{draftSaved ? strings.wizardDraftSaved : ''}</span>\n <span className='foundation-wizard-spacer' />\n {index > 0 && <ActionButton emphasis='secondary' disabledReason={strings.disabledNoPermission} onClick={() => go(neighbour(steps, current, -1)?.id)}>{strings.wizardBack}</ActionButton>}\n <button type='button' className='foundation-button' aria-disabled={!step.valid || undefined} onClick={last ? finish : next}>{last ? strings.wizardFinish : strings.wizardNext}</button>\n </div>\n </>\n )}\n </FoundationFrame>\n );\n}\n"],"names":["storageKey","element","key","id","readSaved","saved","JSON","parse","window","sessionStorage","getItem","Set","Array","isArray","filter","writeSaved","completed","setItem","stringify","WizardProgress","interactions","slots","bindingOutputs","steps","useMemo","readWizardSteps","properties","urlKey","readOptionalString","requested","setRequested","useUrlState","setCompleted","useState","draftSaved","setDraftSaved","strings","readFoundationStrings","state","readComponentState","length","current","reachableStep","index","findIndex","step","last","useEffect","useValueOutput","go","undefined","onSelect","next","valid","current_","add","neighbour","finish","onClick","stopPropagation","_jsx","FoundationFrame","name","skeleton","ComponentState","Loading","_jsxs","_Fragment","nav","aria-label","wizardProgress","ol","className","map","candidate","position","status","statusOf","open","canOpen","text","StepStatus","Completed","wizardComplete","Current","wizardCurrent","wizardUpcoming","li","data-status","button","type","aria-current","disabled","span","FoundationIcon","label","formatText","wizardStep","n","total","div","role","ActionButton","emphasis","disabledReason","disabledNoPermission","wizardSaveDraft","wizardDraftSaved","wizardBack","aria-disabled","wizardFinish","wizardNext"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface BoardColumn {
2
+ id: string;
3
+ label: string;
4
+ limit: number;
5
+ }
6
+ export interface BoardCard {
7
+ id: string;
8
+ title: string;
9
+ column: string;
10
+ assignee?: string;
11
+ fields: {
12
+ label: string;
13
+ value: string;
14
+ }[];
15
+ badges: string[];
16
+ }
17
+ export declare function readColumnsOfBoard(value: unknown): BoardColumn[];
18
+ export declare function readCards(value: unknown): BoardCard[];
19
+ export declare function cardsOf(columns: BoardColumn[], cards: BoardCard[], column: string): BoardCard[];
20
+ export declare function moveCard(columns: BoardColumn[], cards: BoardCard[], id: string, to: string): BoardCard[];
21
+ export declare const overLimit: (column: BoardColumn, count: number) => boolean;
22
+ //# sourceMappingURL=boardModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardModel.d.ts","sourceRoot":"","sources":["../../../foundation/boardModel.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAOhE;AAGD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,EAAE,CAYrD;AAGD,wBAAgB,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAG/F;AAGD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,CAIxG;AAGD,eAAO,MAAM,SAAS,WAAY,WAAW,SAAS,MAAM,KAAG,OAAmD,CAAC"}
@@ -0,0 +1,61 @@
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
+ /** One column of a board, mapped to a status or stage value. */ const records = (value)=>Array.isArray(value) ? value.filter((entry)=>typeof entry === 'object' && entry !== null && !Array.isArray(entry)) : [];
4
+ /** Reads columns, dropping those without an id and repeated ids. */ function readColumnsOfBoard(value) {
5
+ const seen = new Set();
6
+ return records(value).flatMap((entry)=>{
7
+ if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id)) return [];
8
+ seen.add(entry.id);
9
+ return [
10
+ {
11
+ id: entry.id,
12
+ label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.id,
13
+ limit: typeof entry.limit === 'number' && entry.limit > 0 ? Math.floor(entry.limit) : 0
14
+ }
15
+ ];
16
+ });
17
+ }
18
+ /** Reads cards, dropping those without an id or a title, keeping at most three key fields. */ function readCards(value) {
19
+ const seen = new Set();
20
+ return records(value).flatMap((entry)=>{
21
+ if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id) || typeof entry.title !== 'string') return [];
22
+ seen.add(entry.id);
23
+ const fields = records(entry.fields).flatMap((field)=>typeof field.label === 'string' && typeof field.value === 'string' ? [
24
+ {
25
+ label: field.label,
26
+ value: field.value
27
+ }
28
+ ] : []).slice(0, 3);
29
+ return [
30
+ {
31
+ id: entry.id,
32
+ title: entry.title,
33
+ column: typeof entry.column === 'string' ? entry.column : '',
34
+ fields,
35
+ badges: Array.isArray(entry.badges) ? entry.badges.filter((badge)=>typeof badge === 'string') : [],
36
+ ...typeof entry.assignee === 'string' ? {
37
+ assignee: entry.assignee
38
+ } : {}
39
+ }
40
+ ];
41
+ });
42
+ }
43
+ /** The cards of one column, in the order given. A card whose column does not exist is shown in the first column rather than lost. */ function cardsOf(columns, cards, column) {
44
+ const known = new Set(columns.map((candidate)=>candidate.id));
45
+ return cards.filter((card)=>card.column === column || column === columns[0]?.id && !known.has(card.column));
46
+ }
47
+ /** Moves a card to a column, at the end; moving to the column it is in, or to one that does not exist, changes nothing. */ function moveCard(columns, cards, id, to) {
48
+ const card = cards.find((candidate)=>candidate.id === id);
49
+ if (!card || !columns.some((column)=>column.id === to) || cardsOf(columns, cards, to).includes(card)) return cards;
50
+ return [
51
+ ...cards.filter((candidate)=>candidate.id !== id),
52
+ {
53
+ ...card,
54
+ column: to
55
+ }
56
+ ];
57
+ }
58
+ /** Whether a column holds more cards than its limit. */ const overLimit = (column, count)=>column.limit > 0 && count > column.limit;
59
+
60
+ export { cardsOf, moveCard, overLimit, readCards, readColumnsOfBoard };
61
+ //# sourceMappingURL=boardModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardModel.js","sources":["../../../foundation/boardModel.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/** One column of a board, mapped to a status or stage value. */\nexport interface BoardColumn {\n id: string;\n label: string;\n /** The most cards the stage should hold; 0 means no limit. */\n limit: number;\n}\n\n/** One card: the title, an assignee, a few key fields and badges. */\nexport interface BoardCard {\n id: string;\n title: string;\n column: string;\n assignee?: string;\n fields: { label: string; value: string }[];\n badges: string[];\n}\n\nconst records = (value: unknown): Record<string, unknown>[] =>\n Array.isArray(value) ? value.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null && !Array.isArray(entry)) : [];\n\n/** Reads columns, dropping those without an id and repeated ids. */\nexport function readColumnsOfBoard(value: unknown): BoardColumn[] {\n const seen = new Set<string>();\n return records(value).flatMap(entry => {\n if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id)) return [];\n seen.add(entry.id);\n return [{ id: entry.id, label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.id, limit: typeof entry.limit === 'number' && entry.limit > 0 ? Math.floor(entry.limit) : 0 }];\n });\n}\n\n/** Reads cards, dropping those without an id or a title, keeping at most three key fields. */\nexport function readCards(value: unknown): BoardCard[] {\n const seen = new Set<string>();\n return records(value).flatMap(entry => {\n if (typeof entry.id !== 'string' || entry.id.length === 0 || seen.has(entry.id) || typeof entry.title !== 'string') return [];\n seen.add(entry.id);\n const fields = records(entry.fields).flatMap(field => (typeof field.label === 'string' && typeof field.value === 'string' ? [{ label: field.label, value: field.value }] : [])).slice(0, 3);\n return [{\n id: entry.id, title: entry.title, column: typeof entry.column === 'string' ? entry.column : '', fields,\n badges: Array.isArray(entry.badges) ? entry.badges.filter((badge): badge is string => typeof badge === 'string') : [],\n ...(typeof entry.assignee === 'string' ? { assignee: entry.assignee } : {}),\n }];\n });\n}\n\n/** The cards of one column, in the order given. A card whose column does not exist is shown in the first column rather than lost. */\nexport function cardsOf(columns: BoardColumn[], cards: BoardCard[], column: string): BoardCard[] {\n const known = new Set(columns.map(candidate => candidate.id));\n return cards.filter(card => card.column === column || (column === columns[0]?.id && !known.has(card.column)));\n}\n\n/** Moves a card to a column, at the end; moving to the column it is in, or to one that does not exist, changes nothing. */\nexport function moveCard(columns: BoardColumn[], cards: BoardCard[], id: string, to: string): BoardCard[] {\n const card = cards.find(candidate => candidate.id === id);\n if (!card || !columns.some(column => column.id === to) || cardsOf(columns, cards, to).includes(card)) return cards;\n return [...cards.filter(candidate => candidate.id !== id), { ...card, column: to }];\n}\n\n/** Whether a column holds more cards than its limit. */\nexport const overLimit = (column: BoardColumn, count: number): boolean => column.limit > 0 && count > column.limit;\n"],"names":["records","value","Array","isArray","filter","entry","readColumnsOfBoard","seen","Set","flatMap","id","length","has","add","label","limit","Math","floor","readCards","title","fields","field","slice","column","badges","badge","assignee","cardsOf","columns","cards","known","map","candidate","card","moveCard","to","find","some","includes","overLimit","count"],"mappings":"AAAA;AACA;AAEA,iEAkBA,MAAMA,OAAAA,GAAU,CAACC,KAAAA,GACbC,MAAMC,OAAO,CAACF,KAAAA,CAAAA,GAASA,KAAAA,CAAMG,MAAM,CAAC,CAACC,KAAAA,GAA4C,OAAOA,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQ,CAACH,KAAAA,CAAMC,OAAO,CAACE,KAAAA,CAAAA,CAAAA,GAAU,EAAE;AAE/J,qEACO,SAASC,kBAAAA,CAAmBL,KAAc,EAAA;AAC7C,IAAA,MAAMM,OAAO,IAAIC,GAAAA,EAAAA;AACjB,IAAA,OAAOR,OAAAA,CAAQC,KAAAA,CAAAA,CAAOQ,OAAO,CAACJ,CAAAA,KAAAA,GAAAA;AAC1B,QAAA,IAAI,OAAOA,KAAAA,CAAMK,EAAE,KAAK,QAAA,IAAYL,KAAAA,CAAMK,EAAE,CAACC,MAAM,KAAK,CAAA,IAAKJ,KAAKK,GAAG,CAACP,MAAMK,EAAE,CAAA,EAAG,OAAO,EAAE;QAC1FH,IAAAA,CAAKM,GAAG,CAACR,KAAAA,CAAMK,EAAE,CAAA;QACjB,OAAO;AAAC,YAAA;AAAEA,gBAAAA,EAAAA,EAAIL,MAAMK,EAAE;AAAEI,gBAAAA,KAAAA,EAAO,OAAOT,KAAAA,CAAMS,KAAK,KAAK,YAAYT,KAAAA,CAAMS,KAAK,CAACH,MAAM,GAAG,CAAA,GAAIN,KAAAA,CAAMS,KAAK,GAAGT,MAAMK,EAAE;AAAEK,gBAAAA,KAAAA,EAAO,OAAOV,KAAAA,CAAMU,KAAK,KAAK,YAAYV,KAAAA,CAAMU,KAAK,GAAG,CAAA,GAAIC,IAAAA,CAAKC,KAAK,CAACZ,KAAAA,CAAMU,KAAK,CAAA,GAAI;AAAE;AAAE,SAAA;AACjN,IAAA,CAAA,CAAA;AACJ;AAEA,+FACO,SAASG,SAAAA,CAAUjB,KAAc,EAAA;AACpC,IAAA,MAAMM,OAAO,IAAIC,GAAAA,EAAAA;AACjB,IAAA,OAAOR,OAAAA,CAAQC,KAAAA,CAAAA,CAAOQ,OAAO,CAACJ,CAAAA,KAAAA,GAAAA;QAC1B,IAAI,OAAOA,MAAMK,EAAE,KAAK,YAAYL,KAAAA,CAAMK,EAAE,CAACC,MAAM,KAAK,CAAA,IAAKJ,KAAKK,GAAG,CAACP,KAAAA,CAAMK,EAAE,CAAA,IAAK,OAAOL,MAAMc,KAAK,KAAK,QAAA,EAAU,OAAO,EAAE;QAC7HZ,IAAAA,CAAKM,GAAG,CAACR,KAAAA,CAAMK,EAAE,CAAA;AACjB,QAAA,MAAMU,SAASpB,OAAAA,CAAQK,KAAAA,CAAMe,MAAM,CAAA,CAAEX,OAAO,CAACY,CAAAA,KAAAA,GAAU,OAAOA,KAAAA,CAAMP,KAAK,KAAK,QAAA,IAAY,OAAOO,KAAAA,CAAMpB,KAAK,KAAK,QAAA,GAAW;AAAC,gBAAA;AAAEa,oBAAAA,KAAAA,EAAOO,MAAMP,KAAK;AAAEb,oBAAAA,KAAAA,EAAOoB,MAAMpB;AAAM;AAAE,aAAA,GAAG,EAAE,CAAA,CAAGqB,KAAK,CAAC,CAAA,EAAG,CAAA,CAAA;QACzL,OAAO;AAAC,YAAA;AACJZ,gBAAAA,EAAAA,EAAIL,MAAMK,EAAE;AAAES,gBAAAA,KAAAA,EAAOd,MAAMc,KAAK;AAAEI,gBAAAA,MAAAA,EAAQ,OAAOlB,KAAAA,CAAMkB,MAAM,KAAK,QAAA,GAAWlB,KAAAA,CAAMkB,MAAM,GAAG,EAAA;AAAIH,gBAAAA,MAAAA;AAChGI,gBAAAA,MAAAA,EAAQtB,MAAMC,OAAO,CAACE,KAAAA,CAAMmB,MAAM,IAAInB,KAAAA,CAAMmB,MAAM,CAACpB,MAAM,CAAC,CAACqB,KAAAA,GAA2B,OAAOA,KAAAA,KAAU,YAAY,EAAE;AACrH,gBAAA,GAAI,OAAOpB,KAAAA,CAAMqB,QAAQ,KAAK,QAAA,GAAW;AAAEA,oBAAAA,QAAAA,EAAUrB,MAAMqB;AAAS,iBAAA,GAAI;AAC5E;AAAE,SAAA;AACN,IAAA,CAAA,CAAA;AACJ;AAEA,sIACO,SAASC,OAAAA,CAAQC,OAAsB,EAAEC,KAAkB,EAAEN,MAAc,EAAA;IAC9E,MAAMO,KAAAA,GAAQ,IAAItB,GAAAA,CAAIoB,OAAAA,CAAQG,GAAG,CAACC,CAAAA,SAAAA,GAAaA,SAAAA,CAAUtB,EAAE,CAAA,CAAA;IAC3D,OAAOmB,KAAAA,CAAMzB,MAAM,CAAC6B,CAAAA,OAAQA,IAAAA,CAAKV,MAAM,KAAKA,MAAAA,IAAWA,MAAAA,KAAWK,OAAO,CAAC,CAAA,CAAE,EAAElB,EAAAA,IAAM,CAACoB,MAAMlB,GAAG,CAACqB,KAAKV,MAAM,CAAA,CAAA;AAC9G;AAEA,4HACO,SAASW,QAAAA,CAASN,OAAsB,EAAEC,KAAkB,EAAEnB,EAAU,EAAEyB,EAAU,EAAA;IACvF,MAAMF,IAAAA,GAAOJ,MAAMO,IAAI,CAACJ,CAAAA,SAAAA,GAAaA,SAAAA,CAAUtB,EAAE,KAAKA,EAAAA,CAAAA;AACtD,IAAA,IAAI,CAACuB,IAAAA,IAAQ,CAACL,QAAQS,IAAI,CAACd,CAAAA,MAAAA,GAAUA,MAAAA,CAAOb,EAAE,KAAKyB,EAAAA,CAAAA,IAAOR,QAAQC,OAAAA,EAASC,KAAAA,EAAOM,IAAIG,QAAQ,CAACL,OAAO,OAAOJ,KAAAA;IAC7G,OAAO;AAAIA,QAAAA,GAAAA,KAAAA,CAAMzB,MAAM,CAAC4B,CAAAA,SAAAA,GAAaA,SAAAA,CAAUtB,EAAE,KAAKA,EAAAA,CAAAA;AAAK,QAAA;AAAE,YAAA,GAAGuB,IAAI;YAAEV,MAAAA,EAAQY;AAAG;AAAE,KAAA;AACvF;AAEA,yDACO,MAAMI,SAAAA,GAAY,CAAChB,MAAAA,EAAqBiB,KAAAA,GAA2BjB,MAAAA,CAAOR,KAAK,GAAG,CAAA,IAAKyB,KAAAA,GAAQjB,MAAAA,CAAOR;;"}
@@ -0,0 +1,48 @@
1
+ export declare enum FormFieldType {
2
+ Text = "text",
3
+ Number = "number",
4
+ Currency = "currency",
5
+ Date = "date",
6
+ Select = "select",
7
+ MultiSelect = "multiSelect",
8
+ Lookup = "lookup",
9
+ Toggle = "toggle",
10
+ File = "file",
11
+ RichText = "richText",
12
+ Address = "address"
13
+ }
14
+ export interface FormField {
15
+ name: string;
16
+ label: string;
17
+ type: FormFieldType;
18
+ required: boolean;
19
+ options: string[];
20
+ span: number;
21
+ placeholder?: string;
22
+ help?: string;
23
+ }
24
+ export interface FormSection {
25
+ id: string;
26
+ title: string;
27
+ columns: number;
28
+ fields: FormField[];
29
+ }
30
+ export type FormValues = Record<string, unknown>;
31
+ export declare enum FormMode {
32
+ Create = "create",
33
+ Edit = "edit",
34
+ ReadOnly = "readOnly",
35
+ Settings = "settings"
36
+ }
37
+ export declare const addressLines: readonly ['street', 'postalCode', 'city', 'country'];
38
+ export declare const readFormMode: (value: unknown) => FormMode;
39
+ export declare function readFormSections(value: unknown): FormSection[];
40
+ export declare function isFilled(field: FormField, value: unknown): boolean;
41
+ export declare function problemOf(field: FormField, value: unknown): 'required' | 'invalid' | undefined;
42
+ export declare function validate(sections: FormSection[], values: FormValues): Record<string, 'required' | 'invalid'>;
43
+ export declare function sameValues(left: FormValues, right: FormValues): boolean;
44
+ export declare function mapServerErrors(sections: FormSection[], errors: unknown): {
45
+ byField: Record<string, string>;
46
+ general: string[];
47
+ };
48
+ //# sourceMappingURL=formModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formModel.d.ts","sourceRoot":"","sources":["../../../foundation/formModel.ts"],"names":[],"mappings":"AAIA,oBAAY,aAAa;IACrB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,WAAW,gBAAgB;IAC3B,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,OAAO,YAAY;CACtB;AAGD,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;CACvB;AAGD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAGjD,oBAAY,QAAQ;IAChB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,QAAQ,aAAa;CACxB;AAGD,eAAO,MAAM,YAAY,YAAI,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AAMjF,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,QAAqH,CAAC;AAGpK,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAoB9D;AAGD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CASlE;AAGD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAS9F;AAGD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,CAQ5G;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAGvE;AAUD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAchI"}
@@ -0,0 +1,147 @@
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
+ /** The field types a form offers (Part B 2.8). */ var FormFieldType = /*#__PURE__*/ function(FormFieldType) {
4
+ FormFieldType["Text"] = "text";
5
+ FormFieldType["Number"] = "number";
6
+ FormFieldType["Currency"] = "currency";
7
+ FormFieldType["Date"] = "date";
8
+ FormFieldType["Select"] = "select";
9
+ FormFieldType["MultiSelect"] = "multiSelect";
10
+ FormFieldType["Lookup"] = "lookup";
11
+ FormFieldType["Toggle"] = "toggle";
12
+ FormFieldType["File"] = "file";
13
+ FormFieldType["RichText"] = "richText";
14
+ FormFieldType["Address"] = "address";
15
+ return FormFieldType;
16
+ }({});
17
+ /** The ways the one form can be used (Part B 2.8). */ var FormMode = /*#__PURE__*/ function(FormMode) {
18
+ FormMode["Create"] = "create";
19
+ FormMode["Edit"] = "edit";
20
+ FormMode["ReadOnly"] = "readOnly";
21
+ FormMode["Settings"] = "settings";
22
+ return FormMode;
23
+ }({});
24
+ /** The lines of an address, in the order they are asked. */ const addressLines = [
25
+ 'street',
26
+ 'postalCode',
27
+ 'city',
28
+ 'country'
29
+ ];
30
+ const records = (value)=>Array.isArray(value) ? value.filter((entry)=>typeof entry === 'object' && entry !== null && !Array.isArray(entry)) : [];
31
+ /** Reads the mode, defaulting to create. */ const readFormMode = (value)=>Object.values(FormMode).includes(value) ? value : "create";
32
+ /** Reads sections, with their fields, dropping fields without a name and repeated names; a section grid is clamped to one to three columns. */ function readFormSections(value) {
33
+ const names = new Set();
34
+ return records(value).flatMap((section, index)=>{
35
+ const columns = typeof section.columns === 'number' ? Math.min(3, Math.max(1, Math.floor(section.columns))) : 1;
36
+ const fields = records(section.fields).flatMap((entry)=>{
37
+ if (typeof entry.name !== 'string' || entry.name.length === 0 || names.has(entry.name)) return [];
38
+ names.add(entry.name);
39
+ return [
40
+ {
41
+ name: entry.name,
42
+ label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.name,
43
+ type: Object.values(FormFieldType).includes(entry.type) ? entry.type : "text",
44
+ required: entry.required === true,
45
+ options: Array.isArray(entry.options) ? entry.options.filter((option)=>typeof option === 'string') : [],
46
+ span: typeof entry.span === 'number' ? Math.min(columns, Math.max(1, Math.floor(entry.span))) : 1,
47
+ ...typeof entry.placeholder === 'string' ? {
48
+ placeholder: entry.placeholder
49
+ } : {},
50
+ ...typeof entry.help === 'string' ? {
51
+ help: entry.help
52
+ } : {}
53
+ }
54
+ ];
55
+ });
56
+ return [
57
+ {
58
+ id: typeof section.id === 'string' && section.id.length > 0 ? section.id : `section-${index}`,
59
+ title: typeof section.title === 'string' ? section.title : '',
60
+ columns,
61
+ fields
62
+ }
63
+ ];
64
+ });
65
+ }
66
+ /** Whether a field holds a value: an empty or blank text, an empty list, a toggle left false (a required toggle must be on) or an address with no street and city are empty. */ function isFilled(field, value) {
67
+ switch(field.type){
68
+ case "toggle":
69
+ return value === true;
70
+ case "multiSelect":
71
+ return Array.isArray(value) && value.length > 0;
72
+ case "file":
73
+ return Array.isArray(value) ? value.length > 0 : typeof value === 'string' && value.trim().length > 0;
74
+ case "address":
75
+ return typeof value === 'object' && value !== null && [
76
+ 'street',
77
+ 'city'
78
+ ].every((line)=>typeof value[line] === 'string' && value[line].trim().length > 0);
79
+ case "number":
80
+ case "currency":
81
+ return typeof value === 'number' ? Number.isFinite(value) : typeof value === 'string' && value.trim().length > 0 && Number.isFinite(Number(value));
82
+ default:
83
+ return typeof value === 'string' ? value.trim().length > 0 : value !== undefined && value !== null && value !== '';
84
+ }
85
+ }
86
+ /** Why a field is invalid, as a kind the component words in the locale: `required`, `invalid`, or undefined when it is fine. An empty optional field is fine. */ function problemOf(field, value) {
87
+ if (!isFilled(field, value)) {
88
+ const empty = value === undefined || value === null || value === '' || typeof value === 'string' && value.trim() === '' || Array.isArray(value) && value.length === 0 || value === false;
89
+ return field.required ? 'required' : empty ? undefined : 'invalid';
90
+ }
91
+ if (field.type === "date" && typeof value === 'string' && Number.isNaN(new Date(value).getTime())) return 'invalid';
92
+ if ((field.type === "select" || field.type === "lookup") && field.options.length > 0 && typeof value === 'string' && !field.options.includes(value)) return 'invalid';
93
+ return undefined;
94
+ }
95
+ /** The problems of every field, by field name, for the fields in the given sections. */ function validate(sections, values) {
96
+ const problems = {};
97
+ for (const field of sections.flatMap((section)=>section.fields)){
98
+ const problem = problemOf(field, values[field.name]);
99
+ if (problem) problems[field.name] = problem;
100
+ }
101
+ return problems;
102
+ }
103
+ /** Whether two sets of values are the same, field by field and in depth, so the unsaved-changes guard compares what the person sees. */ function sameValues(left, right) {
104
+ const keys = new Set([
105
+ ...Object.keys(left),
106
+ ...Object.keys(right)
107
+ ]);
108
+ return [
109
+ ...keys
110
+ ].every((key)=>JSON.stringify(normalizeValue(left[key])) === JSON.stringify(normalizeValue(right[key])));
111
+ }
112
+ function normalizeValue(value) {
113
+ if (value === undefined || value === null || value === '' || value === false) return null;
114
+ if (Array.isArray(value)) return value.length === 0 ? null : value;
115
+ if (typeof value === 'object') {
116
+ const entries = Object.entries(value).filter(([, line])=>line !== '' && line !== undefined).sort(([a], [b])=>a.localeCompare(b));
117
+ return entries.length === 0 ? null : entries;
118
+ }
119
+ return value;
120
+ }
121
+ /** Maps a server's field errors (`{ field: message }` or `[{ field, message }]`) onto the fields of the form; an error for a field the form does not have is returned as a general one so it is never lost. */ function mapServerErrors(sections, errors) {
122
+ const known = new Set(sections.flatMap((section)=>section.fields.map((field)=>field.name)));
123
+ const pairs = Array.isArray(errors) ? records(errors).flatMap((entry)=>typeof entry.message === 'string' ? [
124
+ [
125
+ typeof entry.field === 'string' ? entry.field : '',
126
+ entry.message
127
+ ]
128
+ ] : []) : typeof errors === 'object' && errors !== null ? Object.entries(errors).flatMap(([field, message])=>typeof message === 'string' ? [
129
+ [
130
+ field,
131
+ message
132
+ ]
133
+ ] : []) : [];
134
+ const byField = {};
135
+ const general = [];
136
+ for (const [field, message] of pairs){
137
+ if (known.has(field) && byField[field] === undefined) byField[field] = message;
138
+ else general.push(field && !known.has(field) ? `${field}: ${message}` : message);
139
+ }
140
+ return {
141
+ byField,
142
+ general
143
+ };
144
+ }
145
+
146
+ export { FormFieldType, FormMode, addressLines, isFilled, mapServerErrors, problemOf, readFormMode, readFormSections, sameValues, validate };
147
+ //# sourceMappingURL=formModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formModel.js","sources":["../../../foundation/formModel.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/** The field types a form offers (Part B 2.8). */\nexport enum FormFieldType {\n Text = 'text',\n Number = 'number',\n Currency = 'currency',\n Date = 'date',\n Select = 'select',\n MultiSelect = 'multiSelect',\n Lookup = 'lookup',\n Toggle = 'toggle',\n File = 'file',\n RichText = 'richText',\n Address = 'address',\n}\n\n/** One field of a form section. */\nexport interface FormField {\n name: string;\n label: string;\n type: FormFieldType;\n required: boolean;\n options: string[];\n /** The columns of the section grid the field spans (1 to 3). */\n span: number;\n placeholder?: string;\n help?: string;\n}\n\n/** A titled group of fields in a grid of one to three columns. */\nexport interface FormSection {\n id: string;\n title: string;\n columns: number;\n fields: FormField[];\n}\n\n/** The values of a form by field name; a multi-select is an array, a toggle a boolean, an address an object of lines. */\nexport type FormValues = Record<string, unknown>;\n\n/** The ways the one form can be used (Part B 2.8). */\nexport enum FormMode {\n Create = 'create',\n Edit = 'edit',\n ReadOnly = 'readOnly',\n Settings = 'settings',\n}\n\n/** The lines of an address, in the order they are asked. */\nexport const addressLines = ['street', 'postalCode', 'city', 'country'] as const;\n\nconst records = (value: unknown): Record<string, unknown>[] =>\n Array.isArray(value) ? value.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null && !Array.isArray(entry)) : [];\n\n/** Reads the mode, defaulting to create. */\nexport const readFormMode = (value: unknown): FormMode => ((Object.values(FormMode) as string[]).includes(value as string) ? (value as FormMode) : FormMode.Create);\n\n/** Reads sections, with their fields, dropping fields without a name and repeated names; a section grid is clamped to one to three columns. */\nexport function readFormSections(value: unknown): FormSection[] {\n const names = new Set<string>();\n return records(value).flatMap((section, index) => {\n const columns = typeof section.columns === 'number' ? Math.min(3, Math.max(1, Math.floor(section.columns))) : 1;\n const fields = records(section.fields).flatMap((entry): FormField[] => {\n if (typeof entry.name !== 'string' || entry.name.length === 0 || names.has(entry.name)) return [];\n names.add(entry.name);\n return [{\n name: entry.name,\n label: typeof entry.label === 'string' && entry.label.length > 0 ? entry.label : entry.name,\n type: (Object.values(FormFieldType) as string[]).includes(entry.type as string) ? (entry.type as FormFieldType) : FormFieldType.Text,\n required: entry.required === true,\n options: Array.isArray(entry.options) ? entry.options.filter((option): option is string => typeof option === 'string') : [],\n span: typeof entry.span === 'number' ? Math.min(columns, Math.max(1, Math.floor(entry.span))) : 1,\n ...(typeof entry.placeholder === 'string' ? { placeholder: entry.placeholder } : {}),\n ...(typeof entry.help === 'string' ? { help: entry.help } : {}),\n }];\n });\n return [{ id: typeof section.id === 'string' && section.id.length > 0 ? section.id : `section-${index}`, title: typeof section.title === 'string' ? section.title : '', columns, fields }];\n });\n}\n\n/** Whether a field holds a value: an empty or blank text, an empty list, a toggle left false (a required toggle must be on) or an address with no street and city are empty. */\nexport function isFilled(field: FormField, value: unknown): boolean {\n switch (field.type) {\n case FormFieldType.Toggle: return value === true;\n case FormFieldType.MultiSelect: return Array.isArray(value) && value.length > 0;\n case FormFieldType.File: return Array.isArray(value) ? value.length > 0 : typeof value === 'string' && value.trim().length > 0;\n case FormFieldType.Address: return typeof value === 'object' && value !== null && ['street', 'city'].every(line => typeof (value as Record<string, unknown>)[line] === 'string' && ((value as Record<string, unknown>)[line] as string).trim().length > 0);\n case FormFieldType.Number: case FormFieldType.Currency: return typeof value === 'number' ? Number.isFinite(value) : typeof value === 'string' && value.trim().length > 0 && Number.isFinite(Number(value));\n default: return typeof value === 'string' ? value.trim().length > 0 : value !== undefined && value !== null && value !== '';\n }\n}\n\n/** Why a field is invalid, as a kind the component words in the locale: `required`, `invalid`, or undefined when it is fine. An empty optional field is fine. */\nexport function problemOf(field: FormField, value: unknown): 'required' | 'invalid' | undefined {\n if (!isFilled(field, value)) {\n const empty = value === undefined || value === null || value === '' || (typeof value === 'string' && value.trim() === '') || (Array.isArray(value) && value.length === 0) || value === false;\n return field.required ? 'required' : empty ? undefined : 'invalid';\n }\n\n if (field.type === FormFieldType.Date && typeof value === 'string' && Number.isNaN(new Date(value).getTime())) return 'invalid';\n if ((field.type === FormFieldType.Select || field.type === FormFieldType.Lookup) && field.options.length > 0 && typeof value === 'string' && !field.options.includes(value)) return 'invalid';\n return undefined;\n}\n\n/** The problems of every field, by field name, for the fields in the given sections. */\nexport function validate(sections: FormSection[], values: FormValues): Record<string, 'required' | 'invalid'> {\n const problems: Record<string, 'required' | 'invalid'> = {};\n for (const field of sections.flatMap(section => section.fields)) {\n const problem = problemOf(field, values[field.name]);\n if (problem) problems[field.name] = problem;\n }\n\n return problems;\n}\n\n/** Whether two sets of values are the same, field by field and in depth, so the unsaved-changes guard compares what the person sees. */\nexport function sameValues(left: FormValues, right: FormValues): boolean {\n const keys = new Set([...Object.keys(left), ...Object.keys(right)]);\n return [...keys].every(key => JSON.stringify(normalizeValue(left[key])) === JSON.stringify(normalizeValue(right[key])));\n}\n\nfunction normalizeValue(value: unknown): unknown {\n if (value === undefined || value === null || value === '' || value === false) return null;\n if (Array.isArray(value)) return value.length === 0 ? null : value;\n if (typeof value === 'object') { const entries = Object.entries(value as Record<string, unknown>).filter(([, line]) => line !== '' && line !== undefined).sort(([a], [b]) => a.localeCompare(b)); return entries.length === 0 ? null : entries; }\n return value;\n}\n\n/** Maps a server's field errors (`{ field: message }` or `[{ field, message }]`) onto the fields of the form; an error for a field the form does not have is returned as a general one so it is never lost. */\nexport function mapServerErrors(sections: FormSection[], errors: unknown): { byField: Record<string, string>; general: string[] } {\n const known = new Set(sections.flatMap(section => section.fields.map(field => field.name)));\n const pairs: [string, string][] = Array.isArray(errors)\n ? records(errors).flatMap(entry => (typeof entry.message === 'string' ? [[typeof entry.field === 'string' ? entry.field : '', entry.message] as [string, string]] : []))\n : typeof errors === 'object' && errors !== null ? Object.entries(errors as Record<string, unknown>).flatMap(([field, message]) => (typeof message === 'string' ? [[field, message] as [string, string]] : [])) : [];\n\n const byField: Record<string, string> = {};\n const general: string[] = [];\n for (const [field, message] of pairs) {\n if (known.has(field) && byField[field] === undefined) byField[field] = message;\n else general.push(field && !known.has(field) ? `${field}: ${message}` : message);\n }\n\n return { byField, general };\n}\n"],"names":["FormFieldType","FormMode","addressLines","records","value","Array","isArray","filter","entry","readFormMode","Object","values","includes","readFormSections","names","Set","flatMap","section","index","columns","Math","min","max","floor","fields","name","length","has","add","label","type","required","options","option","span","placeholder","help","id","title","isFilled","field","trim","every","line","Number","isFinite","undefined","problemOf","empty","isNaN","Date","getTime","validate","sections","problems","problem","sameValues","left","right","keys","key","JSON","stringify","normalizeValue","entries","sort","a","b","localeCompare","mapServerErrors","errors","known","map","pairs","message","byField","general","push"],"mappings":"AAAA;AACA;AAEA,mDACO,IAAKA,aAAAA,iBAAAA,SAAAA,aAAAA,EAAAA;;;;;;;;;;;;AAAAA,IAAAA,OAAAA,aAAAA;AAYX,CAAA,CAAA,EAAA;AA0BD,uDACO,IAAKC,QAAAA,iBAAAA,SAAAA,QAAAA,EAAAA;;;;;AAAAA,IAAAA,OAAAA,QAAAA;AAKX,CAAA,CAAA,EAAA;AAED,6DACO,MAAMC,YAAAA,GAAe;AAAC,IAAA,QAAA;AAAU,IAAA,YAAA;AAAc,IAAA,MAAA;AAAQ,IAAA;;AAE7D,MAAMC,OAAAA,GAAU,CAACC,KAAAA,GACbC,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,GAASA,KAAAA,CAAMG,MAAM,CAAC,CAACC,QAA4C,OAAOA,KAAAA,KAAU,YAAYA,KAAAA,KAAU,IAAA,IAAQ,CAACH,KAAAA,CAAMC,OAAO,CAACE,KAAAA,CAAAA,CAAAA,GAAU,EAAE;AAE/J,6CACO,MAAMC,YAAAA,GAAe,CAACL,KAAAA,GAA+BM,MAAAA,CAAOC,MAAM,CAACV,QAAAA,CAAAA,CAAuBW,QAAQ,CAACR,SAAoBA,KAAAA,GAAAA;AAE9H,gJACO,SAASS,gBAAAA,CAAiBT,KAAc,EAAA;AAC3C,IAAA,MAAMU,QAAQ,IAAIC,GAAAA,EAAAA;AAClB,IAAA,OAAOZ,OAAAA,CAAQC,KAAAA,CAAAA,CAAOY,OAAO,CAAC,CAACC,OAAAA,EAASC,KAAAA,GAAAA;AACpC,QAAA,MAAMC,UAAU,OAAOF,OAAAA,CAAQE,OAAO,KAAK,QAAA,GAAWC,KAAKC,GAAG,CAAC,GAAGD,IAAAA,CAAKE,GAAG,CAAC,CAAA,EAAGF,IAAAA,CAAKG,KAAK,CAACN,OAAAA,CAAQE,OAAO,CAAA,CAAA,CAAA,GAAM,CAAA;AAC9G,QAAA,MAAMK,SAASrB,OAAAA,CAAQc,OAAAA,CAAQO,MAAM,CAAA,CAAER,OAAO,CAAC,CAACR,KAAAA,GAAAA;AAC5C,YAAA,IAAI,OAAOA,KAAAA,CAAMiB,IAAI,KAAK,QAAA,IAAYjB,KAAAA,CAAMiB,IAAI,CAACC,MAAM,KAAK,CAAA,IAAKZ,MAAMa,GAAG,CAACnB,MAAMiB,IAAI,CAAA,EAAG,OAAO,EAAE;YACjGX,KAAAA,CAAMc,GAAG,CAACpB,KAAAA,CAAMiB,IAAI,CAAA;YACpB,OAAO;AAAC,gBAAA;AACJA,oBAAAA,IAAAA,EAAMjB,MAAMiB,IAAI;AAChBI,oBAAAA,KAAAA,EAAO,OAAOrB,KAAAA,CAAMqB,KAAK,KAAK,YAAYrB,KAAAA,CAAMqB,KAAK,CAACH,MAAM,GAAG,CAAA,GAAIlB,KAAAA,CAAMqB,KAAK,GAAGrB,MAAMiB,IAAI;oBAC3FK,IAAAA,EAAOpB,MAAAA,CAAOC,MAAM,CAACX,aAAAA,CAAAA,CAA4BY,QAAQ,CAACJ,KAAAA,CAAMsB,IAAI,CAAA,GAAetB,KAAAA,CAAMsB,IAAI,GAAA,MAAA;oBAC7FC,QAAAA,EAAUvB,KAAAA,CAAMuB,QAAQ,KAAK,IAAA;AAC7BC,oBAAAA,OAAAA,EAAS3B,MAAMC,OAAO,CAACE,KAAAA,CAAMwB,OAAO,IAAIxB,KAAAA,CAAMwB,OAAO,CAACzB,MAAM,CAAC,CAAC0B,MAAAA,GAA6B,OAAOA,MAAAA,KAAW,YAAY,EAAE;AAC3HC,oBAAAA,IAAAA,EAAM,OAAO1B,KAAAA,CAAM0B,IAAI,KAAK,QAAA,GAAWd,IAAAA,CAAKC,GAAG,CAACF,OAAAA,EAASC,IAAAA,CAAKE,GAAG,CAAC,CAAA,EAAGF,IAAAA,CAAKG,KAAK,CAACf,KAAAA,CAAM0B,IAAI,CAAA,CAAA,CAAA,GAAM,CAAA;AAChG,oBAAA,GAAI,OAAO1B,KAAAA,CAAM2B,WAAW,KAAK,QAAA,GAAW;AAAEA,wBAAAA,WAAAA,EAAa3B,MAAM2B;AAAY,qBAAA,GAAI,EAAE;AACnF,oBAAA,GAAI,OAAO3B,KAAAA,CAAM4B,IAAI,KAAK,QAAA,GAAW;AAAEA,wBAAAA,IAAAA,EAAM5B,MAAM4B;AAAK,qBAAA,GAAI;AAChE;AAAE,aAAA;AACN,QAAA,CAAA,CAAA;QACA,OAAO;AAAC,YAAA;AAAEC,gBAAAA,EAAAA,EAAI,OAAOpB,OAAAA,CAAQoB,EAAE,KAAK,QAAA,IAAYpB,QAAQoB,EAAE,CAACX,MAAM,GAAG,IAAIT,OAAAA,CAAQoB,EAAE,GAAG,CAAC,QAAQ,EAAEnB,KAAAA,CAAAA,CAAO;AAAEoB,gBAAAA,KAAAA,EAAO,OAAOrB,OAAAA,CAAQqB,KAAK,KAAK,QAAA,GAAWrB,OAAAA,CAAQqB,KAAK,GAAG,EAAA;AAAInB,gBAAAA,OAAAA;AAASK,gBAAAA;AAAO;AAAE,SAAA;AAC9L,IAAA,CAAA,CAAA;AACJ;AAEA,iLACO,SAASe,QAAAA,CAASC,KAAgB,EAAEpC,KAAc,EAAA;AACrD,IAAA,OAAQoC,MAAMV,IAAI;AACd,QAAA,KAAA,QAAA;AAA2B,YAAA,OAAO1B,KAAAA,KAAU,IAAA;AAC5C,QAAA,KAAA,aAAA;AAAgC,YAAA,OAAOC,MAAMC,OAAO,CAACF,KAAAA,CAAAA,IAAUA,KAAAA,CAAMsB,MAAM,GAAG,CAAA;AAC9E,QAAA,KAAA,MAAA;AAAyB,YAAA,OAAOrB,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,GAASA,MAAMsB,MAAM,GAAG,CAAA,GAAI,OAAOtB,UAAU,QAAA,IAAYA,KAAAA,CAAMqC,IAAI,EAAA,CAAGf,MAAM,GAAG,CAAA;AAC7H,QAAA,KAAA,SAAA;AAA4B,YAAA,OAAO,OAAOtB,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQ;AAAC,gBAAA,QAAA;AAAU,gBAAA;aAAO,CAACsC,KAAK,CAACC,CAAAA,IAAAA,GAAQ,OAAO,KAAkC,CAACA,IAAAA,CAAK,KAAK,YAAcvC,KAAiC,CAACuC,IAAAA,CAAK,CAAYF,IAAI,EAAA,CAAGf,MAAM,GAAG,CAAA,CAAA;AACxP,QAAA,KAAA,QAAA;AAA2B,QAAA,KAAA,UAAA;AAA6B,YAAA,OAAO,OAAOtB,KAAAA,KAAU,QAAA,GAAWwC,OAAOC,QAAQ,CAACzC,SAAS,OAAOA,KAAAA,KAAU,YAAYA,KAAAA,CAAMqC,IAAI,GAAGf,MAAM,GAAG,KAAKkB,MAAAA,CAAOC,QAAQ,CAACD,MAAAA,CAAOxC,KAAAA,CAAAA,CAAAA;AACnM,QAAA;AAAS,YAAA,OAAO,OAAOA,KAAAA,KAAU,QAAA,GAAWA,KAAAA,CAAMqC,IAAI,EAAA,CAAGf,MAAM,GAAG,CAAA,GAAItB,KAAAA,KAAU0C,SAAAA,IAAa1C,KAAAA,KAAU,QAAQA,KAAAA,KAAU,EAAA;AAC7H;AACJ;AAEA,kKACO,SAAS2C,SAAAA,CAAUP,KAAgB,EAAEpC,KAAc,EAAA;IACtD,IAAI,CAACmC,QAAAA,CAASC,KAAAA,EAAOpC,KAAAA,CAAAA,EAAQ;QACzB,MAAM4C,KAAAA,GAAQ5C,UAAU0C,SAAAA,IAAa1C,KAAAA,KAAU,QAAQA,KAAAA,KAAU,EAAA,IAAO,OAAOA,KAAAA,KAAU,QAAA,IAAYA,KAAAA,CAAMqC,IAAI,EAAA,KAAO,EAAA,IAAQpC,MAAMC,OAAO,CAACF,UAAUA,KAAAA,CAAMsB,MAAM,KAAK,CAAA,IAAMtB,KAAAA,KAAU,KAAA;AACvL,QAAA,OAAOoC,KAAAA,CAAMT,QAAQ,GAAG,UAAA,GAAaiB,QAAQF,SAAAA,GAAY,SAAA;AAC7D,IAAA;AAEA,IAAA,IAAIN,KAAAA,CAAMV,IAAI,KAAA,MAAA,IAA2B,OAAO1B,UAAU,QAAA,IAAYwC,MAAAA,CAAOK,KAAK,CAAC,IAAIC,IAAAA,CAAK9C,KAAAA,CAAAA,CAAO+C,OAAO,KAAK,OAAO,SAAA;IACtH,IAAKX,CAAAA,KAAAA,CAAMV,IAAI,KAAA,QAAA,IAA6BU,MAAMV,IAAI,KAAA,QAAwB,KAAMU,KAAAA,CAAMR,OAAO,CAACN,MAAM,GAAG,CAAA,IAAK,OAAOtB,KAAAA,KAAU,QAAA,IAAY,CAACoC,KAAAA,CAAMR,OAAO,CAACpB,QAAQ,CAACR,KAAAA,CAAAA,EAAQ,OAAO,SAAA;IACpL,OAAO0C,SAAAA;AACX;AAEA,yFACO,SAASM,QAAAA,CAASC,QAAuB,EAAE1C,MAAkB,EAAA;AAChE,IAAA,MAAM2C,WAAmD,EAAC;IAC1D,KAAK,MAAMd,SAASa,QAAAA,CAASrC,OAAO,CAACC,CAAAA,OAAAA,GAAWA,OAAAA,CAAQO,MAAM,CAAA,CAAG;AAC7D,QAAA,MAAM+B,UAAUR,SAAAA,CAAUP,KAAAA,EAAO7B,MAAM,CAAC6B,KAAAA,CAAMf,IAAI,CAAC,CAAA;AACnD,QAAA,IAAI8B,SAASD,QAAQ,CAACd,KAAAA,CAAMf,IAAI,CAAC,GAAG8B,OAAAA;AACxC,IAAA;IAEA,OAAOD,QAAAA;AACX;AAEA,yIACO,SAASE,UAAAA,CAAWC,IAAgB,EAAEC,KAAiB,EAAA;IAC1D,MAAMC,IAAAA,GAAO,IAAI5C,GAAAA,CAAI;AAAIL,QAAAA,GAAAA,MAAAA,CAAOiD,IAAI,CAACF,IAAAA,CAAAA;AAAU/C,QAAAA,GAAAA,MAAAA,CAAOiD,IAAI,CAACD,KAAAA;AAAO,KAAA,CAAA;IAClE,OAAO;AAAIC,QAAAA,GAAAA;AAAK,KAAA,CAACjB,KAAK,CAACkB,CAAAA,MAAOC,IAAAA,CAAKC,SAAS,CAACC,cAAAA,CAAeN,IAAI,CAACG,GAAAA,CAAI,OAAOC,IAAAA,CAAKC,SAAS,CAACC,cAAAA,CAAeL,KAAK,CAACE,GAAAA,CAAI,CAAA,CAAA,CAAA;AACxH;AAEA,SAASG,eAAe3D,KAAc,EAAA;IAClC,IAAIA,KAAAA,KAAU0C,aAAa1C,KAAAA,KAAU,IAAA,IAAQA,UAAU,EAAA,IAAMA,KAAAA,KAAU,OAAO,OAAO,IAAA;IACrF,IAAIC,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,EAAQ,OAAOA,KAAAA,CAAMsB,MAAM,KAAK,CAAA,GAAI,IAAA,GAAOtB,KAAAA;IAC7D,IAAI,OAAOA,UAAU,QAAA,EAAU;QAAE,MAAM4D,OAAAA,GAAUtD,MAAAA,CAAOsD,OAAO,CAAC5D,KAAAA,CAAAA,CAAkCG,MAAM,CAAC,CAAC,GAAGoC,IAAAA,CAAK,GAAKA,IAAAA,KAAS,EAAA,IAAMA,SAASG,SAAAA,CAAAA,CAAWmB,IAAI,CAAC,CAAC,CAACC,CAAAA,CAAE,EAAE,CAACC,CAAAA,CAAE,GAAKD,CAAAA,CAAEE,aAAa,CAACD,CAAAA,CAAAA,CAAAA;AAAK,QAAA,OAAOH,OAAAA,CAAQtC,MAAM,KAAK,CAAA,GAAI,IAAA,GAAOsC,OAAAA;AAAS,IAAA;IAChP,OAAO5D,KAAAA;AACX;AAEA,gNACO,SAASiE,eAAAA,CAAgBhB,QAAuB,EAAEiB,MAAe,EAAA;AACpE,IAAA,MAAMC,QAAQ,IAAIxD,GAAAA,CAAIsC,QAAAA,CAASrC,OAAO,CAACC,CAAAA,OAAAA,GAAWA,OAAAA,CAAQO,MAAM,CAACgD,GAAG,CAAChC,CAAAA,KAAAA,GAASA,MAAMf,IAAI,CAAA,CAAA,CAAA;AACxF,IAAA,MAAMgD,KAAAA,GAA4BpE,KAAAA,CAAMC,OAAO,CAACgE,UAC1CnE,OAAAA,CAAQmE,MAAAA,CAAAA,CAAQtD,OAAO,CAACR,CAAAA,KAAAA,GAAU,OAAOA,KAAAA,CAAMkE,OAAO,KAAK,QAAA,GAAW;AAAC,YAAA;AAAC,gBAAA,OAAOlE,MAAMgC,KAAK,KAAK,QAAA,GAAWhC,KAAAA,CAAMgC,KAAK,GAAG,EAAA;AAAIhC,gBAAAA,KAAAA,CAAMkE;AAAQ;SAAqB,GAAG,EAAE,IACpK,OAAOJ,MAAAA,KAAW,YAAYA,MAAAA,KAAW,IAAA,GAAO5D,OAAOsD,OAAO,CAACM,QAAmCtD,OAAO,CAAC,CAAC,CAACwB,KAAAA,EAAOkC,QAAQ,GAAM,OAAOA,YAAY,QAAA,GAAW;AAAC,YAAA;AAAClC,gBAAAA,KAAAA;AAAOkC,gBAAAA;AAAQ;SAAqB,GAAG,EAAE,IAAK,EAAE;AAEvN,IAAA,MAAMC,UAAkC,EAAC;AACzC,IAAA,MAAMC,UAAoB,EAAE;AAC5B,IAAA,KAAK,MAAM,CAACpC,KAAAA,EAAOkC,OAAAA,CAAQ,IAAID,KAAAA,CAAO;AAClC,QAAA,IAAIF,KAAAA,CAAM5C,GAAG,CAACa,KAAAA,CAAAA,IAAUmC,OAAO,CAACnC,KAAAA,CAAM,KAAKM,SAAAA,EAAW6B,OAAO,CAACnC,KAAAA,CAAM,GAAGkC,OAAAA;AAClEE,aAAAA,OAAAA,CAAQC,IAAI,CAACrC,KAAAA,IAAS,CAAC+B,KAAAA,CAAM5C,GAAG,CAACa,KAAAA,CAAAA,GAAS,CAAA,EAAGA,KAAAA,CAAM,EAAE,EAAEkC,SAAS,GAAGA,OAAAA,CAAAA;AAC5E,IAAA;IAEA,OAAO;AAAEC,QAAAA,OAAAA;AAASC,QAAAA;AAAQ,KAAA;AAC9B;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"foundationComponentMetadata.d.ts","sourceRoot":"","sources":["../../../foundation/foundationComponentMetadata.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,qBAAqB,CAAC;AAOhF,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAgBzE,CAAC"}
1
+ {"version":3,"file":"foundationComponentMetadata.d.ts","sourceRoot":"","sources":["../../../foundation/foundationComponentMetadata.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,qBAAqB,CAAC;AAOhF,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAyBzE,CAAC"}
@@ -107,6 +107,60 @@ import { ComponentName } from '../ComponentName.js';
107
107
  'access',
108
108
  'grants',
109
109
  'rbac'
110
+ ]),
111
+ [ComponentName.DataList]: componentMetadata('Data list', ComponentCategory.DataDisplay, 'table', [
112
+ 'table',
113
+ 'grid',
114
+ 'records',
115
+ 'cards',
116
+ 'collection',
117
+ 'list'
118
+ ]),
119
+ [ComponentName.TabSet]: componentMetadata('Tab set', ComponentCategory.Navigation, 'panel-top', [
120
+ 'tabs',
121
+ 'sections',
122
+ 'panels'
123
+ ]),
124
+ [ComponentName.SubNavigation]: componentMetadata('Sub-navigation', ComponentCategory.Navigation, 'panel-left', [
125
+ 'module',
126
+ 'sections',
127
+ 'rail',
128
+ 'tabs'
129
+ ]),
130
+ [ComponentName.BulkBar]: componentMetadata('Bulk bar', ComponentCategory.Actions, 'check-check', [
131
+ 'selection',
132
+ 'bulk',
133
+ 'batch',
134
+ 'actions'
135
+ ]),
136
+ [ComponentName.GlobalSearch]: componentMetadata('Search', ComponentCategory.Input, 'search', [
137
+ 'search',
138
+ 'find',
139
+ 'query'
140
+ ]),
141
+ [ComponentName.NotificationsButton]: componentMetadata('Notifications button', ComponentCategory.Actions, 'bell', [
142
+ 'bell',
143
+ 'alerts',
144
+ 'unread'
145
+ ]),
146
+ [ComponentName.KanbanBoard]: componentMetadata('Kanban board', ComponentCategory.DataDisplay, 'kanban', [
147
+ 'board',
148
+ 'kanban',
149
+ 'columns',
150
+ 'pipeline',
151
+ 'cards'
152
+ ]),
153
+ [ComponentName.WizardProgress]: componentMetadata('Wizard', ComponentCategory.Navigation, 'footprints', [
154
+ 'wizard',
155
+ 'steps',
156
+ 'stepper',
157
+ 'multi-step'
158
+ ]),
159
+ [ComponentName.FormScreenBody]: componentMetadata('Form', ComponentCategory.Forms, 'text-cursor-input', [
160
+ 'form',
161
+ 'fields',
162
+ 'sections',
163
+ 'validation'
110
164
  ])
111
165
  };
112
166