@unofficialbox/box-open-elements 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/components/actions/button-group.d.ts +22 -0
- package/dist/components/actions/button-group.js +255 -0
- package/dist/components/actions/button.d.ts +16 -0
- package/dist/components/actions/button.js +190 -0
- package/dist/components/actions/icon-button.d.ts +20 -0
- package/dist/components/actions/icon-button.js +187 -0
- package/dist/components/actions/link-button.d.ts +14 -0
- package/dist/components/actions/link-button.js +137 -0
- package/dist/components/actions/menu-item.d.ts +17 -0
- package/dist/components/actions/menu-item.js +137 -0
- package/dist/components/actions/menu.d.ts +22 -0
- package/dist/components/actions/menu.js +178 -0
- package/dist/components/actions/segmented-control.d.ts +27 -0
- package/dist/components/actions/segmented-control.js +273 -0
- package/dist/components/collections/card.d.ts +13 -0
- package/dist/components/collections/card.js +110 -0
- package/dist/components/collections/carousel.d.ts +40 -0
- package/dist/components/collections/carousel.js +406 -0
- package/dist/components/collections/datalist-item.d.ts +32 -0
- package/dist/components/collections/datalist-item.js +196 -0
- package/dist/components/collections/draggable-list.d.ts +33 -0
- package/dist/components/collections/draggable-list.js +306 -0
- package/dist/components/collections/grid-view.d.ts +38 -0
- package/dist/components/collections/grid-view.js +321 -0
- package/dist/components/collections/pagination.d.ts +19 -0
- package/dist/components/collections/pagination.js +147 -0
- package/dist/components/collections/tree-grid.d.ts +47 -0
- package/dist/components/collections/tree-grid.js +593 -0
- package/dist/components/collections/tree.d.ts +37 -0
- package/dist/components/collections/tree.js +526 -0
- package/dist/components/feedback/alert.d.ts +27 -0
- package/dist/components/feedback/alert.js +263 -0
- package/dist/components/feedback/badge.d.ts +12 -0
- package/dist/components/feedback/badge.js +99 -0
- package/dist/components/feedback/chip.d.ts +33 -0
- package/dist/components/feedback/chip.js +261 -0
- package/dist/components/feedback/empty-state.d.ts +19 -0
- package/dist/components/feedback/empty-state.js +137 -0
- package/dist/components/feedback/error-mask.d.ts +25 -0
- package/dist/components/feedback/error-mask.js +199 -0
- package/dist/components/feedback/help-text.d.ts +22 -0
- package/dist/components/feedback/help-text.js +176 -0
- package/dist/components/feedback/nudge.d.ts +32 -0
- package/dist/components/feedback/nudge.js +299 -0
- package/dist/components/feedback/progress-bar.d.ts +18 -0
- package/dist/components/feedback/progress-bar.js +128 -0
- package/dist/components/feedback/progress-ring.d.ts +21 -0
- package/dist/components/feedback/progress-ring.js +155 -0
- package/dist/components/feedback/progress-steps.d.ts +25 -0
- package/dist/components/feedback/progress-steps.js +276 -0
- package/dist/components/feedback/skeleton.d.ts +14 -0
- package/dist/components/feedback/skeleton.js +95 -0
- package/dist/components/feedback/spinner.d.ts +10 -0
- package/dist/components/feedback/spinner.js +80 -0
- package/dist/components/feedback/toast.d.ts +26 -0
- package/dist/components/feedback/toast.js +205 -0
- package/dist/components/files/drop-zone.d.ts +19 -0
- package/dist/components/files/drop-zone.js +169 -0
- package/dist/components/forms/calendar.d.ts +47 -0
- package/dist/components/forms/calendar.js +499 -0
- package/dist/components/forms/category-selector.d.ts +35 -0
- package/dist/components/forms/category-selector.js +269 -0
- package/dist/components/forms/checkbox-group.d.ts +31 -0
- package/dist/components/forms/checkbox-group.js +267 -0
- package/dist/components/forms/checkbox.d.ts +28 -0
- package/dist/components/forms/checkbox.js +250 -0
- package/dist/components/forms/color-picker.d.ts +34 -0
- package/dist/components/forms/color-picker.js +314 -0
- package/dist/components/forms/combobox.d.ts +37 -0
- package/dist/components/forms/combobox.js +234 -0
- package/dist/components/forms/date-field.d.ts +27 -0
- package/dist/components/forms/date-field.js +187 -0
- package/dist/components/forms/dropdown.d.ts +38 -0
- package/dist/components/forms/dropdown.js +405 -0
- package/dist/components/forms/dual-listbox.d.ts +44 -0
- package/dist/components/forms/dual-listbox.js +484 -0
- package/dist/components/forms/fieldset.d.ts +32 -0
- package/dist/components/forms/fieldset.js +168 -0
- package/dist/components/forms/multi-select.d.ts +30 -0
- package/dist/components/forms/multi-select.js +264 -0
- package/dist/components/forms/number-input.d.ts +32 -0
- package/dist/components/forms/number-input.js +245 -0
- package/dist/components/forms/pill-cloud.d.ts +35 -0
- package/dist/components/forms/pill-cloud.js +230 -0
- package/dist/components/forms/pill-selector-dropdown.d.ts +55 -0
- package/dist/components/forms/pill-selector-dropdown.js +499 -0
- package/dist/components/forms/radio-group.d.ts +32 -0
- package/dist/components/forms/radio-group.js +285 -0
- package/dist/components/forms/range-slider.d.ts +36 -0
- package/dist/components/forms/range-slider.js +294 -0
- package/dist/components/forms/rating.d.ts +31 -0
- package/dist/components/forms/rating.js +302 -0
- package/dist/components/forms/rich-text-input.d.ts +32 -0
- package/dist/components/forms/rich-text-input.js +403 -0
- package/dist/components/forms/search-field.d.ts +27 -0
- package/dist/components/forms/search-field.js +293 -0
- package/dist/components/forms/select.d.ts +30 -0
- package/dist/components/forms/select.js +198 -0
- package/dist/components/forms/slider.d.ts +31 -0
- package/dist/components/forms/slider.js +221 -0
- package/dist/components/forms/spin-button.d.ts +33 -0
- package/dist/components/forms/spin-button.js +352 -0
- package/dist/components/forms/switch.d.ts +32 -0
- package/dist/components/forms/switch.js +302 -0
- package/dist/components/forms/tag-input.d.ts +46 -0
- package/dist/components/forms/tag-input.js +420 -0
- package/dist/components/forms/text-area.d.ts +26 -0
- package/dist/components/forms/text-area.js +188 -0
- package/dist/components/forms/text-field.d.ts +24 -0
- package/dist/components/forms/text-field.js +151 -0
- package/dist/components/forms/time-field.d.ts +29 -0
- package/dist/components/forms/time-field.js +203 -0
- package/dist/components/identity/avatar.d.ts +21 -0
- package/dist/components/identity/avatar.js +191 -0
- package/dist/components/identity/contact-datalist-item.d.ts +31 -0
- package/dist/components/identity/contact-datalist-item.js +235 -0
- package/dist/components/identity/persona.d.ts +28 -0
- package/dist/components/identity/persona.js +272 -0
- package/dist/components/layout/app-shell.d.ts +26 -0
- package/dist/components/layout/app-shell.js +258 -0
- package/dist/components/layout/divider.d.ts +12 -0
- package/dist/components/layout/divider.js +119 -0
- package/dist/components/layout/nav-sidebar.d.ts +23 -0
- package/dist/components/layout/nav-sidebar.js +167 -0
- package/dist/components/layout/section.d.ts +20 -0
- package/dist/components/layout/section.js +143 -0
- package/dist/components/layout/sidebar-toggle-button.d.ts +26 -0
- package/dist/components/layout/sidebar-toggle-button.js +137 -0
- package/dist/components/layout/split-view.d.ts +19 -0
- package/dist/components/layout/split-view.js +157 -0
- package/dist/components/navigation/accordion.d.ts +25 -0
- package/dist/components/navigation/accordion.js +253 -0
- package/dist/components/navigation/tabs.d.ts +27 -0
- package/dist/components/navigation/tabs.js +289 -0
- package/dist/components/overlays/dialog.d.ts +26 -0
- package/dist/components/overlays/dialog.js +267 -0
- package/dist/components/overlays/drawer.d.ts +32 -0
- package/dist/components/overlays/drawer.js +323 -0
- package/dist/components/overlays/popover.d.ts +35 -0
- package/dist/components/overlays/popover.js +317 -0
- package/dist/components/overlays/tooltip.d.ts +26 -0
- package/dist/components/overlays/tooltip.js +204 -0
- package/dist/components/visuals/illustration.d.ts +26 -0
- package/dist/components/visuals/illustration.js +319 -0
- package/dist/core/controller.d.ts +11 -0
- package/dist/core/controller.js +23 -0
- package/dist/core/element.d.ts +22 -0
- package/dist/core/element.js +28 -0
- package/dist/core/event-emitter.d.ts +9 -0
- package/dist/core/event-emitter.js +33 -0
- package/dist/core/form-associated.d.ts +63 -0
- package/dist/core/form-associated.js +218 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +4 -0
- package/dist/core/json-schema.d.ts +1 -0
- package/dist/core/json-schema.js +1 -0
- package/dist/foundations/a11y/focus.d.ts +17 -0
- package/dist/foundations/a11y/focus.js +94 -0
- package/dist/foundations/a11y/heading.d.ts +26 -0
- package/dist/foundations/a11y/heading.js +40 -0
- package/dist/foundations/a11y/index.d.ts +5 -0
- package/dist/foundations/a11y/index.js +3 -0
- package/dist/foundations/a11y/keyboard.d.ts +28 -0
- package/dist/foundations/a11y/keyboard.js +77 -0
- package/dist/foundations/geometry/index.d.ts +1 -0
- package/dist/foundations/geometry/index.js +1 -0
- package/dist/foundations/geometry/tokens.d.ts +123 -0
- package/dist/foundations/geometry/tokens.js +154 -0
- package/dist/foundations/icons/box-iconography.d.ts +479 -0
- package/dist/foundations/icons/box-iconography.generated.d.ts +2370 -0
- package/dist/foundations/icons/box-iconography.generated.js +4010 -0
- package/dist/foundations/icons/box-iconography.js +39 -0
- package/dist/foundations/icons/index.d.ts +1 -0
- package/dist/foundations/icons/index.js +1 -0
- package/dist/foundations/motion/index.d.ts +2 -0
- package/dist/foundations/motion/index.js +1 -0
- package/dist/foundations/motion/tokens.d.ts +39 -0
- package/dist/foundations/motion/tokens.js +43 -0
- package/dist/foundations/tokens/box-dark.d.ts +11 -0
- package/dist/foundations/tokens/box-dark.js +46 -0
- package/dist/foundations/tokens/box-defaults.d.ts +5 -0
- package/dist/foundations/tokens/box-defaults.js +52 -0
- package/dist/foundations/tokens/index.d.ts +5 -0
- package/dist/foundations/tokens/index.js +4 -0
- package/dist/foundations/tokens/interaction.d.ts +28 -0
- package/dist/foundations/tokens/interaction.js +72 -0
- package/dist/foundations/tokens/registry.d.ts +14 -0
- package/dist/foundations/tokens/registry.js +92 -0
- package/dist/foundations/tokens/types.d.ts +16 -0
- package/dist/foundations/tokens/types.js +1 -0
- package/dist/foundations/typography/index.d.ts +2 -0
- package/dist/foundations/typography/index.js +1 -0
- package/dist/foundations/typography/tokens.d.ts +76 -0
- package/dist/foundations/typography/tokens.js +27 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +98 -0
- package/dist/patterns/content-explorer/actions/controller.d.ts +12 -0
- package/dist/patterns/content-explorer/actions/controller.js +36 -0
- package/dist/patterns/content-explorer/actions/index.d.ts +2 -0
- package/dist/patterns/content-explorer/actions/index.js +1 -0
- package/dist/patterns/content-explorer/actions/types.d.ts +13 -0
- package/dist/patterns/content-explorer/actions/types.js +1 -0
- package/dist/patterns/content-explorer/adapters/action-menu.d.ts +24 -0
- package/dist/patterns/content-explorer/adapters/action-menu.js +312 -0
- package/dist/patterns/content-explorer/adapters/breadcrumbs.d.ts +15 -0
- package/dist/patterns/content-explorer/adapters/breadcrumbs.js +161 -0
- package/dist/patterns/content-explorer/adapters/item-summary.d.ts +11 -0
- package/dist/patterns/content-explorer/adapters/item-summary.js +72 -0
- package/dist/patterns/content-explorer/adapters/items.d.ts +4 -0
- package/dist/patterns/content-explorer/adapters/items.js +12 -0
- package/dist/patterns/content-explorer/adapters/list.d.ts +28 -0
- package/dist/patterns/content-explorer/adapters/list.js +527 -0
- package/dist/patterns/content-explorer/adapters/table.d.ts +23 -0
- package/dist/patterns/content-explorer/adapters/table.js +467 -0
- package/dist/patterns/content-explorer/adapters/toolbar.d.ts +19 -0
- package/dist/patterns/content-explorer/adapters/toolbar.js +234 -0
- package/dist/patterns/content-explorer/box-transport.d.ts +44 -0
- package/dist/patterns/content-explorer/box-transport.js +229 -0
- package/dist/patterns/content-explorer/collection/controller.d.ts +17 -0
- package/dist/patterns/content-explorer/collection/controller.js +67 -0
- package/dist/patterns/content-explorer/collection/index.d.ts +2 -0
- package/dist/patterns/content-explorer/collection/index.js +1 -0
- package/dist/patterns/content-explorer/collection/types.d.ts +20 -0
- package/dist/patterns/content-explorer/collection/types.js +1 -0
- package/dist/patterns/content-explorer/content-explorer.d.ts +85 -0
- package/dist/patterns/content-explorer/content-explorer.js +745 -0
- package/dist/patterns/content-explorer/contracts.d.ts +32 -0
- package/dist/patterns/content-explorer/contracts.js +114 -0
- package/dist/patterns/content-explorer/controller.d.ts +34 -0
- package/dist/patterns/content-explorer/controller.js +399 -0
- package/dist/patterns/content-explorer/host-bindings.d.ts +50 -0
- package/dist/patterns/content-explorer/host-bindings.js +76 -0
- package/dist/patterns/content-explorer/index.d.ts +18 -0
- package/dist/patterns/content-explorer/index.js +24 -0
- package/dist/patterns/content-explorer/navigation/controller.d.ts +10 -0
- package/dist/patterns/content-explorer/navigation/controller.js +48 -0
- package/dist/patterns/content-explorer/navigation/index.d.ts +2 -0
- package/dist/patterns/content-explorer/navigation/index.js +1 -0
- package/dist/patterns/content-explorer/navigation/types.d.ts +20 -0
- package/dist/patterns/content-explorer/navigation/types.js +1 -0
- package/dist/patterns/content-explorer/schemas.d.ts +6 -0
- package/dist/patterns/content-explorer/schemas.js +120 -0
- package/dist/patterns/content-explorer/selection/controller.d.ts +12 -0
- package/dist/patterns/content-explorer/selection/controller.js +54 -0
- package/dist/patterns/content-explorer/selection/index.d.ts +2 -0
- package/dist/patterns/content-explorer/selection/index.js +1 -0
- package/dist/patterns/content-explorer/selection/types.d.ts +14 -0
- package/dist/patterns/content-explorer/selection/types.js +1 -0
- package/dist/patterns/content-explorer/types.d.ts +209 -0
- package/dist/patterns/content-explorer/types.js +10 -0
- package/dist/patterns/file-request/file-request-builder.d.ts +36 -0
- package/dist/patterns/file-request/file-request-builder.js +344 -0
- package/dist/patterns/file-request/index.d.ts +1 -0
- package/dist/patterns/file-request/index.js +1 -0
- package/dist/patterns/governance/governance-panel.d.ts +40 -0
- package/dist/patterns/governance/governance-panel.js +351 -0
- package/dist/patterns/governance/index.d.ts +1 -0
- package/dist/patterns/governance/index.js +1 -0
- package/dist/patterns/insights/bar-chart.d.ts +43 -0
- package/dist/patterns/insights/bar-chart.js +462 -0
- package/dist/patterns/insights/chart-panel.d.ts +43 -0
- package/dist/patterns/insights/chart-panel.js +436 -0
- package/dist/patterns/insights/donut-chart.d.ts +43 -0
- package/dist/patterns/insights/donut-chart.js +535 -0
- package/dist/patterns/insights/index.d.ts +5 -0
- package/dist/patterns/insights/index.js +5 -0
- package/dist/patterns/insights/line-chart.d.ts +46 -0
- package/dist/patterns/insights/line-chart.js +511 -0
- package/dist/patterns/insights/metric-card.d.ts +36 -0
- package/dist/patterns/insights/metric-card.js +278 -0
- package/dist/patterns/item/bulk-action-bar.d.ts +35 -0
- package/dist/patterns/item/bulk-action-bar.js +325 -0
- package/dist/patterns/item/index.d.ts +4 -0
- package/dist/patterns/item/index.js +4 -0
- package/dist/patterns/item/item-details-panel.d.ts +61 -0
- package/dist/patterns/item/item-details-panel.js +429 -0
- package/dist/patterns/item/item-form.d.ts +54 -0
- package/dist/patterns/item/item-form.js +485 -0
- package/dist/patterns/item/preview-header.d.ts +46 -0
- package/dist/patterns/item/preview-header.js +323 -0
- package/dist/patterns/metadata/contracts.d.ts +75 -0
- package/dist/patterns/metadata/contracts.js +91 -0
- package/dist/patterns/metadata/index.d.ts +4 -0
- package/dist/patterns/metadata/index.js +4 -0
- package/dist/patterns/metadata/metadata-filter-builder.d.ts +43 -0
- package/dist/patterns/metadata/metadata-filter-builder.js +362 -0
- package/dist/patterns/metadata/metadata-inspector.d.ts +31 -0
- package/dist/patterns/metadata/metadata-inspector.js +245 -0
- package/dist/patterns/metadata/schemas.d.ts +5 -0
- package/dist/patterns/metadata/schemas.js +66 -0
- package/dist/patterns/preview/annotation-inspector.d.ts +44 -0
- package/dist/patterns/preview/annotation-inspector.js +380 -0
- package/dist/patterns/preview/annotation-thread.d.ts +37 -0
- package/dist/patterns/preview/annotation-thread.js +338 -0
- package/dist/patterns/preview/annotation-toolbar.d.ts +42 -0
- package/dist/patterns/preview/annotation-toolbar.js +393 -0
- package/dist/patterns/preview/content-preview-adapter.d.ts +13 -0
- package/dist/patterns/preview/content-preview-adapter.js +30 -0
- package/dist/patterns/preview/index.d.ts +6 -0
- package/dist/patterns/preview/index.js +6 -0
- package/dist/patterns/preview/preview-element.d.ts +41 -0
- package/dist/patterns/preview/preview-element.js +429 -0
- package/dist/patterns/preview/provider-adapter.d.ts +36 -0
- package/dist/patterns/preview/provider-adapter.js +29 -0
- package/dist/patterns/search/filter-bar.d.ts +50 -0
- package/dist/patterns/search/filter-bar.js +379 -0
- package/dist/patterns/search/index.d.ts +3 -0
- package/dist/patterns/search/index.js +3 -0
- package/dist/patterns/search/saved-view-picker.d.ts +25 -0
- package/dist/patterns/search/saved-view-picker.js +239 -0
- package/dist/patterns/search/search-results-header.d.ts +34 -0
- package/dist/patterns/search/search-results-header.js +288 -0
- package/dist/patterns/share/access-stats.d.ts +19 -0
- package/dist/patterns/share/access-stats.js +204 -0
- package/dist/patterns/share/collaborator-avatars.d.ts +22 -0
- package/dist/patterns/share/collaborator-avatars.js +214 -0
- package/dist/patterns/share/contracts.d.ts +65 -0
- package/dist/patterns/share/contracts.js +75 -0
- package/dist/patterns/share/index.d.ts +14 -0
- package/dist/patterns/share/index.js +14 -0
- package/dist/patterns/share/invite-collaborators-contracts.d.ts +38 -0
- package/dist/patterns/share/invite-collaborators-contracts.js +1 -0
- package/dist/patterns/share/invite-collaborators-controller.d.ts +40 -0
- package/dist/patterns/share/invite-collaborators-controller.js +86 -0
- package/dist/patterns/share/invite-collaborators-modal.d.ts +46 -0
- package/dist/patterns/share/invite-collaborators-modal.js +465 -0
- package/dist/patterns/share/permission-matrix.d.ts +37 -0
- package/dist/patterns/share/permission-matrix.js +360 -0
- package/dist/patterns/share/presence-contracts.d.ts +26 -0
- package/dist/patterns/share/presence-contracts.js +1 -0
- package/dist/patterns/share/presence-controller.d.ts +31 -0
- package/dist/patterns/share/presence-controller.js +47 -0
- package/dist/patterns/share/presence.d.ts +32 -0
- package/dist/patterns/share/presence.js +255 -0
- package/dist/patterns/share/schemas.d.ts +4 -0
- package/dist/patterns/share/schemas.js +41 -0
- package/dist/patterns/share/share-panel.d.ts +75 -0
- package/dist/patterns/share/share-panel.js +565 -0
- package/dist/patterns/share/unified-share-controller.d.ts +52 -0
- package/dist/patterns/share/unified-share-controller.js +97 -0
- package/dist/patterns/share/unified-share-modal.d.ts +50 -0
- package/dist/patterns/share/unified-share-modal.js +646 -0
- package/dist/patterns/task/index.d.ts +2 -0
- package/dist/patterns/task/index.js +2 -0
- package/dist/patterns/task/review-queue-item.d.ts +45 -0
- package/dist/patterns/task/review-queue-item.js +403 -0
- package/dist/patterns/task/task-assignment-panel.d.ts +50 -0
- package/dist/patterns/task/task-assignment-panel.js +450 -0
- package/package.json +151 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
3
|
+
const DEFAULT_TAG_NAME = "box-collaborator-avatars";
|
|
4
|
+
const escapeHtml = (value) => value
|
|
5
|
+
.replaceAll("&", "&")
|
|
6
|
+
.replaceAll("<", "<")
|
|
7
|
+
.replaceAll(">", ">")
|
|
8
|
+
.replaceAll('"', """)
|
|
9
|
+
.replaceAll("'", "'");
|
|
10
|
+
const initialsFromName = (name) => name
|
|
11
|
+
.trim()
|
|
12
|
+
.split(/\s+/)
|
|
13
|
+
.slice(0, 2)
|
|
14
|
+
.map(segment => segment[0] ?? "")
|
|
15
|
+
.join("")
|
|
16
|
+
.toUpperCase();
|
|
17
|
+
const isCollaborator = (value) => {
|
|
18
|
+
if (typeof value !== "object" || value === null) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const candidate = value;
|
|
22
|
+
return (typeof candidate.name === "string" &&
|
|
23
|
+
(candidate.id === undefined || typeof candidate.id === "string") &&
|
|
24
|
+
(candidate.initials === undefined || typeof candidate.initials === "string") &&
|
|
25
|
+
(candidate.src === undefined || typeof candidate.src === "string"));
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A stacked "avatar pile" for the collaborators on an item, with a `+N` overflow
|
|
29
|
+
* chip once the count exceeds `max`. A composition: data arrives via the
|
|
30
|
+
* `collaborators` property (no transport). Each visible avatar is a button that
|
|
31
|
+
* emits `select`; the overflow chip emits `overflow`. The group is labelled for
|
|
32
|
+
* assistive tech and the hidden count is announced.
|
|
33
|
+
*/
|
|
34
|
+
const elementStyles = `
|
|
35
|
+
:host {
|
|
36
|
+
display: inline-block;
|
|
37
|
+
color: inherit;
|
|
38
|
+
font: inherit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[part="group"] {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[part="avatar"],
|
|
47
|
+
[part="overflow"] {
|
|
48
|
+
appearance: none;
|
|
49
|
+
display: inline-grid;
|
|
50
|
+
place-items: center;
|
|
51
|
+
inline-size: 2.1rem;
|
|
52
|
+
block-size: 2.1rem;
|
|
53
|
+
margin-inline-start: -0.55rem;
|
|
54
|
+
padding: 0;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
border: 2px solid var(--boe-token-surface-surface, #ffffff);
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 12%, var(--boe-token-surface-surface, #ffffff) 88%);
|
|
59
|
+
color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 72%, var(--boe-token-text-text, #222222));
|
|
60
|
+
font: inherit;
|
|
61
|
+
font-size: 0.78rem;
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
transition: transform ${boeMotionDuration.fast} ${boeMotionEasing.standard};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
[part="group"] > :first-child {
|
|
68
|
+
margin-inline-start: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[part="avatar"]:hover,
|
|
72
|
+
[part="avatar"]:focus-visible,
|
|
73
|
+
[part="overflow"]:hover,
|
|
74
|
+
[part="overflow"]:focus-visible {
|
|
75
|
+
transform: translateY(-2px);
|
|
76
|
+
outline: none;
|
|
77
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 22%, transparent);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
[part="overflow"] {
|
|
81
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-secondary, #fbfbfb) 82%, var(--boe-token-surface-surface, #ffffff) 18%);
|
|
82
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
[part="avatar-image"] {
|
|
86
|
+
inline-size: 100%;
|
|
87
|
+
block-size: 100%;
|
|
88
|
+
object-fit: cover;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
[part="empty"] {
|
|
92
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
93
|
+
font-size: 0.86rem;
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
export class BoxCollaboratorAvatarsElement extends BaseElement {
|
|
97
|
+
static get observedAttributes() {
|
|
98
|
+
return ["collaborators", "label", "max"];
|
|
99
|
+
}
|
|
100
|
+
get collaborators() {
|
|
101
|
+
const raw = this.getAttribute("collaborators");
|
|
102
|
+
if (!raw) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(raw);
|
|
107
|
+
return Array.isArray(parsed) ? parsed.filter(isCollaborator) : [];
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
set collaborators(value) {
|
|
114
|
+
this.setAttribute("collaborators", JSON.stringify(value));
|
|
115
|
+
}
|
|
116
|
+
get label() {
|
|
117
|
+
return this.getAttribute("label")?.trim() || "Collaborators";
|
|
118
|
+
}
|
|
119
|
+
set label(value) {
|
|
120
|
+
this.setAttribute("label", value);
|
|
121
|
+
}
|
|
122
|
+
get max() {
|
|
123
|
+
const raw = Number(this.getAttribute("max") ?? "5");
|
|
124
|
+
if (!Number.isFinite(raw) || raw < 1) {
|
|
125
|
+
return 5;
|
|
126
|
+
}
|
|
127
|
+
return Math.floor(raw);
|
|
128
|
+
}
|
|
129
|
+
set max(value) {
|
|
130
|
+
this.setAttribute("max", String(value));
|
|
131
|
+
}
|
|
132
|
+
connectedCallback() {
|
|
133
|
+
super.connectedCallback();
|
|
134
|
+
}
|
|
135
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
136
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
137
|
+
}
|
|
138
|
+
renderTemplate() {
|
|
139
|
+
if (!this.shadowRoot) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.shadowRoot.innerHTML = `
|
|
143
|
+
<style>${elementStyles}</style>
|
|
144
|
+
<div part="content-host"></div>
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
update() {
|
|
148
|
+
if (!this.shadowRoot) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const collaborators = this.collaborators;
|
|
152
|
+
const max = this.max;
|
|
153
|
+
const visible = collaborators.slice(0, max);
|
|
154
|
+
const overflow = Math.max(0, collaborators.length - visible.length);
|
|
155
|
+
const avatarsMarkup = visible
|
|
156
|
+
.map((collaborator, index) => {
|
|
157
|
+
const initials = collaborator.initials || initialsFromName(collaborator.name) || "?";
|
|
158
|
+
const inner = collaborator.src
|
|
159
|
+
? `<img part="avatar-image" src="${escapeHtml(collaborator.src)}" alt="" />`
|
|
160
|
+
: escapeHtml(initials);
|
|
161
|
+
return `
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
part="avatar"
|
|
165
|
+
data-index="${index}"
|
|
166
|
+
style="z-index:${visible.length - index};"
|
|
167
|
+
aria-label="${escapeHtml(collaborator.name)}"
|
|
168
|
+
title="${escapeHtml(collaborator.name)}"
|
|
169
|
+
>${inner}</button>
|
|
170
|
+
`;
|
|
171
|
+
})
|
|
172
|
+
.join("");
|
|
173
|
+
const overflowMarkup = overflow
|
|
174
|
+
? `<button type="button" part="overflow" aria-label="${overflow} more" title="${overflow} more">+${overflow}</button>`
|
|
175
|
+
: "";
|
|
176
|
+
const host = this.shadowRoot.querySelector('[part="content-host"]');
|
|
177
|
+
if (!host) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
host.innerHTML = `
|
|
181
|
+
${collaborators.length
|
|
182
|
+
? `<div part="group" role="group" aria-label="${escapeHtml(this.label)}">${avatarsMarkup}${overflowMarkup}</div>`
|
|
183
|
+
: `<div part="group" role="group" aria-label="${escapeHtml(this.label)}"><span part="empty">No collaborators</span></div>`}
|
|
184
|
+
`;
|
|
185
|
+
for (const avatar of Array.from(this.shadowRoot.querySelectorAll('[part="avatar"]'))) {
|
|
186
|
+
avatar.addEventListener("click", () => {
|
|
187
|
+
const index = Number(avatar.dataset.index ?? "-1");
|
|
188
|
+
const collaborator = visible[index];
|
|
189
|
+
if (collaborator) {
|
|
190
|
+
this.dispatchEvent(new CustomEvent("select", {
|
|
191
|
+
bubbles: true,
|
|
192
|
+
composed: true,
|
|
193
|
+
detail: { id: collaborator.id, name: collaborator.name },
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
this.shadowRoot.querySelector('[part="overflow"]')?.addEventListener("click", () => {
|
|
199
|
+
this.dispatchEvent(new CustomEvent("overflow", {
|
|
200
|
+
bubbles: true,
|
|
201
|
+
composed: true,
|
|
202
|
+
detail: { count: overflow },
|
|
203
|
+
}));
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export const defineBoxCollaboratorAvatarsElement = (tagName = DEFAULT_TAG_NAME) => {
|
|
208
|
+
const existingElement = customElements.get(tagName);
|
|
209
|
+
if (existingElement) {
|
|
210
|
+
return existingElement;
|
|
211
|
+
}
|
|
212
|
+
customElements.define(tagName, BoxCollaboratorAvatarsElement);
|
|
213
|
+
return BoxCollaboratorAvatarsElement;
|
|
214
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ShareFetchLike {
|
|
2
|
+
(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
3
|
+
}
|
|
4
|
+
export interface ShareRequestContext {
|
|
5
|
+
locale?: string;
|
|
6
|
+
requestId?: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export interface SharedLinkState {
|
|
10
|
+
url?: string;
|
|
11
|
+
access?: "open" | "company" | "collaborators";
|
|
12
|
+
passwordEnabled?: boolean;
|
|
13
|
+
canDownload?: boolean;
|
|
14
|
+
canPreview?: boolean;
|
|
15
|
+
expiresAt?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface CollaboratorSummary {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
type: "user" | "group" | "invite";
|
|
21
|
+
role: string;
|
|
22
|
+
status?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ShareState {
|
|
25
|
+
itemId: string;
|
|
26
|
+
itemType: "file" | "folder";
|
|
27
|
+
sharedLink: SharedLinkState | null;
|
|
28
|
+
collaborators: CollaboratorSummary[];
|
|
29
|
+
}
|
|
30
|
+
export interface ShareDataSource {
|
|
31
|
+
getShareState(input: {
|
|
32
|
+
itemId: string;
|
|
33
|
+
itemType: "file" | "folder";
|
|
34
|
+
context?: ShareRequestContext;
|
|
35
|
+
}): Promise<ShareState>;
|
|
36
|
+
updateSharedLink(input: {
|
|
37
|
+
itemId: string;
|
|
38
|
+
itemType: "file" | "folder";
|
|
39
|
+
sharedLink: SharedLinkState | null;
|
|
40
|
+
context?: ShareRequestContext;
|
|
41
|
+
}): Promise<ShareState>;
|
|
42
|
+
listCollaborators(input: {
|
|
43
|
+
itemId: string;
|
|
44
|
+
itemType: "file" | "folder";
|
|
45
|
+
context?: ShareRequestContext;
|
|
46
|
+
}): Promise<CollaboratorSummary[]>;
|
|
47
|
+
}
|
|
48
|
+
export interface ShareHttpDataSourceOptions {
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
fetch?: ShareFetchLike;
|
|
51
|
+
headers?: Record<string, string>;
|
|
52
|
+
buildGetShareStateUrl?: (input: {
|
|
53
|
+
itemId: string;
|
|
54
|
+
itemType: "file" | "folder";
|
|
55
|
+
}) => string;
|
|
56
|
+
buildUpdateSharedLinkUrl?: (input: {
|
|
57
|
+
itemId: string;
|
|
58
|
+
itemType: "file" | "folder";
|
|
59
|
+
}) => string;
|
|
60
|
+
buildListCollaboratorsUrl?: (input: {
|
|
61
|
+
itemId: string;
|
|
62
|
+
itemType: "file" | "folder";
|
|
63
|
+
}) => string;
|
|
64
|
+
}
|
|
65
|
+
export declare const createHttpShareDataSource: (options?: ShareHttpDataSourceOptions) => ShareDataSource;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const resolveFetch = (providedFetch) => {
|
|
2
|
+
if (providedFetch) {
|
|
3
|
+
return providedFetch;
|
|
4
|
+
}
|
|
5
|
+
if (typeof globalThis.fetch !== "function") {
|
|
6
|
+
throw new Error("A fetch implementation is required to use the share HTTP data source.");
|
|
7
|
+
}
|
|
8
|
+
return globalThis.fetch.bind(globalThis);
|
|
9
|
+
};
|
|
10
|
+
const getErrorMessage = async (response) => {
|
|
11
|
+
try {
|
|
12
|
+
const payload = (await response.json());
|
|
13
|
+
if (payload.message) {
|
|
14
|
+
return payload.code ? `${payload.code}: ${payload.message}` : payload.message;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// ignore parse issues and fall back to status text
|
|
19
|
+
}
|
|
20
|
+
return `Share request failed with status ${response.status}`;
|
|
21
|
+
};
|
|
22
|
+
export const createHttpShareDataSource = (options = {}) => {
|
|
23
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
24
|
+
const baseUrl = (options.baseUrl ?? "/api/share").replace(/\/$/, "");
|
|
25
|
+
const withContextHeaders = (context) => ({
|
|
26
|
+
accept: "application/json",
|
|
27
|
+
...(context?.locale ? { "accept-language": context.locale } : {}),
|
|
28
|
+
...(context?.requestId ? { "x-request-id": context.requestId } : {}),
|
|
29
|
+
...options.headers,
|
|
30
|
+
});
|
|
31
|
+
const readJson = async (response) => {
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(await getErrorMessage(response));
|
|
34
|
+
}
|
|
35
|
+
return (await response.json());
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
async getShareState(input) {
|
|
39
|
+
const url = options.buildGetShareStateUrl?.(input) ??
|
|
40
|
+
`${baseUrl}/items/${encodeURIComponent(input.itemType)}/${encodeURIComponent(input.itemId)}`;
|
|
41
|
+
const response = await fetchImpl(url, {
|
|
42
|
+
method: "GET",
|
|
43
|
+
headers: withContextHeaders(input.context),
|
|
44
|
+
signal: input.context?.signal,
|
|
45
|
+
});
|
|
46
|
+
return readJson(response);
|
|
47
|
+
},
|
|
48
|
+
async updateSharedLink(input) {
|
|
49
|
+
const url = options.buildUpdateSharedLinkUrl?.(input) ??
|
|
50
|
+
`${baseUrl}/items/${encodeURIComponent(input.itemType)}/${encodeURIComponent(input.itemId)}/shared-link`;
|
|
51
|
+
const response = await fetchImpl(url, {
|
|
52
|
+
method: "PUT",
|
|
53
|
+
headers: {
|
|
54
|
+
...withContextHeaders(input.context),
|
|
55
|
+
"content-type": "application/json",
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
sharedLink: input.sharedLink,
|
|
59
|
+
}),
|
|
60
|
+
signal: input.context?.signal,
|
|
61
|
+
});
|
|
62
|
+
return readJson(response);
|
|
63
|
+
},
|
|
64
|
+
async listCollaborators(input) {
|
|
65
|
+
const url = options.buildListCollaboratorsUrl?.(input) ??
|
|
66
|
+
`${baseUrl}/items/${encodeURIComponent(input.itemType)}/${encodeURIComponent(input.itemId)}/collaborators`;
|
|
67
|
+
const response = await fetchImpl(url, {
|
|
68
|
+
method: "GET",
|
|
69
|
+
headers: withContextHeaders(input.context),
|
|
70
|
+
signal: input.context?.signal,
|
|
71
|
+
});
|
|
72
|
+
return readJson(response);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./contracts.js";
|
|
2
|
+
export * from "./schemas.js";
|
|
3
|
+
export * from "./access-stats.js";
|
|
4
|
+
export * from "./collaborator-avatars.js";
|
|
5
|
+
export * from "./invite-collaborators-contracts.js";
|
|
6
|
+
export * from "./invite-collaborators-controller.js";
|
|
7
|
+
export * from "./invite-collaborators-modal.js";
|
|
8
|
+
export * from "./permission-matrix.js";
|
|
9
|
+
export * from "./presence-contracts.js";
|
|
10
|
+
export * from "./presence-controller.js";
|
|
11
|
+
export * from "./presence.js";
|
|
12
|
+
export * from "./share-panel.js";
|
|
13
|
+
export * from "./unified-share-controller.js";
|
|
14
|
+
export * from "./unified-share-modal.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./contracts.js";
|
|
2
|
+
export * from "./schemas.js";
|
|
3
|
+
export * from "./access-stats.js";
|
|
4
|
+
export * from "./collaborator-avatars.js";
|
|
5
|
+
export * from "./invite-collaborators-contracts.js";
|
|
6
|
+
export * from "./invite-collaborators-controller.js";
|
|
7
|
+
export * from "./invite-collaborators-modal.js";
|
|
8
|
+
export * from "./permission-matrix.js";
|
|
9
|
+
export * from "./presence-contracts.js";
|
|
10
|
+
export * from "./presence-controller.js";
|
|
11
|
+
export * from "./presence.js";
|
|
12
|
+
export * from "./share-panel.js";
|
|
13
|
+
export * from "./unified-share-controller.js";
|
|
14
|
+
export * from "./unified-share-modal.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** A role that can be granted to an invited collaborator. */
|
|
2
|
+
export interface InviteRole {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
/** The payload submitted when inviting collaborators to an item. */
|
|
7
|
+
export interface InviteCollaboratorsInput {
|
|
8
|
+
itemId: string;
|
|
9
|
+
recipients: string[];
|
|
10
|
+
role: string;
|
|
11
|
+
message?: string;
|
|
12
|
+
}
|
|
13
|
+
/** The transport's response to an invite submission. */
|
|
14
|
+
export interface InviteResult {
|
|
15
|
+
invited: string[];
|
|
16
|
+
failed?: {
|
|
17
|
+
recipient: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The transport a host provides to actually deliver invitations. Narrow by
|
|
23
|
+
* design: the workflow owns the form state and validation; the transport owns
|
|
24
|
+
* delivery (an API call, a queue, a mock).
|
|
25
|
+
*/
|
|
26
|
+
export interface InviteCollaboratorsTransport {
|
|
27
|
+
sendInvites(input: InviteCollaboratorsInput): Promise<InviteResult>;
|
|
28
|
+
}
|
|
29
|
+
export type InviteStatus = "idle" | "submitting" | "success" | "error";
|
|
30
|
+
/** The controller's observable state for the invite flow. */
|
|
31
|
+
export interface InviteCollaboratorsState {
|
|
32
|
+
recipients: string[];
|
|
33
|
+
role: string;
|
|
34
|
+
message: string;
|
|
35
|
+
status: InviteStatus;
|
|
36
|
+
error: string | null;
|
|
37
|
+
result: InviteResult | null;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Controller } from "../../core/index.js";
|
|
2
|
+
import type { InviteCollaboratorsState, InviteCollaboratorsTransport, InviteResult } from "./invite-collaborators-contracts.js";
|
|
3
|
+
export interface InviteCollaboratorsControllerConfig {
|
|
4
|
+
itemId: string;
|
|
5
|
+
transport: InviteCollaboratorsTransport;
|
|
6
|
+
role?: string;
|
|
7
|
+
}
|
|
8
|
+
type InviteCollaboratorsEvents = {
|
|
9
|
+
stateChanged: {
|
|
10
|
+
state: InviteCollaboratorsState;
|
|
11
|
+
};
|
|
12
|
+
submitted: {
|
|
13
|
+
result: InviteResult;
|
|
14
|
+
};
|
|
15
|
+
error: {
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Headless controller for the invite-collaborators flow. It owns the form state
|
|
21
|
+
* — the recipient list (validated, de-duplicated emails), the granted role, and
|
|
22
|
+
* an optional message — plus the async submission lifecycle, delegating actual
|
|
23
|
+
* delivery to the injected transport. Consumers subscribe to `stateChanged` and
|
|
24
|
+
* render; `submit()` resolves to whether the invite succeeded.
|
|
25
|
+
*/
|
|
26
|
+
export declare class InviteCollaboratorsController extends Controller<InviteCollaboratorsState, InviteCollaboratorsEvents> {
|
|
27
|
+
private readonly config;
|
|
28
|
+
constructor(config: InviteCollaboratorsControllerConfig);
|
|
29
|
+
get recipients(): string[];
|
|
30
|
+
private update;
|
|
31
|
+
/** Add a validated, lower-cased email. Returns false for invalid or duplicate input. */
|
|
32
|
+
addRecipient(email: string): boolean;
|
|
33
|
+
removeRecipient(email: string): void;
|
|
34
|
+
setRole(role: string): void;
|
|
35
|
+
setMessage(message: string): void;
|
|
36
|
+
reset(): void;
|
|
37
|
+
/** Submit the current recipients via the transport. No-op without recipients or while submitting. */
|
|
38
|
+
submit(): Promise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Controller } from "../../core/index.js";
|
|
2
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3
|
+
/**
|
|
4
|
+
* Headless controller for the invite-collaborators flow. It owns the form state
|
|
5
|
+
* — the recipient list (validated, de-duplicated emails), the granted role, and
|
|
6
|
+
* an optional message — plus the async submission lifecycle, delegating actual
|
|
7
|
+
* delivery to the injected transport. Consumers subscribe to `stateChanged` and
|
|
8
|
+
* render; `submit()` resolves to whether the invite succeeded.
|
|
9
|
+
*/
|
|
10
|
+
export class InviteCollaboratorsController extends Controller {
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super({
|
|
14
|
+
recipients: [],
|
|
15
|
+
role: config.role ?? "editor",
|
|
16
|
+
message: "",
|
|
17
|
+
status: "idle",
|
|
18
|
+
error: null,
|
|
19
|
+
result: null,
|
|
20
|
+
});
|
|
21
|
+
this.config = config;
|
|
22
|
+
}
|
|
23
|
+
get recipients() {
|
|
24
|
+
return this.state.recipients;
|
|
25
|
+
}
|
|
26
|
+
update(patch) {
|
|
27
|
+
this.setState({ ...this.state, ...patch });
|
|
28
|
+
this.emit("stateChanged", { state: this.state });
|
|
29
|
+
}
|
|
30
|
+
/** Add a validated, lower-cased email. Returns false for invalid or duplicate input. */
|
|
31
|
+
addRecipient(email) {
|
|
32
|
+
const value = email.trim().toLowerCase();
|
|
33
|
+
if (!EMAIL_PATTERN.test(value) || this.state.recipients.includes(value)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
this.update({ recipients: [...this.state.recipients, value], error: null });
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
removeRecipient(email) {
|
|
40
|
+
if (!this.state.recipients.includes(email)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.update({ recipients: this.state.recipients.filter(item => item !== email) });
|
|
44
|
+
}
|
|
45
|
+
setRole(role) {
|
|
46
|
+
if (role && role !== this.state.role) {
|
|
47
|
+
this.update({ role });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
setMessage(message) {
|
|
51
|
+
this.update({ message });
|
|
52
|
+
}
|
|
53
|
+
reset() {
|
|
54
|
+
this.update({
|
|
55
|
+
recipients: [],
|
|
56
|
+
message: "",
|
|
57
|
+
status: "idle",
|
|
58
|
+
error: null,
|
|
59
|
+
result: null,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Submit the current recipients via the transport. No-op without recipients or while submitting. */
|
|
63
|
+
async submit() {
|
|
64
|
+
if (!this.state.recipients.length || this.state.status === "submitting") {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
this.update({ status: "submitting", error: null });
|
|
68
|
+
try {
|
|
69
|
+
const result = await this.config.transport.sendInvites({
|
|
70
|
+
itemId: this.config.itemId,
|
|
71
|
+
recipients: [...this.state.recipients],
|
|
72
|
+
role: this.state.role,
|
|
73
|
+
message: this.state.message || undefined,
|
|
74
|
+
});
|
|
75
|
+
this.update({ status: "success", result });
|
|
76
|
+
this.emit("submitted", { result });
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch (caught) {
|
|
80
|
+
const error = caught instanceof Error ? caught.message : "Failed to send invitations";
|
|
81
|
+
this.update({ status: "error", error });
|
|
82
|
+
this.emit("error", { error });
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { InviteCollaboratorsTransport, InviteRole } from "./invite-collaborators-contracts.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The invite-collaborators workflow surface: a modal that collects recipient
|
|
5
|
+
* emails (as removable pills), a role, and an optional message, then submits via
|
|
6
|
+
* an `InviteCollaboratorsController` it owns (created from the injected
|
|
7
|
+
* `transport` + `item-id`). Emits `submitted` on success and `cancel` on
|
|
8
|
+
* dismissal, closing itself in both cases. Structure is built once per open so
|
|
9
|
+
* the live inputs keep focus while only the pills/status update.
|
|
10
|
+
*/
|
|
11
|
+
export declare class BoxInviteCollaboratorsModalElement extends BaseElement {
|
|
12
|
+
static get observedAttributes(): string[];
|
|
13
|
+
private controller;
|
|
14
|
+
private controllerUnsubscribe;
|
|
15
|
+
private transportValue;
|
|
16
|
+
private rolesValue;
|
|
17
|
+
private rolesSignature;
|
|
18
|
+
private readonly focusRestore;
|
|
19
|
+
private wasOpen;
|
|
20
|
+
get open(): boolean;
|
|
21
|
+
set open(value: boolean);
|
|
22
|
+
get itemId(): string;
|
|
23
|
+
set itemId(value: string);
|
|
24
|
+
get heading(): string;
|
|
25
|
+
set heading(value: string);
|
|
26
|
+
get submitLabel(): string;
|
|
27
|
+
set submitLabel(value: string);
|
|
28
|
+
get transport(): InviteCollaboratorsTransport | null;
|
|
29
|
+
set transport(value: InviteCollaboratorsTransport | null);
|
|
30
|
+
get roles(): InviteRole[];
|
|
31
|
+
set roles(value: InviteRole[]);
|
|
32
|
+
connectedCallback(): void;
|
|
33
|
+
disconnectedCallback(): void;
|
|
34
|
+
attributeChangedCallback(name: string): void;
|
|
35
|
+
private ensureController;
|
|
36
|
+
private teardownController;
|
|
37
|
+
private refresh;
|
|
38
|
+
protected renderTemplate(): void;
|
|
39
|
+
protected update(): void;
|
|
40
|
+
private buildStructure;
|
|
41
|
+
private attachListeners;
|
|
42
|
+
private submit;
|
|
43
|
+
private close;
|
|
44
|
+
private updateDynamic;
|
|
45
|
+
}
|
|
46
|
+
export declare const defineBoxInviteCollaboratorsModalElement: (tagName?: string) => typeof BoxInviteCollaboratorsModalElement;
|