@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.
- package/README.md +27 -73
- package/dist/components/collections/table.d.ts +113 -3
- package/dist/components/collections/table.js +540 -37
- package/dist/components/feedback/due-badge.d.ts +38 -0
- package/dist/components/feedback/due-badge.js +168 -0
- package/dist/components/feedback/due-types.d.ts +32 -0
- package/dist/components/feedback/due-types.js +91 -0
- package/dist/components/feedback/progress-steps.d.ts +51 -5
- package/dist/components/feedback/progress-steps.js +200 -22
- package/dist/components/feedback/stage-path.d.ts +44 -0
- package/dist/components/feedback/stage-path.js +235 -0
- package/dist/components/forms/select.d.ts +21 -0
- package/dist/components/forms/select.js +100 -3
- package/dist/components/forms/text-field.d.ts +21 -0
- package/dist/components/forms/text-field.js +100 -1
- package/dist/components/layout/nav-sidebar.d.ts +14 -0
- package/dist/components/layout/nav-sidebar.js +49 -0
- package/dist/components/layout/split-view.d.ts +14 -0
- package/dist/components/layout/split-view.js +80 -1
- package/dist/components/overlays/command-palette.d.ts +62 -0
- package/dist/components/overlays/command-palette.js +537 -0
- package/dist/components/overlays/command-types.d.ts +90 -0
- package/dist/components/overlays/command-types.js +259 -0
- package/dist/components/overlays/drawer.d.ts +18 -0
- package/dist/components/overlays/drawer.js +135 -9
- package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
- package/dist/components/overlays/shortcuts-overlay.js +385 -0
- package/dist/components/overlays/tooltip.js +1 -1
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +2 -0
- package/dist/core/offset-window.d.ts +70 -0
- package/dist/core/offset-window.js +134 -0
- package/dist/core/virtualize.d.ts +71 -0
- package/dist/core/virtualize.js +67 -0
- package/dist/element-maps.d.ts +292 -0
- package/dist/element-maps.js +1 -0
- package/dist/entries/activity-density.d.ts +1 -0
- package/dist/entries/activity-density.js +1 -0
- package/dist/entries/agent-chat.d.ts +1 -0
- package/dist/entries/agent-chat.js +1 -0
- package/dist/entries/audit-log.d.ts +1 -0
- package/dist/entries/audit-log.js +1 -0
- package/dist/entries/command-palette.d.ts +1 -0
- package/dist/entries/command-palette.js +1 -0
- package/dist/entries/compare-view.d.ts +1 -0
- package/dist/entries/compare-view.js +1 -0
- package/dist/entries/diff-viewer.d.ts +1 -0
- package/dist/entries/diff-viewer.js +1 -0
- package/dist/entries/due-badge.d.ts +1 -0
- package/dist/entries/due-badge.js +1 -0
- package/dist/entries/form-wizard.d.ts +1 -0
- package/dist/entries/form-wizard.js +1 -0
- package/dist/entries/lineage-graph.d.ts +1 -0
- package/dist/entries/lineage-graph.js +1 -0
- package/dist/entries/notification-bell.d.ts +1 -0
- package/dist/entries/notification-bell.js +1 -0
- package/dist/entries/notification-inbox.d.ts +1 -0
- package/dist/entries/notification-inbox.js +1 -0
- package/dist/entries/provenance-strip.d.ts +1 -0
- package/dist/entries/provenance-strip.js +1 -0
- package/dist/entries/run-trace.d.ts +1 -0
- package/dist/entries/run-trace.js +1 -0
- package/dist/entries/shortcuts-overlay.d.ts +1 -0
- package/dist/entries/shortcuts-overlay.js +1 -0
- package/dist/entries/signature-ceremony.d.ts +1 -0
- package/dist/entries/signature-ceremony.js +1 -0
- package/dist/entries/stage-path.d.ts +1 -0
- package/dist/entries/stage-path.js +1 -0
- package/dist/entries/timeline.d.ts +1 -0
- package/dist/entries/timeline.js +1 -0
- package/dist/entries/version-graph.d.ts +1 -0
- package/dist/entries/version-graph.js +1 -0
- package/dist/entries/version-list.d.ts +1 -0
- package/dist/entries/version-list.js +1 -0
- package/dist/entries/wizard-summary.d.ts +1 -0
- package/dist/entries/wizard-summary.js +1 -0
- package/dist/entries/work-queue.d.ts +1 -0
- package/dist/entries/work-queue.js +1 -0
- package/dist/entries/workload-board.d.ts +1 -0
- package/dist/entries/workload-board.js +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
- package/dist/patterns/agent-chat/agent-chat.js +820 -0
- package/dist/patterns/agent-chat/controller.d.ts +27 -0
- package/dist/patterns/agent-chat/controller.js +182 -0
- package/dist/patterns/agent-chat/index.d.ts +3 -0
- package/dist/patterns/agent-chat/index.js +6 -0
- package/dist/patterns/agent-chat/types.d.ts +109 -0
- package/dist/patterns/agent-chat/types.js +1 -0
- package/dist/patterns/audit/activity-density.d.ts +68 -0
- package/dist/patterns/audit/activity-density.js +428 -0
- package/dist/patterns/audit/audit-log.d.ts +182 -0
- package/dist/patterns/audit/audit-log.js +1082 -0
- package/dist/patterns/audit/index.d.ts +3 -0
- package/dist/patterns/audit/index.js +7 -0
- package/dist/patterns/audit/shared.d.ts +10 -0
- package/dist/patterns/audit/shared.js +50 -0
- package/dist/patterns/audit/types.d.ts +143 -0
- package/dist/patterns/audit/types.js +327 -0
- package/dist/patterns/audit/window.d.ts +116 -0
- package/dist/patterns/audit/window.js +132 -0
- package/dist/patterns/content-explorer/actions/controller.js +12 -2
- package/dist/patterns/content-explorer/box-transport.js +117 -25
- package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
- package/dist/patterns/content-explorer/content-explorer.js +36 -3
- package/dist/patterns/content-explorer/contracts.d.ts +24 -1
- package/dist/patterns/content-explorer/contracts.js +68 -11
- package/dist/patterns/content-explorer/controller.d.ts +16 -1
- package/dist/patterns/content-explorer/controller.js +130 -4
- package/dist/patterns/content-explorer/navigation/controller.js +11 -9
- package/dist/patterns/content-explorer/types.d.ts +58 -1
- package/dist/patterns/content-picker/content-picker.d.ts +71 -0
- package/dist/patterns/content-picker/content-picker.js +747 -0
- package/dist/patterns/content-picker/controller.d.ts +38 -0
- package/dist/patterns/content-picker/controller.js +141 -0
- package/dist/patterns/content-picker/index.d.ts +3 -0
- package/dist/patterns/content-picker/index.js +6 -0
- package/dist/patterns/content-picker/types.d.ts +52 -0
- package/dist/patterns/content-picker/types.js +26 -0
- package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
- package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
- package/dist/patterns/content-sidebar/index.d.ts +2 -0
- package/dist/patterns/content-sidebar/index.js +4 -0
- package/dist/patterns/content-sidebar/types.d.ts +18 -0
- package/dist/patterns/content-sidebar/types.js +20 -0
- package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
- package/dist/patterns/content-uploader/box-transport.js +54 -0
- package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
- package/dist/patterns/content-uploader/content-uploader.js +559 -0
- package/dist/patterns/content-uploader/controller.d.ts +39 -0
- package/dist/patterns/content-uploader/controller.js +227 -0
- package/dist/patterns/content-uploader/index.d.ts +4 -0
- package/dist/patterns/content-uploader/index.js +8 -0
- package/dist/patterns/content-uploader/types.d.ts +120 -0
- package/dist/patterns/content-uploader/types.js +38 -0
- package/dist/patterns/diff/compare-view.d.ts +47 -0
- package/dist/patterns/diff/compare-view.js +276 -0
- package/dist/patterns/diff/diff-viewer.d.ts +42 -0
- package/dist/patterns/diff/diff-viewer.js +434 -0
- package/dist/patterns/diff/engine.d.ts +22 -0
- package/dist/patterns/diff/engine.js +276 -0
- package/dist/patterns/diff/index.d.ts +4 -0
- package/dist/patterns/diff/index.js +8 -0
- package/dist/patterns/diff/types.d.ts +69 -0
- package/dist/patterns/diff/types.js +24 -0
- package/dist/patterns/form-wizard/controller.d.ts +39 -0
- package/dist/patterns/form-wizard/controller.js +157 -0
- package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
- package/dist/patterns/form-wizard/form-wizard.js +468 -0
- package/dist/patterns/form-wizard/index.d.ts +4 -0
- package/dist/patterns/form-wizard/index.js +8 -0
- package/dist/patterns/form-wizard/types.d.ts +109 -0
- package/dist/patterns/form-wizard/types.js +83 -0
- package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
- package/dist/patterns/form-wizard/wizard-summary.js +324 -0
- package/dist/patterns/internal/safe-href.d.ts +17 -0
- package/dist/patterns/internal/safe-href.js +17 -0
- package/dist/patterns/lineage/index.d.ts +3 -0
- package/dist/patterns/lineage/index.js +6 -0
- package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
- package/dist/patterns/lineage/lineage-graph.js +434 -0
- package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
- package/dist/patterns/lineage/provenance-strip.js +194 -0
- package/dist/patterns/lineage/types.d.ts +53 -0
- package/dist/patterns/lineage/types.js +49 -0
- package/dist/patterns/notifications/index.d.ts +3 -0
- package/dist/patterns/notifications/index.js +6 -0
- package/dist/patterns/notifications/notification-bell.d.ts +38 -0
- package/dist/patterns/notifications/notification-bell.js +198 -0
- package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
- package/dist/patterns/notifications/notification-inbox.js +537 -0
- package/dist/patterns/notifications/types.d.ts +61 -0
- package/dist/patterns/notifications/types.js +108 -0
- package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
- package/dist/patterns/preview/annotation-inspector.js +239 -94
- package/dist/patterns/preview/annotation-thread.d.ts +25 -5
- package/dist/patterns/preview/annotation-thread.js +233 -82
- package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
- package/dist/patterns/preview/annotation-toolbar.js +162 -96
- package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
- package/dist/patterns/preview/content-preview-adapter.js +26 -1
- package/dist/patterns/preview/preview-element.d.ts +15 -2
- package/dist/patterns/preview/preview-element.js +223 -52
- package/dist/patterns/preview/provider-adapter.d.ts +47 -0
- package/dist/patterns/preview/provider-adapter.js +17 -0
- package/dist/patterns/run/index.d.ts +2 -0
- package/dist/patterns/run/index.js +4 -0
- package/dist/patterns/run/run-trace.d.ts +40 -0
- package/dist/patterns/run/run-trace.js +441 -0
- package/dist/patterns/run/types.d.ts +66 -0
- package/dist/patterns/run/types.js +152 -0
- package/dist/patterns/signature/index.d.ts +2 -0
- package/dist/patterns/signature/index.js +4 -0
- package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
- package/dist/patterns/signature/signature-ceremony.js +317 -0
- package/dist/patterns/signature/types.d.ts +68 -0
- package/dist/patterns/signature/types.js +89 -0
- package/dist/patterns/timeline/index.d.ts +2 -0
- package/dist/patterns/timeline/index.js +4 -0
- package/dist/patterns/timeline/timeline.d.ts +27 -0
- package/dist/patterns/timeline/timeline.js +430 -0
- package/dist/patterns/timeline/types.d.ts +36 -0
- package/dist/patterns/timeline/types.js +27 -0
- package/dist/patterns/versions/graph-layout.d.ts +43 -0
- package/dist/patterns/versions/graph-layout.js +159 -0
- package/dist/patterns/versions/index.d.ts +4 -0
- package/dist/patterns/versions/index.js +8 -0
- package/dist/patterns/versions/shared.d.ts +4 -0
- package/dist/patterns/versions/shared.js +13 -0
- package/dist/patterns/versions/types.d.ts +37 -0
- package/dist/patterns/versions/types.js +39 -0
- package/dist/patterns/versions/version-graph.d.ts +36 -0
- package/dist/patterns/versions/version-graph.js +438 -0
- package/dist/patterns/versions/version-list.d.ts +39 -0
- package/dist/patterns/versions/version-list.js +407 -0
- package/dist/patterns/work-queue/controller.d.ts +26 -0
- package/dist/patterns/work-queue/controller.js +150 -0
- package/dist/patterns/work-queue/index.d.ts +4 -0
- package/dist/patterns/work-queue/index.js +7 -0
- package/dist/patterns/work-queue/types.d.ts +128 -0
- package/dist/patterns/work-queue/types.js +45 -0
- package/dist/patterns/work-queue/work-queue.d.ts +48 -0
- package/dist/patterns/work-queue/work-queue.js +534 -0
- package/dist/patterns/work-queue/workload-board.d.ts +53 -0
- package/dist/patterns/work-queue/workload-board.js +608 -0
- 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,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("&", "&")
|
|
9
|
+
.replaceAll("<", "<")
|
|
10
|
+
.replaceAll(">", ">")
|
|
11
|
+
.replaceAll('"', """)
|
|
12
|
+
.replaceAll("'", "'");
|
|
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,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;
|