@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.
@@ -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 isImplied = impliedSet.has(id);
39
- const isDisallowed = disallowedSet.has(id);
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 (isImplied && isDisallowed) {
377
+ } else if (isImplied2 && isDisallowed2) {
44
378
  states.set(id, "IMPLIED_DISALLOWED");
45
- } else if (isImplied) {
379
+ } else if (isImplied2) {
46
380
  states.set(id, "IMPLIED");
47
- } else if (isDisallowed) {
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;