@osdk/cbac-components 0.1.0-beta.3 → 0.1.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/CHANGELOG.md +10 -0
- package/build/browser/cbac-picker/CbacPicker.js +71 -0
- package/build/browser/cbac-picker/CbacPicker.js.map +1 -0
- package/build/browser/cbac-picker/CbacPickerDialog.js +88 -0
- package/build/browser/cbac-picker/CbacPickerDialog.js.map +1 -0
- package/build/browser/public/experimental.js +9 -0
- package/build/browser/public/experimental.js.map +1 -1
- package/build/cjs/public/experimental.cjs +590 -5
- package/build/cjs/public/experimental.cjs.map +1 -1
- package/build/cjs/public/experimental.css +297 -0
- package/build/cjs/public/experimental.css.map +1 -0
- package/build/cjs/public/experimental.d.cts +55 -1
- package/build/esm/cbac-picker/CbacPicker.js +71 -0
- package/build/esm/cbac-picker/CbacPicker.js.map +1 -0
- package/build/esm/cbac-picker/CbacPickerDialog.js +88 -0
- package/build/esm/cbac-picker/CbacPickerDialog.js.map +1 -0
- package/build/esm/public/experimental.js +9 -0
- package/build/esm/public/experimental.js.map +1 -1
- package/build/types/cbac-picker/CbacPicker.d.ts +8 -0
- package/build/types/cbac-picker/CbacPicker.d.ts.map +1 -0
- package/build/types/cbac-picker/CbacPickerDialog.d.ts +8 -0
- package/build/types/cbac-picker/CbacPickerDialog.d.ts.map +1 -0
- package/build/types/public/experimental.d.ts +5 -0
- package/build/types/public/experimental.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1,5 +1,339 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var React5 = require('react');
|
|
4
|
+
var classnames = require('classnames');
|
|
5
|
+
var button = require('@base-ui/react/button');
|
|
6
|
+
var icons = require('@blueprintjs/icons');
|
|
7
|
+
var popover = require('@base-ui/react/popover');
|
|
8
|
+
var admin = require('@osdk/react/experimental/admin');
|
|
9
|
+
var primitives = require('@osdk/react-components/primitives');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var React5__default = /*#__PURE__*/_interopDefault(React5);
|
|
14
|
+
var classnames__default = /*#__PURE__*/_interopDefault(classnames);
|
|
15
|
+
|
|
16
|
+
// src/cbac-picker/CbacPicker.tsx
|
|
17
|
+
|
|
18
|
+
// src/cbac-picker/utils/cbacPickerUtils.ts
|
|
19
|
+
var EMPTY_ARRAY = [];
|
|
20
|
+
function backgroundFromColors(backgroundColors) {
|
|
21
|
+
return backgroundColors.length > 1 ? `linear-gradient(to right, ${backgroundColors.join(", ")})` : backgroundColors[0] ?? "transparent";
|
|
22
|
+
}
|
|
23
|
+
function resolveRequiredGroups(categoryGroups, requiredMarkingGroups) {
|
|
24
|
+
const markingIdToName = new Map(categoryGroups.flatMap((g) => g.markings).map((m) => [m.id, m.name]));
|
|
25
|
+
return requiredMarkingGroups.map((ids) => ({
|
|
26
|
+
markingNames: ids.map((id) => markingIdToName.get(id) ?? id)
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/cbac-picker/base/BaseCbacBanner.module.css
|
|
31
|
+
var BaseCbacBanner_default = {};
|
|
32
|
+
|
|
33
|
+
// src/cbac-picker/base/BaseCbacBanner.tsx
|
|
34
|
+
function BaseCbacBanner({
|
|
35
|
+
classificationString,
|
|
36
|
+
textColor,
|
|
37
|
+
backgroundColors,
|
|
38
|
+
onClick,
|
|
39
|
+
onDismiss,
|
|
40
|
+
className
|
|
41
|
+
}) {
|
|
42
|
+
const bannerStyle = React5__default.default.useMemo(() => ({
|
|
43
|
+
"--osdk-cbac-banner-bg": backgroundFromColors(backgroundColors),
|
|
44
|
+
"--osdk-cbac-banner-color": textColor
|
|
45
|
+
}), [textColor, backgroundColors]);
|
|
46
|
+
const handleDismiss = React5__default.default.useCallback((e) => {
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
onDismiss?.();
|
|
49
|
+
}, [onDismiss]);
|
|
50
|
+
const dismissButton = onDismiss != null ? /* @__PURE__ */ React5__default.default.createElement(button.Button, {
|
|
51
|
+
className: BaseCbacBanner_default.dismissButton,
|
|
52
|
+
onClick: handleDismiss,
|
|
53
|
+
"aria-label": "Clear classification"
|
|
54
|
+
}, /* @__PURE__ */ React5__default.default.createElement(icons.Cross, {
|
|
55
|
+
size: 12,
|
|
56
|
+
color: "currentColor"
|
|
57
|
+
})) : null;
|
|
58
|
+
if (onClick != null) {
|
|
59
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
60
|
+
className: classnames__default.default(BaseCbacBanner_default.bannerRow, className),
|
|
61
|
+
style: bannerStyle
|
|
62
|
+
}, /* @__PURE__ */ React5__default.default.createElement(button.Button, {
|
|
63
|
+
className: classnames__default.default(BaseCbacBanner_default.banner, BaseCbacBanner_default.clickable),
|
|
64
|
+
onClick,
|
|
65
|
+
"aria-label": "Edit classification"
|
|
66
|
+
}, classificationString), dismissButton);
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
69
|
+
className: classnames__default.default(BaseCbacBanner_default.bannerRow, className),
|
|
70
|
+
style: bannerStyle
|
|
71
|
+
}, /* @__PURE__ */ React5__default.default.createElement("span", {
|
|
72
|
+
className: BaseCbacBanner_default.banner
|
|
73
|
+
}, classificationString), dismissButton);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/cbac-picker/base/BaseCbacPicker.module.css
|
|
77
|
+
var BaseCbacPicker_default = {};
|
|
78
|
+
|
|
79
|
+
// src/cbac-picker/base/CategoryMarkingGroup.module.css
|
|
80
|
+
var CategoryMarkingGroup_default = {};
|
|
81
|
+
|
|
82
|
+
// src/cbac-picker/base/MarkingButton.module.css
|
|
83
|
+
var MarkingButton_default = {};
|
|
84
|
+
|
|
85
|
+
// src/cbac-picker/base/selectionStateHelpers.ts
|
|
86
|
+
function isDisallowed(state) {
|
|
87
|
+
return state === "DISALLOWED" || state === "IMPLIED_DISALLOWED";
|
|
88
|
+
}
|
|
89
|
+
function isImplied(state) {
|
|
90
|
+
return state === "IMPLIED" || state === "IMPLIED_DISALLOWED";
|
|
91
|
+
}
|
|
92
|
+
function getDisplayLabel(label, state) {
|
|
93
|
+
return isImplied(state) ? `(${label})` : label;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/cbac-picker/base/MarkingButton.tsx
|
|
97
|
+
var selectionStateClassMap = {
|
|
98
|
+
SELECTED: MarkingButton_default.selected,
|
|
99
|
+
IMPLIED: MarkingButton_default.implied,
|
|
100
|
+
DISALLOWED: MarkingButton_default.disallowed,
|
|
101
|
+
IMPLIED_DISALLOWED: MarkingButton_default.impliedDisallowed,
|
|
102
|
+
NONE: void 0
|
|
103
|
+
};
|
|
104
|
+
function MarkingButton({
|
|
105
|
+
label,
|
|
106
|
+
selectionState,
|
|
107
|
+
onToggle,
|
|
108
|
+
disabled
|
|
109
|
+
}) {
|
|
110
|
+
return /* @__PURE__ */ React5__default.default.createElement(button.Button, {
|
|
111
|
+
className: classnames__default.default(MarkingButton_default.markingButton, selectionStateClassMap[selectionState]),
|
|
112
|
+
onClick: onToggle,
|
|
113
|
+
disabled: disabled ?? isDisallowed(selectionState),
|
|
114
|
+
"aria-pressed": selectionState === "SELECTED" || selectionState === "IMPLIED"
|
|
115
|
+
}, getDisplayLabel(label, selectionState));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/cbac-picker/base/OverflowButton.module.css
|
|
119
|
+
var OverflowButton_default = {};
|
|
120
|
+
|
|
121
|
+
// src/cbac-picker/base/OverflowItem.module.css
|
|
122
|
+
var OverflowItem_default = {};
|
|
123
|
+
|
|
124
|
+
// src/cbac-picker/base/OverflowItem.tsx
|
|
125
|
+
var OverflowItem = /* @__PURE__ */ React5__default.default.memo(function OverflowItem2({
|
|
126
|
+
id,
|
|
127
|
+
label,
|
|
128
|
+
selectionState,
|
|
129
|
+
disabled,
|
|
130
|
+
onToggle
|
|
131
|
+
}) {
|
|
132
|
+
const handleClick = React5__default.default.useCallback(() => {
|
|
133
|
+
onToggle(id);
|
|
134
|
+
}, [onToggle, id]);
|
|
135
|
+
const disallowed = isDisallowed(selectionState);
|
|
136
|
+
const isSelected = selectionState === "SELECTED";
|
|
137
|
+
const implied = isImplied(selectionState);
|
|
138
|
+
return /* @__PURE__ */ React5__default.default.createElement(button.Button, {
|
|
139
|
+
className: classnames__default.default(OverflowItem_default.overflowItem, (isSelected || implied) && OverflowItem_default.overflowItemSelected, disallowed && OverflowItem_default.overflowItemDisabled),
|
|
140
|
+
onClick: handleClick,
|
|
141
|
+
disabled: disabled ?? disallowed,
|
|
142
|
+
"aria-pressed": isSelected || implied
|
|
143
|
+
}, getDisplayLabel(label, selectionState));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// src/cbac-picker/base/OverflowButton.tsx
|
|
147
|
+
function OverflowButton({
|
|
148
|
+
overflowMarkings,
|
|
149
|
+
hasActiveOverflow,
|
|
150
|
+
onMarkingToggle
|
|
151
|
+
}) {
|
|
152
|
+
const [open, setOpen] = React5__default.default.useState(false);
|
|
153
|
+
return /* @__PURE__ */ React5__default.default.createElement(popover.Popover.Root, {
|
|
154
|
+
open,
|
|
155
|
+
onOpenChange: setOpen
|
|
156
|
+
}, /* @__PURE__ */ React5__default.default.createElement(popover.Popover.Trigger, {
|
|
157
|
+
render: /* @__PURE__ */ React5__default.default.createElement(button.Button, {
|
|
158
|
+
className: classnames__default.default(OverflowButton_default.moreButton, hasActiveOverflow && OverflowButton_default.moreButtonActive)
|
|
159
|
+
}, "+", overflowMarkings.length, " more")
|
|
160
|
+
}), /* @__PURE__ */ React5__default.default.createElement(popover.Popover.Portal, null, /* @__PURE__ */ React5__default.default.createElement(popover.Popover.Positioner, {
|
|
161
|
+
side: "bottom",
|
|
162
|
+
align: "start"
|
|
163
|
+
}, /* @__PURE__ */ React5__default.default.createElement(popover.Popover.Popup, {
|
|
164
|
+
className: OverflowButton_default.overflowList
|
|
165
|
+
}, overflowMarkings.map((marking) => /* @__PURE__ */ React5__default.default.createElement(OverflowItem, {
|
|
166
|
+
key: marking.id,
|
|
167
|
+
id: marking.id,
|
|
168
|
+
label: marking.label,
|
|
169
|
+
selectionState: marking.selectionState,
|
|
170
|
+
disabled: marking.disabled,
|
|
171
|
+
onToggle: onMarkingToggle
|
|
172
|
+
}))))));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/cbac-picker/base/CategoryMarkingGroup.tsx
|
|
176
|
+
var GRID_COLUMNS = 4;
|
|
177
|
+
var VISIBLE_ROWS = 3;
|
|
178
|
+
var DEFAULT_MARKING_STATE = "NONE";
|
|
179
|
+
var CategoryMarkingGroup = /* @__PURE__ */ React5__default.default.memo(function CategoryMarkingGroup2({
|
|
180
|
+
categoryName,
|
|
181
|
+
markings,
|
|
182
|
+
markingStates,
|
|
183
|
+
readOnly,
|
|
184
|
+
onMarkingToggle
|
|
185
|
+
}) {
|
|
186
|
+
const headingId = React5__default.default.useId();
|
|
187
|
+
const resolvedMarkings = React5__default.default.useMemo(() => markings.map((marking) => ({
|
|
188
|
+
id: marking.id,
|
|
189
|
+
label: marking.name,
|
|
190
|
+
selectionState: markingStates.get(marking.id) ?? DEFAULT_MARKING_STATE,
|
|
191
|
+
disabled: readOnly
|
|
192
|
+
})), [markings, markingStates, readOnly]);
|
|
193
|
+
const maxVisible = GRID_COLUMNS * VISIBLE_ROWS;
|
|
194
|
+
const hasOverflow = resolvedMarkings.length > maxVisible;
|
|
195
|
+
const visibleMarkings = hasOverflow ? resolvedMarkings.slice(0, maxVisible - 1) : resolvedMarkings;
|
|
196
|
+
const overflowMarkings = hasOverflow ? resolvedMarkings.slice(maxVisible - 1) : [];
|
|
197
|
+
const gridItemCount = hasOverflow ? visibleMarkings.length + 1 : visibleMarkings.length;
|
|
198
|
+
const emptyCellCount = (GRID_COLUMNS - gridItemCount % GRID_COLUMNS) % GRID_COLUMNS;
|
|
199
|
+
const hasActiveOverflow = overflowMarkings.some((m) => m.selectionState === "SELECTED" || isImplied(m.selectionState));
|
|
200
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
201
|
+
className: CategoryMarkingGroup_default.categoryGroup,
|
|
202
|
+
role: "group",
|
|
203
|
+
"aria-labelledby": headingId
|
|
204
|
+
}, /* @__PURE__ */ React5__default.default.createElement("h3", {
|
|
205
|
+
id: headingId,
|
|
206
|
+
className: CategoryMarkingGroup_default.categoryName
|
|
207
|
+
}, categoryName), /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
208
|
+
className: CategoryMarkingGroup_default.markingGrid
|
|
209
|
+
}, visibleMarkings.map((marking) => /* @__PURE__ */ React5__default.default.createElement(MarkingButtonItem, {
|
|
210
|
+
key: marking.id,
|
|
211
|
+
id: marking.id,
|
|
212
|
+
label: marking.label,
|
|
213
|
+
selectionState: marking.selectionState,
|
|
214
|
+
disabled: marking.disabled,
|
|
215
|
+
onToggle: onMarkingToggle
|
|
216
|
+
})), hasOverflow && /* @__PURE__ */ React5__default.default.createElement(OverflowButton, {
|
|
217
|
+
overflowMarkings,
|
|
218
|
+
hasActiveOverflow,
|
|
219
|
+
onMarkingToggle
|
|
220
|
+
}), Array.from({
|
|
221
|
+
length: emptyCellCount
|
|
222
|
+
}, (_, i) => /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
223
|
+
key: `empty-${i}`,
|
|
224
|
+
className: CategoryMarkingGroup_default.emptyCell
|
|
225
|
+
}))));
|
|
226
|
+
});
|
|
227
|
+
var MarkingButtonItem = /* @__PURE__ */ React5__default.default.memo(function MarkingButtonItem2({
|
|
228
|
+
id,
|
|
229
|
+
label,
|
|
230
|
+
selectionState,
|
|
231
|
+
disabled,
|
|
232
|
+
onToggle
|
|
233
|
+
}) {
|
|
234
|
+
const handleToggle = React5__default.default.useCallback(() => {
|
|
235
|
+
onToggle(id);
|
|
236
|
+
}, [onToggle, id]);
|
|
237
|
+
return /* @__PURE__ */ React5__default.default.createElement(MarkingButton, {
|
|
238
|
+
label,
|
|
239
|
+
selectionState,
|
|
240
|
+
onToggle: handleToggle,
|
|
241
|
+
disabled
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// src/cbac-picker/base/InfoBanner.module.css
|
|
246
|
+
var InfoBanner_default = {};
|
|
247
|
+
|
|
248
|
+
// src/cbac-picker/base/InfoBanner.tsx
|
|
249
|
+
function InfoBanner({
|
|
250
|
+
message,
|
|
251
|
+
className
|
|
252
|
+
}) {
|
|
253
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
254
|
+
className: classnames__default.default(InfoBanner_default.infoBanner, className)
|
|
255
|
+
}, /* @__PURE__ */ React5__default.default.createElement(icons.InfoSign, {
|
|
256
|
+
className: InfoBanner_default.icon,
|
|
257
|
+
size: 14
|
|
258
|
+
}), /* @__PURE__ */ React5__default.default.createElement("span", null, message));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/cbac-picker/base/ValidationWarning.module.css
|
|
262
|
+
var ValidationWarning_default = {};
|
|
263
|
+
|
|
264
|
+
// src/cbac-picker/base/ValidationWarning.tsx
|
|
265
|
+
function ValidationWarning({
|
|
266
|
+
requiredMarkingGroups,
|
|
267
|
+
className
|
|
268
|
+
}) {
|
|
269
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
270
|
+
className: classnames__default.default(ValidationWarning_default.validationWarning, className)
|
|
271
|
+
}, /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
272
|
+
className: ValidationWarning_default.header
|
|
273
|
+
}, /* @__PURE__ */ React5__default.default.createElement(icons.WarningSign, {
|
|
274
|
+
className: ValidationWarning_default.icon,
|
|
275
|
+
size: 14
|
|
276
|
+
}), /* @__PURE__ */ React5__default.default.createElement("span", null, "To complete a valid classification, at least one marking from each of the following sets needs to be selected:")), /* @__PURE__ */ React5__default.default.createElement("ol", {
|
|
277
|
+
className: ValidationWarning_default.groupList
|
|
278
|
+
}, requiredMarkingGroups.map((group, index) => /* @__PURE__ */ React5__default.default.createElement("li", {
|
|
279
|
+
key: group.markingNames.join(","),
|
|
280
|
+
className: ValidationWarning_default.groupItem
|
|
281
|
+
}, /* @__PURE__ */ React5__default.default.createElement("span", {
|
|
282
|
+
className: ValidationWarning_default.groupNumber
|
|
283
|
+
}, index + 1, "."), group.markingNames.map((name) => /* @__PURE__ */ React5__default.default.createElement("span", {
|
|
284
|
+
key: name,
|
|
285
|
+
className: ValidationWarning_default.chip
|
|
286
|
+
}, name))))));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/cbac-picker/base/BaseCbacPicker.tsx
|
|
290
|
+
function BaseCbacPicker({
|
|
291
|
+
categories,
|
|
292
|
+
markingStates,
|
|
293
|
+
banner,
|
|
294
|
+
onMarkingToggle,
|
|
295
|
+
onDismissBanner,
|
|
296
|
+
showInfoBanner,
|
|
297
|
+
requiredMarkingGroups,
|
|
298
|
+
isValid,
|
|
299
|
+
readOnly,
|
|
300
|
+
isLoading,
|
|
301
|
+
error,
|
|
302
|
+
className
|
|
303
|
+
}) {
|
|
304
|
+
const showInitialLoading = isLoading === true && categories.length === 0;
|
|
305
|
+
const showValidationWarning = isValid === false && requiredMarkingGroups != null && requiredMarkingGroups.length > 0;
|
|
306
|
+
return /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
307
|
+
className: classnames__default.default(BaseCbacPicker_default.picker, className)
|
|
308
|
+
}, showInfoBanner === true && /* @__PURE__ */ React5__default.default.createElement(InfoBanner, {
|
|
309
|
+
message: "Implied markings are (in parentheses)."
|
|
310
|
+
}), banner != null && /* @__PURE__ */ React5__default.default.createElement(BaseCbacBanner, {
|
|
311
|
+
classificationString: banner.classificationString,
|
|
312
|
+
textColor: banner.textColor,
|
|
313
|
+
backgroundColors: banner.backgroundColors,
|
|
314
|
+
onDismiss: onDismissBanner,
|
|
315
|
+
className: BaseCbacPicker_default.innerBanner
|
|
316
|
+
}), error != null ? /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
317
|
+
className: BaseCbacPicker_default.statusMessage,
|
|
318
|
+
role: "alert"
|
|
319
|
+
}, error.message) : showInitialLoading ? /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
320
|
+
className: BaseCbacPicker_default.statusMessage,
|
|
321
|
+
role: "status",
|
|
322
|
+
"aria-live": "polite"
|
|
323
|
+
}, "Loading...") : /* @__PURE__ */ React5__default.default.createElement("div", {
|
|
324
|
+
className: BaseCbacPicker_default.categoriesContainer
|
|
325
|
+
}, categories.map((group) => /* @__PURE__ */ React5__default.default.createElement(CategoryMarkingGroup, {
|
|
326
|
+
key: group.category.id,
|
|
327
|
+
categoryName: group.category.name,
|
|
328
|
+
markings: group.markings,
|
|
329
|
+
markingStates,
|
|
330
|
+
readOnly,
|
|
331
|
+
onMarkingToggle
|
|
332
|
+
}))), showValidationWarning && /* @__PURE__ */ React5__default.default.createElement(ValidationWarning, {
|
|
333
|
+
requiredMarkingGroups
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
|
|
3
337
|
// src/cbac-picker/utils/selectionLogic.ts
|
|
4
338
|
function toggleMarking(markingId, currentSelection, categories) {
|
|
5
339
|
const isSelected = currentSelection.includes(markingId);
|
|
@@ -35,16 +369,16 @@ function computeMarkingStates(selectedIds, impliedIds, disallowedIds) {
|
|
|
35
369
|
const disallowedSet = new Set(disallowedIds);
|
|
36
370
|
const allIds = /* @__PURE__ */ new Set([...selectedIds, ...impliedIds, ...disallowedIds]);
|
|
37
371
|
for (const id of allIds) {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
372
|
+
const isImplied2 = impliedSet.has(id);
|
|
373
|
+
const isDisallowed2 = disallowedSet.has(id);
|
|
40
374
|
const isSelected = selectedSet.has(id);
|
|
41
375
|
if (isSelected) {
|
|
42
376
|
states.set(id, "SELECTED");
|
|
43
|
-
} else if (
|
|
377
|
+
} else if (isImplied2 && isDisallowed2) {
|
|
44
378
|
states.set(id, "IMPLIED_DISALLOWED");
|
|
45
|
-
} else if (
|
|
379
|
+
} else if (isImplied2) {
|
|
46
380
|
states.set(id, "IMPLIED");
|
|
47
|
-
} else if (
|
|
381
|
+
} else if (isDisallowed2) {
|
|
48
382
|
states.set(id, "DISALLOWED");
|
|
49
383
|
}
|
|
50
384
|
}
|
|
@@ -73,6 +407,257 @@ function groupMarkingsByCategory(markings, categories) {
|
|
|
73
407
|
return groups;
|
|
74
408
|
}
|
|
75
409
|
|
|
410
|
+
// src/cbac-picker/useCbacPickerState.ts
|
|
411
|
+
var EMPTY_GROUPS = [];
|
|
412
|
+
function useStableArray(arr) {
|
|
413
|
+
const ref = React5__default.default.useRef(arr);
|
|
414
|
+
if (arr.length !== ref.current.length || arr.some((id, i) => id !== ref.current[i])) {
|
|
415
|
+
ref.current = arr;
|
|
416
|
+
}
|
|
417
|
+
return ref.current;
|
|
418
|
+
}
|
|
419
|
+
function useCbacPickerState(selectedIds) {
|
|
420
|
+
const stableSelectedIds = useStableArray(selectedIds);
|
|
421
|
+
const {
|
|
422
|
+
categories: rawCategories,
|
|
423
|
+
isLoading: categoriesLoading,
|
|
424
|
+
error: categoriesError
|
|
425
|
+
} = admin.useMarkingCategories();
|
|
426
|
+
const {
|
|
427
|
+
markings: rawMarkings,
|
|
428
|
+
isLoading: markingsLoading,
|
|
429
|
+
error: markingsError
|
|
430
|
+
} = admin.useMarkings();
|
|
431
|
+
const {
|
|
432
|
+
banner,
|
|
433
|
+
isLoading: bannerLoading,
|
|
434
|
+
error: bannerError
|
|
435
|
+
} = admin.useCbacBanner({
|
|
436
|
+
markingIds: stableSelectedIds
|
|
437
|
+
});
|
|
438
|
+
const {
|
|
439
|
+
restrictions,
|
|
440
|
+
isLoading: restrictionsLoading,
|
|
441
|
+
error: restrictionsError
|
|
442
|
+
} = admin.useCbacMarkingRestrictions({
|
|
443
|
+
markingIds: stableSelectedIds
|
|
444
|
+
});
|
|
445
|
+
const impliedMarkingIds = restrictions?.impliedMarkings ?? EMPTY_ARRAY;
|
|
446
|
+
const disallowedMarkingIds = restrictions?.disallowedMarkings ?? EMPTY_ARRAY;
|
|
447
|
+
const requiredMarkingGroups = restrictions?.requiredMarkings ?? EMPTY_GROUPS;
|
|
448
|
+
const isValid = restrictions?.isValid ?? true;
|
|
449
|
+
const isLoading = categoriesLoading || markingsLoading || restrictionsLoading || bannerLoading;
|
|
450
|
+
const error = React5__default.default.useMemo(() => {
|
|
451
|
+
const errors = [categoriesError, markingsError, restrictionsError, bannerError].filter((e) => e != null);
|
|
452
|
+
if (errors.length > 1) {
|
|
453
|
+
return new AggregateError(errors, errors.map((e) => e.message).join("; "));
|
|
454
|
+
}
|
|
455
|
+
return errors[0];
|
|
456
|
+
}, [categoriesError, markingsError, restrictionsError, bannerError]);
|
|
457
|
+
const categoryGroups = React5__default.default.useMemo(() => {
|
|
458
|
+
if (rawCategories === void 0 || rawMarkings === void 0) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
return groupMarkingsByCategory(rawMarkings, rawCategories);
|
|
462
|
+
}, [rawMarkings, rawCategories]);
|
|
463
|
+
const markingStates = React5__default.default.useMemo(() => computeMarkingStates(stableSelectedIds, impliedMarkingIds, disallowedMarkingIds), [stableSelectedIds, impliedMarkingIds, disallowedMarkingIds]);
|
|
464
|
+
const resolvedRequiredGroups = React5__default.default.useMemo(() => resolveRequiredGroups(categoryGroups, requiredMarkingGroups), [categoryGroups, requiredMarkingGroups]);
|
|
465
|
+
return React5__default.default.useMemo(() => ({
|
|
466
|
+
categoryGroups,
|
|
467
|
+
markingStates,
|
|
468
|
+
banner,
|
|
469
|
+
requiredMarkingGroups: resolvedRequiredGroups,
|
|
470
|
+
isValid,
|
|
471
|
+
isLoading,
|
|
472
|
+
error
|
|
473
|
+
}), [categoryGroups, markingStates, banner, resolvedRequiredGroups, isValid, isLoading, error]);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/cbac-picker/CbacPicker.tsx
|
|
477
|
+
function CbacPicker({
|
|
478
|
+
initialMarkingIds,
|
|
479
|
+
onChange,
|
|
480
|
+
readOnly,
|
|
481
|
+
className
|
|
482
|
+
}) {
|
|
483
|
+
const [selectedIds, setSelectedIds] = React5__default.default.useState(initialMarkingIds ?? EMPTY_ARRAY);
|
|
484
|
+
const [prevInitialIds, setPrevInitialIds] = React5__default.default.useState(initialMarkingIds);
|
|
485
|
+
if (initialMarkingIds !== prevInitialIds) {
|
|
486
|
+
setPrevInitialIds(initialMarkingIds);
|
|
487
|
+
setSelectedIds(initialMarkingIds ?? EMPTY_ARRAY);
|
|
488
|
+
}
|
|
489
|
+
const {
|
|
490
|
+
categoryGroups,
|
|
491
|
+
markingStates,
|
|
492
|
+
banner,
|
|
493
|
+
requiredMarkingGroups,
|
|
494
|
+
isValid,
|
|
495
|
+
isLoading,
|
|
496
|
+
error
|
|
497
|
+
} = useCbacPickerState(selectedIds);
|
|
498
|
+
const handleMarkingToggle = React5__default.default.useCallback((markingId) => {
|
|
499
|
+
if (readOnly) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const newSelection = toggleMarking(markingId, selectedIds, categoryGroups);
|
|
503
|
+
setSelectedIds(newSelection);
|
|
504
|
+
onChange(newSelection);
|
|
505
|
+
}, [readOnly, selectedIds, categoryGroups, onChange]);
|
|
506
|
+
const handleDismiss = React5__default.default.useCallback(() => {
|
|
507
|
+
setSelectedIds(EMPTY_ARRAY);
|
|
508
|
+
onChange(EMPTY_ARRAY);
|
|
509
|
+
}, [onChange]);
|
|
510
|
+
return /* @__PURE__ */ React5__default.default.createElement(BaseCbacPicker, {
|
|
511
|
+
categories: categoryGroups,
|
|
512
|
+
markingStates,
|
|
513
|
+
banner,
|
|
514
|
+
onMarkingToggle: handleMarkingToggle,
|
|
515
|
+
onDismissBanner: handleDismiss,
|
|
516
|
+
requiredMarkingGroups,
|
|
517
|
+
isValid,
|
|
518
|
+
readOnly,
|
|
519
|
+
isLoading,
|
|
520
|
+
error,
|
|
521
|
+
className
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/cbac-picker/base/CbacPickerDialogFooter.module.css
|
|
526
|
+
var CbacPickerDialogFooter_default = {};
|
|
527
|
+
|
|
528
|
+
// src/cbac-picker/base/CbacPickerDialogFooter.tsx
|
|
529
|
+
function CbacPickerDialogFooter({
|
|
530
|
+
onCancel,
|
|
531
|
+
onConfirm,
|
|
532
|
+
submitDisabledReason
|
|
533
|
+
}) {
|
|
534
|
+
const isSubmitDisabled = submitDisabledReason != null;
|
|
535
|
+
const submitButton = /* @__PURE__ */ React5__default.default.createElement(primitives.ActionButton, {
|
|
536
|
+
variant: "primary",
|
|
537
|
+
onClick: onConfirm,
|
|
538
|
+
disabled: isSubmitDisabled
|
|
539
|
+
}, "Set classification");
|
|
540
|
+
return /* @__PURE__ */ React5__default.default.createElement(React5__default.default.Fragment, null, /* @__PURE__ */ React5__default.default.createElement(primitives.ActionButton, {
|
|
541
|
+
variant: "secondary",
|
|
542
|
+
onClick: onCancel
|
|
543
|
+
}, "Cancel"), isSubmitDisabled ? /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Root, null, /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Trigger, {
|
|
544
|
+
render: /* @__PURE__ */ React5__default.default.createElement("span", {
|
|
545
|
+
className: CbacPickerDialogFooter_default.tooltipTriggerWrapper
|
|
546
|
+
})
|
|
547
|
+
}, submitButton), /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Portal, null, /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Positioner, {
|
|
548
|
+
side: "top"
|
|
549
|
+
}, /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Popup, null, submitDisabledReason, /* @__PURE__ */ React5__default.default.createElement(primitives.Tooltip.Arrow, null))))) : submitButton);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/cbac-picker/base/BaseCbacPickerDialog.tsx
|
|
553
|
+
function _extends() {
|
|
554
|
+
return _extends = Object.assign ? Object.assign.bind() : function(n) {
|
|
555
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
556
|
+
var t = arguments[e];
|
|
557
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
558
|
+
}
|
|
559
|
+
return n;
|
|
560
|
+
}, _extends.apply(null, arguments);
|
|
561
|
+
}
|
|
562
|
+
function BaseCbacPickerDialog({
|
|
563
|
+
isOpen,
|
|
564
|
+
onOpenChange,
|
|
565
|
+
onConfirm,
|
|
566
|
+
onCancel,
|
|
567
|
+
title = "Select classification",
|
|
568
|
+
submitDisabledReason,
|
|
569
|
+
...pickerProps
|
|
570
|
+
}) {
|
|
571
|
+
const handleOpenChange = React5__default.default.useCallback((open) => {
|
|
572
|
+
if (!open) {
|
|
573
|
+
onCancel();
|
|
574
|
+
}
|
|
575
|
+
onOpenChange(open);
|
|
576
|
+
}, [onCancel, onOpenChange]);
|
|
577
|
+
return /* @__PURE__ */ React5__default.default.createElement(primitives.Dialog, {
|
|
578
|
+
isOpen,
|
|
579
|
+
onOpenChange: handleOpenChange,
|
|
580
|
+
title,
|
|
581
|
+
footer: /* @__PURE__ */ React5__default.default.createElement(CbacPickerDialogFooter, {
|
|
582
|
+
onCancel,
|
|
583
|
+
onConfirm,
|
|
584
|
+
submitDisabledReason
|
|
585
|
+
}),
|
|
586
|
+
disablePointerDismissal: true
|
|
587
|
+
}, /* @__PURE__ */ React5__default.default.createElement(BaseCbacPicker, _extends({}, pickerProps, {
|
|
588
|
+
showInfoBanner: pickerProps.showInfoBanner ?? true
|
|
589
|
+
})));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/cbac-picker/CbacPickerDialog.tsx
|
|
593
|
+
function CbacPickerDialog({
|
|
594
|
+
isOpen,
|
|
595
|
+
onOpenChange,
|
|
596
|
+
onConfirm,
|
|
597
|
+
initialMarkingIds
|
|
598
|
+
}) {
|
|
599
|
+
const [selectedIds, setSelectedIds] = React5__default.default.useState(initialMarkingIds ?? EMPTY_ARRAY);
|
|
600
|
+
const [prevInitialIds, setPrevInitialIds] = React5__default.default.useState(initialMarkingIds);
|
|
601
|
+
if (initialMarkingIds !== prevInitialIds) {
|
|
602
|
+
setPrevInitialIds(initialMarkingIds);
|
|
603
|
+
setSelectedIds(initialMarkingIds ?? EMPTY_ARRAY);
|
|
604
|
+
}
|
|
605
|
+
const {
|
|
606
|
+
categoryGroups,
|
|
607
|
+
markingStates,
|
|
608
|
+
banner,
|
|
609
|
+
requiredMarkingGroups,
|
|
610
|
+
isValid,
|
|
611
|
+
isLoading,
|
|
612
|
+
error
|
|
613
|
+
} = useCbacPickerState(selectedIds);
|
|
614
|
+
const handleMarkingToggle = React5__default.default.useCallback((markingId) => {
|
|
615
|
+
setSelectedIds((prev) => toggleMarking(markingId, prev, categoryGroups));
|
|
616
|
+
}, [categoryGroups]);
|
|
617
|
+
const handleConfirm = React5__default.default.useCallback(() => {
|
|
618
|
+
onConfirm(selectedIds);
|
|
619
|
+
}, [onConfirm, selectedIds]);
|
|
620
|
+
const handleCancel = React5__default.default.useCallback(() => {
|
|
621
|
+
setSelectedIds(initialMarkingIds ?? EMPTY_ARRAY);
|
|
622
|
+
onOpenChange(false);
|
|
623
|
+
}, [initialMarkingIds, onOpenChange]);
|
|
624
|
+
const handleDismiss = React5__default.default.useCallback(() => {
|
|
625
|
+
setSelectedIds(EMPTY_ARRAY);
|
|
626
|
+
}, []);
|
|
627
|
+
const hasInitialMarkings = initialMarkingIds !== void 0 && initialMarkingIds.length > 0;
|
|
628
|
+
const submitDisabledReason = React5__default.default.useMemo(() => {
|
|
629
|
+
if (isValid) {
|
|
630
|
+
return void 0;
|
|
631
|
+
}
|
|
632
|
+
if (requiredMarkingGroups.length > 0) {
|
|
633
|
+
return "Selected markings do not include all required markings.";
|
|
634
|
+
}
|
|
635
|
+
return "Invalid marking selection.";
|
|
636
|
+
}, [isValid, requiredMarkingGroups.length]);
|
|
637
|
+
return /* @__PURE__ */ React5__default.default.createElement(BaseCbacPickerDialog, {
|
|
638
|
+
isOpen,
|
|
639
|
+
onOpenChange,
|
|
640
|
+
onConfirm: handleConfirm,
|
|
641
|
+
onCancel: handleCancel,
|
|
642
|
+
title: hasInitialMarkings ? "Edit classification" : "Add classification",
|
|
643
|
+
categories: categoryGroups,
|
|
644
|
+
markingStates,
|
|
645
|
+
banner,
|
|
646
|
+
onMarkingToggle: handleMarkingToggle,
|
|
647
|
+
onDismissBanner: handleDismiss,
|
|
648
|
+
requiredMarkingGroups,
|
|
649
|
+
isValid,
|
|
650
|
+
submitDisabledReason,
|
|
651
|
+
isLoading,
|
|
652
|
+
error
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
exports.BaseCbacBanner = BaseCbacBanner;
|
|
657
|
+
exports.BaseCbacPicker = BaseCbacPicker;
|
|
658
|
+
exports.BaseCbacPickerDialog = BaseCbacPickerDialog;
|
|
659
|
+
exports.CbacPicker = CbacPicker;
|
|
660
|
+
exports.CbacPickerDialog = CbacPickerDialog;
|
|
76
661
|
exports.computeMarkingStates = computeMarkingStates;
|
|
77
662
|
exports.groupMarkingsByCategory = groupMarkingsByCategory;
|
|
78
663
|
exports.toggleMarking = toggleMarking;
|