@unofficialbox/box-open-elements 0.5.0 → 0.7.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 (227) hide show
  1. package/README.md +27 -73
  2. package/dist/components/collections/table.d.ts +113 -3
  3. package/dist/components/collections/table.js +540 -37
  4. package/dist/components/feedback/due-badge.d.ts +38 -0
  5. package/dist/components/feedback/due-badge.js +168 -0
  6. package/dist/components/feedback/due-types.d.ts +32 -0
  7. package/dist/components/feedback/due-types.js +91 -0
  8. package/dist/components/feedback/progress-steps.d.ts +51 -5
  9. package/dist/components/feedback/progress-steps.js +200 -22
  10. package/dist/components/feedback/stage-path.d.ts +44 -0
  11. package/dist/components/feedback/stage-path.js +235 -0
  12. package/dist/components/forms/select.d.ts +21 -0
  13. package/dist/components/forms/select.js +100 -3
  14. package/dist/components/forms/text-field.d.ts +21 -0
  15. package/dist/components/forms/text-field.js +100 -1
  16. package/dist/components/layout/nav-sidebar.d.ts +14 -0
  17. package/dist/components/layout/nav-sidebar.js +49 -0
  18. package/dist/components/layout/split-view.d.ts +14 -0
  19. package/dist/components/layout/split-view.js +80 -1
  20. package/dist/components/overlays/command-palette.d.ts +62 -0
  21. package/dist/components/overlays/command-palette.js +537 -0
  22. package/dist/components/overlays/command-types.d.ts +90 -0
  23. package/dist/components/overlays/command-types.js +259 -0
  24. package/dist/components/overlays/drawer.d.ts +18 -0
  25. package/dist/components/overlays/drawer.js +135 -9
  26. package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
  27. package/dist/components/overlays/shortcuts-overlay.js +385 -0
  28. package/dist/components/overlays/tooltip.js +1 -1
  29. package/dist/core/index.d.ts +4 -0
  30. package/dist/core/index.js +2 -0
  31. package/dist/core/offset-window.d.ts +70 -0
  32. package/dist/core/offset-window.js +134 -0
  33. package/dist/core/virtualize.d.ts +71 -0
  34. package/dist/core/virtualize.js +67 -0
  35. package/dist/element-maps.d.ts +292 -0
  36. package/dist/element-maps.js +1 -0
  37. package/dist/entries/activity-density.d.ts +1 -0
  38. package/dist/entries/activity-density.js +1 -0
  39. package/dist/entries/agent-chat.d.ts +1 -0
  40. package/dist/entries/agent-chat.js +1 -0
  41. package/dist/entries/audit-log.d.ts +1 -0
  42. package/dist/entries/audit-log.js +1 -0
  43. package/dist/entries/command-palette.d.ts +1 -0
  44. package/dist/entries/command-palette.js +1 -0
  45. package/dist/entries/compare-view.d.ts +1 -0
  46. package/dist/entries/compare-view.js +1 -0
  47. package/dist/entries/diff-viewer.d.ts +1 -0
  48. package/dist/entries/diff-viewer.js +1 -0
  49. package/dist/entries/due-badge.d.ts +1 -0
  50. package/dist/entries/due-badge.js +1 -0
  51. package/dist/entries/form-wizard.d.ts +1 -0
  52. package/dist/entries/form-wizard.js +1 -0
  53. package/dist/entries/lineage-graph.d.ts +1 -0
  54. package/dist/entries/lineage-graph.js +1 -0
  55. package/dist/entries/notification-bell.d.ts +1 -0
  56. package/dist/entries/notification-bell.js +1 -0
  57. package/dist/entries/notification-inbox.d.ts +1 -0
  58. package/dist/entries/notification-inbox.js +1 -0
  59. package/dist/entries/provenance-strip.d.ts +1 -0
  60. package/dist/entries/provenance-strip.js +1 -0
  61. package/dist/entries/run-trace.d.ts +1 -0
  62. package/dist/entries/run-trace.js +1 -0
  63. package/dist/entries/shortcuts-overlay.d.ts +1 -0
  64. package/dist/entries/shortcuts-overlay.js +1 -0
  65. package/dist/entries/signature-ceremony.d.ts +1 -0
  66. package/dist/entries/signature-ceremony.js +1 -0
  67. package/dist/entries/stage-path.d.ts +1 -0
  68. package/dist/entries/stage-path.js +1 -0
  69. package/dist/entries/timeline.d.ts +1 -0
  70. package/dist/entries/timeline.js +1 -0
  71. package/dist/entries/version-graph.d.ts +1 -0
  72. package/dist/entries/version-graph.js +1 -0
  73. package/dist/entries/version-list.d.ts +1 -0
  74. package/dist/entries/version-list.js +1 -0
  75. package/dist/entries/wizard-summary.d.ts +1 -0
  76. package/dist/entries/wizard-summary.js +1 -0
  77. package/dist/entries/work-queue.d.ts +1 -0
  78. package/dist/entries/work-queue.js +1 -0
  79. package/dist/entries/workload-board.d.ts +1 -0
  80. package/dist/entries/workload-board.js +1 -0
  81. package/dist/index.d.ts +21 -0
  82. package/dist/index.js +21 -0
  83. package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
  84. package/dist/patterns/agent-chat/agent-chat.js +820 -0
  85. package/dist/patterns/agent-chat/controller.d.ts +27 -0
  86. package/dist/patterns/agent-chat/controller.js +182 -0
  87. package/dist/patterns/agent-chat/index.d.ts +3 -0
  88. package/dist/patterns/agent-chat/index.js +6 -0
  89. package/dist/patterns/agent-chat/types.d.ts +109 -0
  90. package/dist/patterns/agent-chat/types.js +1 -0
  91. package/dist/patterns/audit/activity-density.d.ts +68 -0
  92. package/dist/patterns/audit/activity-density.js +428 -0
  93. package/dist/patterns/audit/audit-log.d.ts +182 -0
  94. package/dist/patterns/audit/audit-log.js +1082 -0
  95. package/dist/patterns/audit/index.d.ts +3 -0
  96. package/dist/patterns/audit/index.js +7 -0
  97. package/dist/patterns/audit/shared.d.ts +10 -0
  98. package/dist/patterns/audit/shared.js +50 -0
  99. package/dist/patterns/audit/types.d.ts +143 -0
  100. package/dist/patterns/audit/types.js +327 -0
  101. package/dist/patterns/audit/window.d.ts +116 -0
  102. package/dist/patterns/audit/window.js +132 -0
  103. package/dist/patterns/content-explorer/actions/controller.js +12 -2
  104. package/dist/patterns/content-explorer/box-transport.js +117 -25
  105. package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
  106. package/dist/patterns/content-explorer/content-explorer.js +36 -3
  107. package/dist/patterns/content-explorer/contracts.d.ts +24 -1
  108. package/dist/patterns/content-explorer/contracts.js +68 -11
  109. package/dist/patterns/content-explorer/controller.d.ts +16 -1
  110. package/dist/patterns/content-explorer/controller.js +130 -4
  111. package/dist/patterns/content-explorer/navigation/controller.js +11 -9
  112. package/dist/patterns/content-explorer/types.d.ts +58 -1
  113. package/dist/patterns/content-picker/content-picker.d.ts +71 -0
  114. package/dist/patterns/content-picker/content-picker.js +747 -0
  115. package/dist/patterns/content-picker/controller.d.ts +38 -0
  116. package/dist/patterns/content-picker/controller.js +141 -0
  117. package/dist/patterns/content-picker/index.d.ts +3 -0
  118. package/dist/patterns/content-picker/index.js +6 -0
  119. package/dist/patterns/content-picker/types.d.ts +52 -0
  120. package/dist/patterns/content-picker/types.js +26 -0
  121. package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
  122. package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
  123. package/dist/patterns/content-sidebar/index.d.ts +2 -0
  124. package/dist/patterns/content-sidebar/index.js +4 -0
  125. package/dist/patterns/content-sidebar/types.d.ts +18 -0
  126. package/dist/patterns/content-sidebar/types.js +20 -0
  127. package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
  128. package/dist/patterns/content-uploader/box-transport.js +54 -0
  129. package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
  130. package/dist/patterns/content-uploader/content-uploader.js +559 -0
  131. package/dist/patterns/content-uploader/controller.d.ts +39 -0
  132. package/dist/patterns/content-uploader/controller.js +227 -0
  133. package/dist/patterns/content-uploader/index.d.ts +4 -0
  134. package/dist/patterns/content-uploader/index.js +8 -0
  135. package/dist/patterns/content-uploader/types.d.ts +120 -0
  136. package/dist/patterns/content-uploader/types.js +38 -0
  137. package/dist/patterns/diff/compare-view.d.ts +47 -0
  138. package/dist/patterns/diff/compare-view.js +276 -0
  139. package/dist/patterns/diff/diff-viewer.d.ts +42 -0
  140. package/dist/patterns/diff/diff-viewer.js +434 -0
  141. package/dist/patterns/diff/engine.d.ts +22 -0
  142. package/dist/patterns/diff/engine.js +276 -0
  143. package/dist/patterns/diff/index.d.ts +4 -0
  144. package/dist/patterns/diff/index.js +8 -0
  145. package/dist/patterns/diff/types.d.ts +69 -0
  146. package/dist/patterns/diff/types.js +24 -0
  147. package/dist/patterns/form-wizard/controller.d.ts +39 -0
  148. package/dist/patterns/form-wizard/controller.js +157 -0
  149. package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
  150. package/dist/patterns/form-wizard/form-wizard.js +468 -0
  151. package/dist/patterns/form-wizard/index.d.ts +4 -0
  152. package/dist/patterns/form-wizard/index.js +8 -0
  153. package/dist/patterns/form-wizard/types.d.ts +109 -0
  154. package/dist/patterns/form-wizard/types.js +83 -0
  155. package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
  156. package/dist/patterns/form-wizard/wizard-summary.js +324 -0
  157. package/dist/patterns/internal/safe-href.d.ts +17 -0
  158. package/dist/patterns/internal/safe-href.js +17 -0
  159. package/dist/patterns/lineage/index.d.ts +3 -0
  160. package/dist/patterns/lineage/index.js +6 -0
  161. package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
  162. package/dist/patterns/lineage/lineage-graph.js +434 -0
  163. package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
  164. package/dist/patterns/lineage/provenance-strip.js +194 -0
  165. package/dist/patterns/lineage/types.d.ts +53 -0
  166. package/dist/patterns/lineage/types.js +49 -0
  167. package/dist/patterns/notifications/index.d.ts +3 -0
  168. package/dist/patterns/notifications/index.js +6 -0
  169. package/dist/patterns/notifications/notification-bell.d.ts +38 -0
  170. package/dist/patterns/notifications/notification-bell.js +198 -0
  171. package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
  172. package/dist/patterns/notifications/notification-inbox.js +537 -0
  173. package/dist/patterns/notifications/types.d.ts +61 -0
  174. package/dist/patterns/notifications/types.js +108 -0
  175. package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
  176. package/dist/patterns/preview/annotation-inspector.js +239 -94
  177. package/dist/patterns/preview/annotation-thread.d.ts +25 -5
  178. package/dist/patterns/preview/annotation-thread.js +233 -82
  179. package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
  180. package/dist/patterns/preview/annotation-toolbar.js +162 -96
  181. package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
  182. package/dist/patterns/preview/content-preview-adapter.js +26 -1
  183. package/dist/patterns/preview/preview-element.d.ts +15 -2
  184. package/dist/patterns/preview/preview-element.js +223 -52
  185. package/dist/patterns/preview/provider-adapter.d.ts +47 -0
  186. package/dist/patterns/preview/provider-adapter.js +17 -0
  187. package/dist/patterns/run/index.d.ts +2 -0
  188. package/dist/patterns/run/index.js +4 -0
  189. package/dist/patterns/run/run-trace.d.ts +40 -0
  190. package/dist/patterns/run/run-trace.js +441 -0
  191. package/dist/patterns/run/types.d.ts +66 -0
  192. package/dist/patterns/run/types.js +152 -0
  193. package/dist/patterns/signature/index.d.ts +2 -0
  194. package/dist/patterns/signature/index.js +4 -0
  195. package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
  196. package/dist/patterns/signature/signature-ceremony.js +317 -0
  197. package/dist/patterns/signature/types.d.ts +68 -0
  198. package/dist/patterns/signature/types.js +89 -0
  199. package/dist/patterns/timeline/index.d.ts +2 -0
  200. package/dist/patterns/timeline/index.js +4 -0
  201. package/dist/patterns/timeline/timeline.d.ts +27 -0
  202. package/dist/patterns/timeline/timeline.js +430 -0
  203. package/dist/patterns/timeline/types.d.ts +36 -0
  204. package/dist/patterns/timeline/types.js +27 -0
  205. package/dist/patterns/versions/graph-layout.d.ts +43 -0
  206. package/dist/patterns/versions/graph-layout.js +159 -0
  207. package/dist/patterns/versions/index.d.ts +4 -0
  208. package/dist/patterns/versions/index.js +8 -0
  209. package/dist/patterns/versions/shared.d.ts +4 -0
  210. package/dist/patterns/versions/shared.js +13 -0
  211. package/dist/patterns/versions/types.d.ts +37 -0
  212. package/dist/patterns/versions/types.js +39 -0
  213. package/dist/patterns/versions/version-graph.d.ts +36 -0
  214. package/dist/patterns/versions/version-graph.js +438 -0
  215. package/dist/patterns/versions/version-list.d.ts +39 -0
  216. package/dist/patterns/versions/version-list.js +407 -0
  217. package/dist/patterns/work-queue/controller.d.ts +26 -0
  218. package/dist/patterns/work-queue/controller.js +150 -0
  219. package/dist/patterns/work-queue/index.d.ts +4 -0
  220. package/dist/patterns/work-queue/index.js +7 -0
  221. package/dist/patterns/work-queue/types.d.ts +128 -0
  222. package/dist/patterns/work-queue/types.js +45 -0
  223. package/dist/patterns/work-queue/work-queue.d.ts +48 -0
  224. package/dist/patterns/work-queue/work-queue.js +534 -0
  225. package/dist/patterns/work-queue/workload-board.d.ts +53 -0
  226. package/dist/patterns/work-queue/workload-board.js +608 -0
  227. package/package.json +89 -5
@@ -0,0 +1,38 @@
1
+ import { Controller } from "../../core/controller.js";
2
+ import { ContentExplorerController } from "../content-explorer/controller.js";
3
+ import type { ExplorerItem } from "../content-explorer/types.js";
4
+ import type { PickerEvents, PickerSessionConfig, PickerState } from "./types.js";
5
+ /**
6
+ * A constrained explorer session for choosing content: the same browse /
7
+ * search surface as ContentExplorerController, plus a cross-folder pick
8
+ * roster with type / extension / count constraints and a choose/cancel
9
+ * contract. The inner explorer's per-folder selection is kept in sync so
10
+ * roster members render selected in whatever folder is on screen.
11
+ */
12
+ export declare class ContentPickerController extends Controller<PickerState, PickerEvents> {
13
+ readonly explorer: ContentExplorerController;
14
+ private readonly constraints;
15
+ private readonly roster;
16
+ private readonly unsubscribeFns;
17
+ constructor(config: PickerSessionConfig);
18
+ connect(): Promise<void>;
19
+ disconnect(): void;
20
+ destroy(): void;
21
+ isItemPickable(item: ExplorerItem): boolean;
22
+ isPicked(itemId: string): boolean;
23
+ /**
24
+ * Toggle an item in or out of the roster. Unpicking works for any roster
25
+ * member (including items picked in another folder); picking requires the
26
+ * item to be in the current collection, eligible, and under the limit.
27
+ * With `maxSelectable: 1` picking replaces the previous pick.
28
+ */
29
+ togglePick(itemId: string): void;
30
+ unpick(itemId: string): void;
31
+ clearPicks(): void;
32
+ /** Emit `chosen` with the roster. No-op (returns null) while it is empty. */
33
+ choose(): ExplorerItem[] | null;
34
+ /** Abandon the session: clears the roster and emits `cancelled`. */
35
+ cancel(): void;
36
+ private publishSelection;
37
+ private syncExplorerSelection;
38
+ }
@@ -0,0 +1,141 @@
1
+ import { Controller } from "../../core/controller.js";
2
+ import { ContentExplorerController } from "../content-explorer/controller.js";
3
+ import { isItemPickable } from "./types.js";
4
+ const createInitialState = (config) => ({
5
+ canChoose: false,
6
+ maxSelectable: config.maxSelectable ?? null,
7
+ selectedItems: [],
8
+ });
9
+ /**
10
+ * A constrained explorer session for choosing content: the same browse /
11
+ * search surface as ContentExplorerController, plus a cross-folder pick
12
+ * roster with type / extension / count constraints and a choose/cancel
13
+ * contract. The inner explorer's per-folder selection is kept in sync so
14
+ * roster members render selected in whatever folder is on screen.
15
+ */
16
+ export class ContentPickerController extends Controller {
17
+ explorer;
18
+ constraints;
19
+ roster = new Map();
20
+ unsubscribeFns;
21
+ constructor(config) {
22
+ super(createInitialState(config));
23
+ const { extensions, maxSelectable, selectableTypes, ...explorerConfig } = config;
24
+ this.constraints = { extensions, maxSelectable, selectableTypes };
25
+ this.explorer = new ContentExplorerController({
26
+ ...explorerConfig,
27
+ selectionMode: maxSelectable === 1 ? "single" : "multiple",
28
+ });
29
+ // Re-mark roster members whenever the visible collection changes —
30
+ // navigation and search reset the explorer's own selection.
31
+ const resync = () => {
32
+ this.syncExplorerSelection();
33
+ };
34
+ this.unsubscribeFns = [
35
+ this.explorer.subscribe("loadSucceeded", resync),
36
+ this.explorer.subscribe("searchSucceeded", resync),
37
+ ];
38
+ }
39
+ async connect() {
40
+ await this.explorer.connect();
41
+ }
42
+ disconnect() {
43
+ this.explorer.disconnect();
44
+ if (this.roster.size > 0) {
45
+ this.roster.clear();
46
+ this.publishSelection();
47
+ }
48
+ }
49
+ destroy() {
50
+ for (const unsubscribe of this.unsubscribeFns) {
51
+ unsubscribe();
52
+ }
53
+ this.explorer.destroy();
54
+ super.destroy();
55
+ }
56
+ isItemPickable(item) {
57
+ return isItemPickable(item, this.constraints);
58
+ }
59
+ isPicked(itemId) {
60
+ return this.roster.has(itemId);
61
+ }
62
+ /**
63
+ * Toggle an item in or out of the roster. Unpicking works for any roster
64
+ * member (including items picked in another folder); picking requires the
65
+ * item to be in the current collection, eligible, and under the limit.
66
+ * With `maxSelectable: 1` picking replaces the previous pick.
67
+ */
68
+ togglePick(itemId) {
69
+ if (this.roster.has(itemId)) {
70
+ this.roster.delete(itemId);
71
+ this.publishSelection();
72
+ return;
73
+ }
74
+ const item = this.explorer.getState().items.find(entry => entry.id === itemId);
75
+ if (!item) {
76
+ return;
77
+ }
78
+ if (!this.isItemPickable(item)) {
79
+ this.emit("selectionRejected", { item, reason: "not-selectable" });
80
+ return;
81
+ }
82
+ const max = this.state.maxSelectable;
83
+ if (max !== null && this.roster.size >= max) {
84
+ if (max === 1) {
85
+ this.roster.clear();
86
+ }
87
+ else {
88
+ this.emit("selectionRejected", { item, reason: "limit-reached" });
89
+ return;
90
+ }
91
+ }
92
+ this.roster.set(itemId, item);
93
+ this.publishSelection();
94
+ }
95
+ unpick(itemId) {
96
+ if (this.roster.delete(itemId)) {
97
+ this.publishSelection();
98
+ }
99
+ }
100
+ clearPicks() {
101
+ if (this.roster.size === 0) {
102
+ return;
103
+ }
104
+ this.roster.clear();
105
+ this.publishSelection();
106
+ }
107
+ /** Emit `chosen` with the roster. No-op (returns null) while it is empty. */
108
+ choose() {
109
+ if (this.roster.size === 0) {
110
+ return null;
111
+ }
112
+ const items = [...this.roster.values()];
113
+ this.emit("chosen", { items });
114
+ return items;
115
+ }
116
+ /** Abandon the session: clears the roster and emits `cancelled`. */
117
+ cancel() {
118
+ if (this.roster.size > 0) {
119
+ this.roster.clear();
120
+ this.publishSelection();
121
+ }
122
+ this.emit("cancelled", undefined);
123
+ }
124
+ publishSelection() {
125
+ this.syncExplorerSelection();
126
+ const selectedItems = [...this.roster.values()];
127
+ this.setState({
128
+ ...this.state,
129
+ canChoose: selectedItems.length > 0,
130
+ selectedItems,
131
+ });
132
+ this.emit("selectionChanged", { selectedItems });
133
+ }
134
+ syncExplorerSelection() {
135
+ const visibleIds = this.explorer
136
+ .getState()
137
+ .items.filter(item => this.roster.has(item.id))
138
+ .map(item => item.id);
139
+ this.explorer.select(visibleIds);
140
+ }
141
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./controller.js";
3
+ export * from "./content-picker.js";
@@ -0,0 +1,6 @@
1
+ // Picker contracts, constraints, and state types
2
+ export * from "./types.js";
3
+ // Composition root over the explorer headless blocks
4
+ export * from "./controller.js";
5
+ // Composed workflow surface
6
+ export * from "./content-picker.js";
@@ -0,0 +1,52 @@
1
+ import type { ExplorerItem, ExplorerSessionConfig } from "../content-explorer/types.js";
2
+ export type PickerItemType = ExplorerItem["type"];
3
+ /**
4
+ * Eligibility rules for what the picker lets the user put in its roster.
5
+ * Items that fail the rules stay visible (folders remain navigable) but their
6
+ * rows are not pickable.
7
+ */
8
+ export interface PickerConstraints {
9
+ /**
10
+ * Item types eligible for picking. Defaults to `["file"]`, mirroring
11
+ * box-ui-elements' ContentPicker default type. Folders always stay
12
+ * navigable whether or not they are pickable.
13
+ */
14
+ selectableTypes?: PickerItemType[];
15
+ /**
16
+ * Extension allowlist applied to files (case-insensitive, no leading dot).
17
+ * Empty or omitted allows every extension. Only files are filtered — other
18
+ * item types have no extension.
19
+ */
20
+ extensions?: string[];
21
+ /**
22
+ * Maximum roster size. Omitted means unlimited. `1` also switches the
23
+ * underlying explorer selection to single mode, so picking replaces
24
+ * instead of rejecting.
25
+ */
26
+ maxSelectable?: number;
27
+ }
28
+ export interface PickerSessionConfig extends Omit<ExplorerSessionConfig, "itemActions" | "selectionMode">, PickerConstraints {
29
+ }
30
+ export interface PickerState {
31
+ /** True when the roster is non-empty — drives the Choose button. */
32
+ canChoose: boolean;
33
+ maxSelectable: number | null;
34
+ /** The cross-folder pick roster, in pick order. */
35
+ selectedItems: ExplorerItem[];
36
+ }
37
+ export type PickerSelectionRejectionReason = "limit-reached" | "not-selectable";
38
+ export interface PickerEvents {
39
+ cancelled: undefined;
40
+ chosen: {
41
+ items: ExplorerItem[];
42
+ };
43
+ selectionChanged: {
44
+ selectedItems: ExplorerItem[];
45
+ };
46
+ selectionRejected: {
47
+ item: ExplorerItem;
48
+ reason: PickerSelectionRejectionReason;
49
+ };
50
+ }
51
+ /** Pure eligibility check — the single source of truth for pickability. */
52
+ export declare const isItemPickable: (item: ExplorerItem, constraints: PickerConstraints) => boolean;
@@ -0,0 +1,26 @@
1
+ const normalizeExtension = (value) => value.replace(/^\./, "").toLowerCase();
2
+ /** File extension resolved from the item's own field or its name's suffix. */
3
+ const resolveItemExtension = (item) => {
4
+ if (item.extension) {
5
+ return normalizeExtension(item.extension);
6
+ }
7
+ const dotIndex = item.name.lastIndexOf(".");
8
+ if (dotIndex <= 0 || dotIndex === item.name.length - 1) {
9
+ return null;
10
+ }
11
+ return normalizeExtension(item.name.slice(dotIndex + 1));
12
+ };
13
+ /** Pure eligibility check — the single source of truth for pickability. */
14
+ export const isItemPickable = (item, constraints) => {
15
+ const selectableTypes = constraints.selectableTypes?.length ? constraints.selectableTypes : ["file"];
16
+ if (!selectableTypes.includes(item.type)) {
17
+ return false;
18
+ }
19
+ if (item.type === "file" && constraints.extensions?.length) {
20
+ const extension = resolveItemExtension(item);
21
+ if (!extension || !constraints.extensions.map(normalizeExtension).includes(extension)) {
22
+ return false;
23
+ }
24
+ }
25
+ return true;
26
+ };
@@ -0,0 +1,43 @@
1
+ import type { SidebarTab } from "./types.js";
2
+ import { BaseElement } from "../../core/index.js";
3
+ export declare class ContentSidebar extends BaseElement {
4
+ static readonly tagName: string;
5
+ static get observedAttributes(): string[];
6
+ private asideEl;
7
+ private titleEl;
8
+ private toggleEl;
9
+ private bodyEl;
10
+ private emptyEl;
11
+ private tabsHost;
12
+ private readonly presentSlots;
13
+ private forwardedSlotsSignature;
14
+ private suppressTabsEvent;
15
+ get heading(): string;
16
+ set heading(value: string);
17
+ /**
18
+ * Explicit tab configuration; JSON `[{"id","label"}]`. Overrides slot
19
+ * detection — an explicit `[]` renders no tabs. Malformed payloads
20
+ * (non-array JSON, records without a string id/label) are ignored.
21
+ */
22
+ get tabs(): SidebarTab[] | null;
23
+ set tabs(value: SidebarTab[] | null);
24
+ get activeTab(): string;
25
+ set activeTab(value: string);
26
+ get collapsible(): boolean;
27
+ set collapsible(value: boolean);
28
+ get collapsed(): boolean;
29
+ set collapsed(value: boolean);
30
+ /** The tabs the sidebar is currently showing. */
31
+ get resolvedTabs(): SidebarTab[];
32
+ protected renderTemplate(): void;
33
+ protected setupListeners(): void;
34
+ /**
35
+ * The forwarded slots live inside box-tabs: host content slotted as
36
+ * `slot="details"` chains through `<slot name="details" slot="details">`
37
+ * into the matching tabpanel. One forwarded slot exists per known tab id
38
+ * (defaults + configured), so presence detection works before tabs render.
39
+ */
40
+ private syncForwardedSlots;
41
+ private refreshSlotPresence;
42
+ protected update(): void;
43
+ }
@@ -0,0 +1,293 @@
1
+ import { DEFAULT_SIDEBAR_TABS, resolveSidebarTabs } from "./types.js";
2
+ import { Tabs } from "../../components/navigation/tabs.js";
3
+ import { BaseElement } from "../../core/index.js";
4
+ import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
5
+ import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
6
+ const DEFAULT_TAG_NAME = "box-content-sidebar";
7
+ const escapeHtml = (value) => value
8
+ .replaceAll("&", "&amp;")
9
+ .replaceAll("<", "&lt;")
10
+ .replaceAll(">", "&gt;")
11
+ .replaceAll('"', "&quot;")
12
+ .replaceAll("'", "&#39;");
13
+ /** Attribute payloads are author input — validate every record, not just the array. */
14
+ const isSidebarTabRecord = (value) => {
15
+ if (typeof value !== "object" || value === null) {
16
+ return false;
17
+ }
18
+ const tab = value;
19
+ return typeof tab.id === "string" && tab.id.length > 0 && typeof tab.label === "string";
20
+ };
21
+ const elementStyles = `
22
+ :host {
23
+ display: block;
24
+ color: inherit;
25
+ font: inherit;
26
+ }
27
+
28
+ [part="sidebar"] {
29
+ display: grid;
30
+ gap: ${boePanel.gap};
31
+ padding: ${boePanel.padding};
32
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 82%, transparent);
33
+ border-radius: ${boePanel.radius};
34
+ background: var(--boe-token-surface-surface, #ffffff);
35
+ }
36
+
37
+ [part="header"] {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: ${boePanel.gap};
41
+ }
42
+
43
+ [part="title"] {
44
+ flex: 1;
45
+ margin: 0;
46
+ font: inherit;
47
+ font-size: 1.05rem;
48
+ font-weight: 700;
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ color: var(--boe-token-text-text, #1f1e1b);
53
+ }
54
+
55
+ [part="toggle"] {
56
+ appearance: none;
57
+ font: inherit;
58
+ font-size: 0.8rem;
59
+ font-weight: 600;
60
+ padding: 0.3rem 0.65rem;
61
+ border-radius: 999px;
62
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 60%, transparent);
63
+ background: var(--boe-token-surface-surface, #ffffff);
64
+ color: inherit;
65
+ cursor: pointer;
66
+ transition: background ${boeMotionDuration.interactive} ${boeMotionEasing.standard}, border-color ${boeMotionDuration.interactive} ${boeMotionEasing.standard};
67
+ }
68
+
69
+ [part="toggle"]:hover {
70
+ background: var(--boe-token-surface-surface-hover, #f4f4f4);
71
+ border-color: var(--boe-token-stroke-stroke-hover, #bcbcbc);
72
+ }
73
+
74
+ [part="toggle"]:focus-visible {
75
+ outline: none;
76
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent);
77
+ }
78
+
79
+ [part="body"][hidden] {
80
+ display: none;
81
+ }
82
+
83
+ [part="empty"] {
84
+ padding: ${boePanel.padding};
85
+ border-radius: ${boeRadius.large};
86
+ border: 1px dashed color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 70%, transparent);
87
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
88
+ }
89
+
90
+ [part="empty"][hidden] {
91
+ display: none;
92
+ }
93
+ `;
94
+ export class ContentSidebar extends BaseElement {
95
+ static tagName = DEFAULT_TAG_NAME;
96
+ static get observedAttributes() {
97
+ return ["active-tab", "collapsed", "collapsible", "heading", "tabs"];
98
+ }
99
+ asideEl;
100
+ titleEl;
101
+ toggleEl;
102
+ bodyEl;
103
+ emptyEl;
104
+ tabsHost;
105
+ presentSlots = new Set();
106
+ forwardedSlotsSignature = "";
107
+ suppressTabsEvent = false;
108
+ get heading() {
109
+ return this.getAttribute("heading") ?? "Sidebar";
110
+ }
111
+ set heading(value) {
112
+ this.setAttribute("heading", value);
113
+ }
114
+ /**
115
+ * Explicit tab configuration; JSON `[{"id","label"}]`. Overrides slot
116
+ * detection — an explicit `[]` renders no tabs. Malformed payloads
117
+ * (non-array JSON, records without a string id/label) are ignored.
118
+ */
119
+ get tabs() {
120
+ const raw = this.getAttribute("tabs");
121
+ if (!raw) {
122
+ return null;
123
+ }
124
+ try {
125
+ const parsed = JSON.parse(raw);
126
+ return Array.isArray(parsed) && parsed.every(isSidebarTabRecord) ? parsed : null;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ set tabs(value) {
133
+ if (value) {
134
+ this.setAttribute("tabs", JSON.stringify(value));
135
+ return;
136
+ }
137
+ this.removeAttribute("tabs");
138
+ }
139
+ get activeTab() {
140
+ return this.getAttribute("active-tab") ?? "";
141
+ }
142
+ set activeTab(value) {
143
+ if (!value) {
144
+ this.removeAttribute("active-tab");
145
+ return;
146
+ }
147
+ this.setAttribute("active-tab", value);
148
+ }
149
+ get collapsible() {
150
+ return this.hasAttribute("collapsible");
151
+ }
152
+ set collapsible(value) {
153
+ this.toggleAttribute("collapsible", value);
154
+ }
155
+ get collapsed() {
156
+ return this.hasAttribute("collapsed");
157
+ }
158
+ set collapsed(value) {
159
+ this.toggleAttribute("collapsed", value);
160
+ }
161
+ /** The tabs the sidebar is currently showing. */
162
+ get resolvedTabs() {
163
+ return resolveSidebarTabs(this.tabs, this.presentSlots);
164
+ }
165
+ renderTemplate() {
166
+ if (!this.shadowRoot) {
167
+ return;
168
+ }
169
+ this.shadowRoot.innerHTML = `
170
+ <style>${elementStyles}</style>
171
+ <aside part="sidebar">
172
+ <header part="header">
173
+ <h2 part="title"></h2>
174
+ <button type="button" part="toggle" hidden></button>
175
+ </header>
176
+ <div part="body">
177
+ <box-tabs part="tabs"></box-tabs>
178
+ <div part="empty" hidden>No sidebar panels provided.</div>
179
+ </div>
180
+ </aside>
181
+ `;
182
+ this.asideEl = this.shadowRoot.querySelector('[part="sidebar"]');
183
+ this.titleEl = this.shadowRoot.querySelector('[part="title"]');
184
+ this.toggleEl = this.shadowRoot.querySelector('[part="toggle"]');
185
+ this.bodyEl = this.shadowRoot.querySelector('[part="body"]');
186
+ this.emptyEl = this.shadowRoot.querySelector('[part="empty"]');
187
+ this.tabsHost = this.shadowRoot.querySelector('[part="tabs"]');
188
+ }
189
+ setupListeners() {
190
+ this.toggleEl.addEventListener("click", () => {
191
+ this.collapsed = !this.collapsed;
192
+ this.dispatchEvent(new CustomEvent("collapsed-changed", {
193
+ bubbles: true,
194
+ composed: true,
195
+ detail: { collapsed: this.collapsed },
196
+ }));
197
+ });
198
+ this.tabsHost.addEventListener("value-changed", event => {
199
+ // box-tabs re-emits when we push a value into it; only user-driven
200
+ // changes should surface as tab-changed.
201
+ event.stopPropagation();
202
+ const tabId = event.detail?.value ?? "";
203
+ if (this.suppressTabsEvent || !tabId || tabId === this.activeTab) {
204
+ return;
205
+ }
206
+ this.activeTab = tabId;
207
+ this.dispatchEvent(new CustomEvent("tab-changed", {
208
+ bubbles: true,
209
+ composed: true,
210
+ detail: { tabId },
211
+ }));
212
+ });
213
+ }
214
+ /**
215
+ * The forwarded slots live inside box-tabs: host content slotted as
216
+ * `slot="details"` chains through `<slot name="details" slot="details">`
217
+ * into the matching tabpanel. One forwarded slot exists per known tab id
218
+ * (defaults + configured), so presence detection works before tabs render.
219
+ */
220
+ syncForwardedSlots() {
221
+ const ids = new Set(DEFAULT_SIDEBAR_TABS.map(tab => tab.id));
222
+ for (const tab of this.tabs ?? []) {
223
+ ids.add(tab.id);
224
+ }
225
+ const signature = JSON.stringify([...ids].sort());
226
+ if (signature === this.forwardedSlotsSignature) {
227
+ return;
228
+ }
229
+ this.forwardedSlotsSignature = signature;
230
+ this.tabsHost.innerHTML = [...ids]
231
+ .map(id => `<slot name="${escapeHtml(id)}" slot="${escapeHtml(id)}"></slot>`)
232
+ .join("");
233
+ this.tabsHost.querySelectorAll("slot").forEach(slot => {
234
+ slot.addEventListener("slotchange", () => {
235
+ this.refreshSlotPresence();
236
+ if (this.isRendered) {
237
+ this.update();
238
+ }
239
+ });
240
+ });
241
+ this.refreshSlotPresence();
242
+ }
243
+ refreshSlotPresence() {
244
+ this.presentSlots.clear();
245
+ this.tabsHost.querySelectorAll("slot").forEach(slot => {
246
+ if (slot.assignedNodes().length > 0) {
247
+ this.presentSlots.add(slot.name);
248
+ }
249
+ });
250
+ }
251
+ update() {
252
+ if (!this.tabsHost) {
253
+ return;
254
+ }
255
+ this.syncForwardedSlots();
256
+ this.titleEl.textContent = this.heading;
257
+ this.asideEl.setAttribute("role", "complementary");
258
+ this.asideEl.setAttribute("aria-label", this.heading);
259
+ const collapsible = this.collapsible;
260
+ const collapsed = this.collapsed;
261
+ this.toggleEl.hidden = !collapsible;
262
+ this.toggleEl.textContent = collapsed ? "Expand" : "Collapse";
263
+ this.toggleEl.setAttribute("aria-expanded", collapsed ? "false" : "true");
264
+ this.bodyEl.hidden = collapsible && collapsed;
265
+ this.asideEl.setAttribute("data-collapsed", collapsed ? "true" : "false");
266
+ const tabs = this.resolvedTabs;
267
+ this.emptyEl.hidden = tabs.length > 0;
268
+ this.tabsHost.hidden = tabs.length === 0;
269
+ this.tabsHost.label = `${this.heading} tabs`;
270
+ this.suppressTabsEvent = true;
271
+ try {
272
+ this.tabsHost.options = tabs.map(tab => ({ label: tab.label, value: tab.id }));
273
+ const active = this.activeTab;
274
+ const validActive = tabs.some(tab => tab.id === active) ? active : (tabs[0]?.id ?? "");
275
+ if (validActive && this.tabsHost.value !== validActive) {
276
+ this.tabsHost.value = validActive;
277
+ }
278
+ if (validActive !== active) {
279
+ if (validActive) {
280
+ this.setAttribute("active-tab", validActive);
281
+ }
282
+ else {
283
+ this.removeAttribute("active-tab");
284
+ }
285
+ }
286
+ }
287
+ finally {
288
+ this.suppressTabsEvent = false;
289
+ }
290
+ }
291
+ }
292
+ Tabs.register();
293
+ ContentSidebar.register();
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./content-sidebar.js";
@@ -0,0 +1,4 @@
1
+ // Sidebar tab model and resolution
2
+ export * from "./types.js";
3
+ // Composed workflow surface
4
+ export * from "./content-sidebar.js";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * A tab in the content sidebar. `id` doubles as the slot name that feeds the
3
+ * tab's panel, so custom tabs work the same way as the built-in four.
4
+ */
5
+ export interface SidebarTab {
6
+ id: string;
7
+ label: string;
8
+ }
9
+ /** The upstream ContentSidebar tab set, in its canonical order. */
10
+ export declare const DEFAULT_SIDEBAR_TABS: readonly SidebarTab[];
11
+ /**
12
+ * Resolve which tabs the sidebar shows. An explicit configuration wins
13
+ * verbatim (the host is declaring its surface — an empty array means "no
14
+ * tabs", not "use the defaults"); otherwise the default tabs are filtered
15
+ * to the ones with slotted panel content, so an integration that only
16
+ * provides a details panel gets a details-only sidebar.
17
+ */
18
+ export declare const resolveSidebarTabs: (configured: readonly SidebarTab[] | null, presentSlots: ReadonlySet<string>) => SidebarTab[];
@@ -0,0 +1,20 @@
1
+ /** The upstream ContentSidebar tab set, in its canonical order. */
2
+ export const DEFAULT_SIDEBAR_TABS = [
3
+ { id: "details", label: "Details" },
4
+ { id: "activity", label: "Activity" },
5
+ { id: "metadata", label: "Metadata" },
6
+ { id: "versions", label: "Versions" },
7
+ ];
8
+ /**
9
+ * Resolve which tabs the sidebar shows. An explicit configuration wins
10
+ * verbatim (the host is declaring its surface — an empty array means "no
11
+ * tabs", not "use the defaults"); otherwise the default tabs are filtered
12
+ * to the ones with slotted panel content, so an integration that only
13
+ * provides a details panel gets a details-only sidebar.
14
+ */
15
+ export const resolveSidebarTabs = (configured, presentSlots) => {
16
+ if (configured) {
17
+ return [...configured];
18
+ }
19
+ return DEFAULT_SIDEBAR_TABS.filter(tab => presentSlots.has(tab.id));
20
+ };
@@ -0,0 +1,14 @@
1
+ import type { ExplorerFetchLike } from "../content-explorer/types.js";
2
+ import type { UploadTransport } from "./types.js";
3
+ export interface BoxUploadTransportOptions {
4
+ /** Box upload host — uploads do NOT go to api.box.com. */
5
+ uploadBaseUrl?: string;
6
+ fetch?: ExplorerFetchLike;
7
+ }
8
+ /**
9
+ * Multipart upload against `POST {uploadBase}/files/content` (the Box
10
+ * pre-flight-free path). fetch cannot observe upload progress, so
11
+ * `onProgress` fires once at completion; a chunked upload-session transport
12
+ * can implement the same `UploadTransport` contract with real fractions.
13
+ */
14
+ export declare const createBoxUploadTransport: (options?: BoxUploadTransportOptions) => UploadTransport;