@unofficialbox/box-open-elements 0.3.0 → 0.4.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/dist/components/actions/link-button.d.ts +9 -0
- package/dist/components/actions/link-button.js +55 -4
- package/dist/components/collections/carousel.d.ts +7 -0
- package/dist/components/collections/carousel.js +71 -4
- package/dist/components/collections/datalist-item.d.ts +6 -0
- package/dist/components/collections/datalist-item.js +39 -1
- package/dist/components/collections/draggable-list.js +1 -1
- package/dist/components/collections/grid-view.js +7 -3
- package/dist/components/feedback/alert.d.ts +2 -0
- package/dist/components/feedback/alert.js +30 -1
- package/dist/components/feedback/badge.d.ts +10 -0
- package/dist/components/feedback/badge.js +70 -4
- package/dist/components/feedback/chip.d.ts +5 -0
- package/dist/components/feedback/chip.js +74 -2
- package/dist/components/feedback/error-mask.d.ts +2 -0
- package/dist/components/feedback/error-mask.js +23 -0
- package/dist/components/feedback/help-text.js +7 -0
- package/dist/components/feedback/spinner.d.ts +4 -0
- package/dist/components/feedback/spinner.js +30 -2
- package/dist/components/feedback/toast.d.ts +5 -0
- package/dist/components/feedback/toast.js +59 -11
- package/dist/components/forms/category-selector.d.ts +11 -0
- package/dist/components/forms/category-selector.js +211 -17
- package/dist/components/forms/checkbox.d.ts +6 -0
- package/dist/components/forms/checkbox.js +58 -1
- package/dist/components/forms/combobox.d.ts +30 -1
- package/dist/components/forms/combobox.js +348 -44
- package/dist/components/forms/date-field.d.ts +7 -0
- package/dist/components/forms/date-field.js +73 -1
- package/dist/components/forms/dropdown.d.ts +5 -0
- package/dist/components/forms/dropdown.js +32 -3
- package/dist/components/forms/radio-group.d.ts +9 -8
- package/dist/components/forms/radio-group.js +43 -6
- package/dist/components/forms/search-field.d.ts +8 -0
- package/dist/components/forms/search-field.js +55 -13
- package/dist/components/forms/select.d.ts +18 -8
- package/dist/components/forms/select.js +104 -14
- package/dist/components/forms/text-field.d.ts +13 -0
- package/dist/components/forms/text-field.js +136 -1
- package/dist/components/forms/time-field.d.ts +11 -0
- package/dist/components/forms/time-field.js +51 -0
- package/dist/components/identity/avatar.d.ts +4 -0
- package/dist/components/identity/avatar.js +65 -1
- package/dist/components/identity/contact-datalist-item.d.ts +8 -0
- package/dist/components/identity/contact-datalist-item.js +71 -2
- package/dist/components/layout/nav-sidebar.js +23 -0
- package/dist/components/layout/sidebar-toggle-button.d.ts +3 -0
- package/dist/components/layout/sidebar-toggle-button.js +21 -2
- package/dist/components/navigation/accordion.d.ts +3 -0
- package/dist/components/navigation/accordion.js +17 -2
- package/dist/components/overlays/guide-tooltip.d.ts +51 -0
- package/dist/components/overlays/guide-tooltip.js +361 -0
- package/dist/components/overlays/tooltip.d.ts +10 -0
- package/dist/components/overlays/tooltip.js +74 -11
- package/dist/foundations/overlay/positioning.js +5 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -42,12 +42,59 @@ const chipStyles = `
|
|
|
42
42
|
background: color-mix(in srgb, var(--boe-token-surface-surface-hover, #f4f4f4) 70%, var(--boe-token-surface-surface-secondary, #fbfbfb) 30%);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
[part="chip"][data-tone="brand"]
|
|
45
|
+
[part="chip"][data-tone="brand"],
|
|
46
|
+
[part="chip"][data-tone="info"] {
|
|
46
47
|
border-color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 26%, transparent);
|
|
47
48
|
background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #ffffff) 90%);
|
|
48
49
|
color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 72%, var(--boe-token-text-text, #222222));
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
[part="chip"][data-tone="success"] {
|
|
53
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26c281) 32%, transparent);
|
|
54
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26c281) 12%, var(--boe-token-surface-surface, #ffffff) 88%);
|
|
55
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26c281) 60%, var(--boe-token-text-text, #222222));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[part="chip"][data-tone="warning"] {
|
|
59
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-status-surface-inprogress, #f5b31b) 40%, transparent);
|
|
60
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-inprogress, #f5b31b) 16%, var(--boe-token-surface-surface, #ffffff) 84%);
|
|
61
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-inprogress, #f5b31b) 58%, var(--boe-token-text-text, #222222));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[part="chip"][data-tone="error"] {
|
|
65
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 30%, transparent);
|
|
66
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 10%, var(--boe-token-surface-surface, #ffffff) 90%);
|
|
67
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 70%, var(--boe-token-text-text, #222222));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Compact size. */
|
|
71
|
+
[part="chip"][data-size="small"] {
|
|
72
|
+
gap: 0.25rem;
|
|
73
|
+
padding: 0.14rem 0.28rem 0.14rem 0.55rem;
|
|
74
|
+
font-size: 0.74rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
[part="chip"][data-size="small"]:not([data-removable="true"]) {
|
|
78
|
+
padding-inline-end: 0.55rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Leading icon slot — hidden (and taking no gap) until content is assigned. */
|
|
82
|
+
[part="icon"] {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
flex: none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[part="icon"]:not(.has-content) {
|
|
89
|
+
display: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[part="icon"]::slotted(*) {
|
|
93
|
+
inline-size: 0.95rem;
|
|
94
|
+
block-size: 0.95rem;
|
|
95
|
+
display: block;
|
|
96
|
+
}
|
|
97
|
+
|
|
51
98
|
[part="chip"][data-selected="true"],
|
|
52
99
|
[part="chip"][data-selected="true"]:hover:not([data-disabled="true"]),
|
|
53
100
|
[part="chip"][data-selected="true"]:active:not([data-disabled="true"]) {
|
|
@@ -94,11 +141,19 @@ const chipStyles = `
|
|
|
94
141
|
*/
|
|
95
142
|
export class BoxChipElement extends BaseElement {
|
|
96
143
|
static get observedAttributes() {
|
|
97
|
-
return ["disabled", "label", "removable", "selectable", "selected", "tone", "value"];
|
|
144
|
+
return ["disabled", "label", "removable", "selectable", "selected", "size", "tone", "value"];
|
|
98
145
|
}
|
|
99
146
|
chipEl;
|
|
100
147
|
labelEl;
|
|
148
|
+
iconSlot;
|
|
101
149
|
removeEl = null;
|
|
150
|
+
/** Chip size: `medium` (default) or `small`. */
|
|
151
|
+
get size() {
|
|
152
|
+
return this.getAttribute("size") === "small" ? "small" : "medium";
|
|
153
|
+
}
|
|
154
|
+
set size(value) {
|
|
155
|
+
this.setAttribute("size", value);
|
|
156
|
+
}
|
|
102
157
|
get label() {
|
|
103
158
|
return this.getAttribute("label") ?? "";
|
|
104
159
|
}
|
|
@@ -169,13 +224,16 @@ export class BoxChipElement extends BaseElement {
|
|
|
169
224
|
this.shadowRoot.innerHTML = `
|
|
170
225
|
<style>${chipStyles}</style>
|
|
171
226
|
<span part="chip">
|
|
227
|
+
<slot name="icon" part="icon"></slot>
|
|
172
228
|
<span part="label"></span>
|
|
173
229
|
</span>
|
|
174
230
|
`;
|
|
175
231
|
this.chipEl = this.shadowRoot.querySelector('[part="chip"]');
|
|
176
232
|
this.labelEl = this.shadowRoot.querySelector('[part="label"]');
|
|
233
|
+
this.iconSlot = this.shadowRoot.querySelector('slot[name="icon"]');
|
|
177
234
|
}
|
|
178
235
|
setupListeners() {
|
|
236
|
+
this.iconSlot.addEventListener("slotchange", () => this.syncIconSlot());
|
|
179
237
|
this.chipEl.addEventListener("click", event => {
|
|
180
238
|
if (event.target.closest('[part="remove"]')) {
|
|
181
239
|
return;
|
|
@@ -217,11 +275,25 @@ export class BoxChipElement extends BaseElement {
|
|
|
217
275
|
this.removeEl = null;
|
|
218
276
|
}
|
|
219
277
|
}
|
|
278
|
+
syncIconSlot() {
|
|
279
|
+
if (!this.iconSlot) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const hasContent = this.iconSlot.assignedNodes({ flatten: true }).length > 0;
|
|
283
|
+
this.iconSlot.classList.toggle("has-content", hasContent);
|
|
284
|
+
}
|
|
220
285
|
update() {
|
|
221
286
|
if (!this.chipEl) {
|
|
222
287
|
return;
|
|
223
288
|
}
|
|
224
289
|
this.chipEl.dataset.tone = this.tone;
|
|
290
|
+
if (this.size === "medium") {
|
|
291
|
+
this.chipEl.removeAttribute("data-size");
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
this.chipEl.dataset.size = this.size;
|
|
295
|
+
}
|
|
296
|
+
this.syncIconSlot();
|
|
225
297
|
this.chipEl.dataset.removable = this.removable ? "true" : "false";
|
|
226
298
|
this.chipEl.dataset.selected = this.selected ? "true" : "false";
|
|
227
299
|
this.chipEl.dataset.disabled = this.disabled ? "true" : "false";
|
|
@@ -9,6 +9,7 @@ export declare class BoxErrorMaskElement extends BaseElement {
|
|
|
9
9
|
static get observedAttributes(): string[];
|
|
10
10
|
private titleEl;
|
|
11
11
|
private messageEl;
|
|
12
|
+
private bodySlot;
|
|
12
13
|
private actionEl;
|
|
13
14
|
get heading(): string;
|
|
14
15
|
set heading(value: string);
|
|
@@ -19,6 +20,7 @@ export declare class BoxErrorMaskElement extends BaseElement {
|
|
|
19
20
|
get actionLabel(): string;
|
|
20
21
|
set actionLabel(value: string);
|
|
21
22
|
protected renderTemplate(): void;
|
|
23
|
+
protected setupListeners(): void;
|
|
22
24
|
private syncActionButton;
|
|
23
25
|
protected update(): void;
|
|
24
26
|
}
|
|
@@ -57,6 +57,17 @@ const errorMaskStyles = `
|
|
|
57
57
|
display: none;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/* Optional rich body — slotted markup below the message.
|
|
61
|
+
Hidden (no column gap) until content is assigned. */
|
|
62
|
+
[part="body"] {
|
|
63
|
+
display: block;
|
|
64
|
+
max-width: 32rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
[part="body"]:not(.has-content) {
|
|
68
|
+
display: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
[part="action"] {
|
|
61
72
|
appearance: none;
|
|
62
73
|
box-sizing: border-box;
|
|
@@ -100,6 +111,7 @@ export class BoxErrorMaskElement extends BaseElement {
|
|
|
100
111
|
}
|
|
101
112
|
titleEl;
|
|
102
113
|
messageEl;
|
|
114
|
+
bodySlot;
|
|
103
115
|
actionEl = null;
|
|
104
116
|
get heading() {
|
|
105
117
|
return this.getAttribute("heading") ?? "Something went wrong";
|
|
@@ -141,10 +153,19 @@ export class BoxErrorMaskElement extends BaseElement {
|
|
|
141
153
|
</span>
|
|
142
154
|
<h2 part="title"></h2>
|
|
143
155
|
<span part="message description" hidden></span>
|
|
156
|
+
<slot part="body"></slot>
|
|
144
157
|
</section>
|
|
145
158
|
`;
|
|
146
159
|
this.titleEl = this.shadowRoot.querySelector('[part="title"]');
|
|
147
160
|
this.messageEl = this.shadowRoot.querySelector('[part~="message"]');
|
|
161
|
+
this.bodySlot = this.shadowRoot.querySelector('slot[part="body"]');
|
|
162
|
+
}
|
|
163
|
+
setupListeners() {
|
|
164
|
+
this.bodySlot.addEventListener("slotchange", () => {
|
|
165
|
+
if (this.isRendered) {
|
|
166
|
+
this.update();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
148
169
|
}
|
|
149
170
|
syncActionButton() {
|
|
150
171
|
const section = this.shadowRoot?.querySelector('[part="error-mask"]');
|
|
@@ -178,6 +199,8 @@ export class BoxErrorMaskElement extends BaseElement {
|
|
|
178
199
|
return;
|
|
179
200
|
}
|
|
180
201
|
this.titleEl.textContent = this.heading;
|
|
202
|
+
const hasBody = this.bodySlot.assignedNodes({ flatten: true }).length > 0;
|
|
203
|
+
this.bodySlot.classList.toggle("has-content", hasBody);
|
|
181
204
|
if (this.message) {
|
|
182
205
|
this.messageEl.hidden = false;
|
|
183
206
|
this.messageEl.textContent = this.message;
|
|
@@ -152,6 +152,13 @@ export class BoxHelpTextElement extends BaseElement {
|
|
|
152
152
|
: "info";
|
|
153
153
|
const iconMarkup = resolveDesignIcon(iconName) ?? "i";
|
|
154
154
|
this.helpTextEl.dataset.tone = this.tone;
|
|
155
|
+
// Error help text is an inline validation error: announce it assertively.
|
|
156
|
+
if (this.tone === "error") {
|
|
157
|
+
this.helpTextEl.setAttribute("role", "alert");
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.helpTextEl.setAttribute("role", "note");
|
|
161
|
+
}
|
|
155
162
|
this.helpTextEl.setAttribute("aria-label", this.label || "Help text");
|
|
156
163
|
this.iconEl.setAttribute("data-icon-source", iconMarkup === "i" ? "text" : "design-system");
|
|
157
164
|
this.iconEl.innerHTML = iconMarkup;
|
|
@@ -2,8 +2,12 @@ import { BaseElement } from "../../core/index.js";
|
|
|
2
2
|
export declare class BoxSpinnerElement extends BaseElement {
|
|
3
3
|
static get observedAttributes(): string[];
|
|
4
4
|
private labelEl;
|
|
5
|
+
private indicatorEl;
|
|
5
6
|
get label(): string;
|
|
6
7
|
set label(value: string);
|
|
8
|
+
/** Indicator size: `small`, `medium` (default), or `large`. */
|
|
9
|
+
get size(): string;
|
|
10
|
+
set size(value: string);
|
|
7
11
|
protected renderTemplate(): void;
|
|
8
12
|
protected update(): void;
|
|
9
13
|
}
|
|
@@ -25,6 +25,18 @@ const spinnerStyles = `
|
|
|
25
25
|
animation: boe-spinner-rotate ${boeMotionDuration.spin} ${boeMotionEasing.linear} infinite;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
[part="indicator"][data-size="small"] {
|
|
29
|
+
width: 0.85rem;
|
|
30
|
+
height: 0.85rem;
|
|
31
|
+
border-width: 2px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[part="indicator"][data-size="large"] {
|
|
35
|
+
width: 1.6rem;
|
|
36
|
+
height: 1.6rem;
|
|
37
|
+
border-width: 3px;
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
@keyframes boe-spinner-rotate {
|
|
29
41
|
to {
|
|
30
42
|
transform: rotate(360deg);
|
|
@@ -41,15 +53,24 @@ const spinnerStyles = `
|
|
|
41
53
|
`;
|
|
42
54
|
export class BoxSpinnerElement extends BaseElement {
|
|
43
55
|
static get observedAttributes() {
|
|
44
|
-
return ["label"];
|
|
56
|
+
return ["label", "size"];
|
|
45
57
|
}
|
|
46
58
|
labelEl;
|
|
59
|
+
indicatorEl;
|
|
47
60
|
get label() {
|
|
48
61
|
return this.getAttribute("label") ?? "Loading";
|
|
49
62
|
}
|
|
50
63
|
set label(value) {
|
|
51
64
|
this.setAttribute("label", value);
|
|
52
65
|
}
|
|
66
|
+
/** Indicator size: `small`, `medium` (default), or `large`. */
|
|
67
|
+
get size() {
|
|
68
|
+
const value = this.getAttribute("size");
|
|
69
|
+
return value === "small" || value === "large" ? value : "medium";
|
|
70
|
+
}
|
|
71
|
+
set size(value) {
|
|
72
|
+
this.setAttribute("size", value);
|
|
73
|
+
}
|
|
53
74
|
renderTemplate() {
|
|
54
75
|
if (!this.shadowRoot) {
|
|
55
76
|
return;
|
|
@@ -62,12 +83,19 @@ export class BoxSpinnerElement extends BaseElement {
|
|
|
62
83
|
</div>
|
|
63
84
|
`;
|
|
64
85
|
this.labelEl = this.shadowRoot.querySelector('[part="label"]');
|
|
86
|
+
this.indicatorEl = this.shadowRoot.querySelector('[part="indicator"]');
|
|
65
87
|
}
|
|
66
88
|
update() {
|
|
67
|
-
if (!this.labelEl) {
|
|
89
|
+
if (!this.labelEl || !this.indicatorEl) {
|
|
68
90
|
return;
|
|
69
91
|
}
|
|
70
92
|
this.labelEl.textContent = this.label;
|
|
93
|
+
if (this.size === "medium") {
|
|
94
|
+
this.indicatorEl.removeAttribute("data-size");
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.indicatorEl.dataset.size = this.size;
|
|
98
|
+
}
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
export const defineBoxSpinnerElement = (tagName = DEFAULT_TAG_NAME) => {
|
|
@@ -5,9 +5,14 @@ export declare class BoxToastElement extends BaseElement {
|
|
|
5
5
|
private timeoutId;
|
|
6
6
|
private toastEl;
|
|
7
7
|
private messageEl;
|
|
8
|
+
private actionSlot;
|
|
8
9
|
private dismissEl;
|
|
10
|
+
/** Auto-dismiss delay in ms for the declarative `open` path. 0 = sticky. */
|
|
11
|
+
get duration(): number;
|
|
12
|
+
set duration(value: number);
|
|
9
13
|
get open(): boolean;
|
|
10
14
|
set open(value: boolean);
|
|
15
|
+
private scheduleAutoDismiss;
|
|
11
16
|
get message(): string;
|
|
12
17
|
set message(value: string);
|
|
13
18
|
get tone(): string;
|
|
@@ -53,6 +53,28 @@ const toastStyles = `
|
|
|
53
53
|
font-size: 15px;
|
|
54
54
|
font-weight: 700;
|
|
55
55
|
line-height: 1.35;
|
|
56
|
+
/* Long unbroken tokens (URLs, filenames) wrap instead of overflowing. */
|
|
57
|
+
overflow-wrap: anywhere;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Optional action affordance (e.g. "Undo") before the dismiss button.
|
|
61
|
+
Hidden — taking no flex gap — until content is assigned. */
|
|
62
|
+
[part="action"] {
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
flex: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[part="action"]:not(.has-content) {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
[part="action"]::slotted(*) {
|
|
73
|
+
font: inherit;
|
|
74
|
+
font-size: 13px;
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
77
|
+
cursor: pointer;
|
|
56
78
|
}
|
|
57
79
|
|
|
58
80
|
[part="dismiss"] {
|
|
@@ -88,13 +110,22 @@ const toastStyles = `
|
|
|
88
110
|
`;
|
|
89
111
|
export class BoxToastElement extends BaseElement {
|
|
90
112
|
static get observedAttributes() {
|
|
91
|
-
return ["message", "open", "tone"];
|
|
113
|
+
return ["message", "open", "tone", "duration"];
|
|
92
114
|
}
|
|
93
115
|
openValue = false;
|
|
94
116
|
timeoutId = null;
|
|
95
117
|
toastEl;
|
|
96
118
|
messageEl;
|
|
119
|
+
actionSlot;
|
|
97
120
|
dismissEl;
|
|
121
|
+
/** Auto-dismiss delay in ms for the declarative `open` path. 0 = sticky. */
|
|
122
|
+
get duration() {
|
|
123
|
+
const raw = Number(this.getAttribute("duration"));
|
|
124
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
125
|
+
}
|
|
126
|
+
set duration(value) {
|
|
127
|
+
this.setAttribute("duration", String(value));
|
|
128
|
+
}
|
|
98
129
|
get open() {
|
|
99
130
|
return this.openValue;
|
|
100
131
|
}
|
|
@@ -110,11 +141,29 @@ export class BoxToastElement extends BaseElement {
|
|
|
110
141
|
else {
|
|
111
142
|
this.removeAttribute("open");
|
|
112
143
|
}
|
|
144
|
+
if (nextOpen) {
|
|
145
|
+
// Declarative open (attribute/property) auto-dismisses per `duration`;
|
|
146
|
+
// show() reschedules afterwards with its own option when provided.
|
|
147
|
+
this.scheduleAutoDismiss(this.duration);
|
|
148
|
+
}
|
|
149
|
+
else if (this.timeoutId) {
|
|
150
|
+
clearTimeout(this.timeoutId);
|
|
151
|
+
this.timeoutId = null;
|
|
152
|
+
}
|
|
113
153
|
this.dispatchEvent(new CustomEvent("open-changed", { bubbles: true, composed: true, detail: { open: nextOpen } }));
|
|
114
154
|
if (this.isRendered) {
|
|
115
155
|
this.update();
|
|
116
156
|
}
|
|
117
157
|
}
|
|
158
|
+
scheduleAutoDismiss(duration) {
|
|
159
|
+
if (this.timeoutId) {
|
|
160
|
+
clearTimeout(this.timeoutId);
|
|
161
|
+
this.timeoutId = null;
|
|
162
|
+
}
|
|
163
|
+
if (duration > 0) {
|
|
164
|
+
this.timeoutId = setTimeout(() => this.hide(), duration);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
118
167
|
get message() {
|
|
119
168
|
return this.getAttribute("message") ?? "";
|
|
120
169
|
}
|
|
@@ -147,16 +196,9 @@ export class BoxToastElement extends BaseElement {
|
|
|
147
196
|
this.tone = options.tone;
|
|
148
197
|
}
|
|
149
198
|
this.open = true;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const duration = options?.duration ?? 2500;
|
|
155
|
-
if (duration > 0) {
|
|
156
|
-
this.timeoutId = setTimeout(() => {
|
|
157
|
-
this.hide();
|
|
158
|
-
}, duration);
|
|
159
|
-
}
|
|
199
|
+
// options.duration wins; else a declarative `duration` attribute; else 2500.
|
|
200
|
+
const duration = options?.duration ?? (this.duration || 2500);
|
|
201
|
+
this.scheduleAutoDismiss(duration);
|
|
160
202
|
}
|
|
161
203
|
hide() {
|
|
162
204
|
this.open = false;
|
|
@@ -169,11 +211,13 @@ export class BoxToastElement extends BaseElement {
|
|
|
169
211
|
<style>${toastStyles}</style>
|
|
170
212
|
<div part="toast" role="status" aria-live="polite">
|
|
171
213
|
<span part="message"></span>
|
|
214
|
+
<slot name="action" part="action"></slot>
|
|
172
215
|
<button type="button" part="dismiss">Dismiss</button>
|
|
173
216
|
</div>
|
|
174
217
|
`;
|
|
175
218
|
this.toastEl = this.shadowRoot.querySelector('[part="toast"]');
|
|
176
219
|
this.messageEl = this.shadowRoot.querySelector('[part="message"]');
|
|
220
|
+
this.actionSlot = this.shadowRoot.querySelector('slot[name="action"]');
|
|
177
221
|
this.dismissEl = this.shadowRoot.querySelector('[part="dismiss"]');
|
|
178
222
|
}
|
|
179
223
|
setupListeners() {
|
|
@@ -181,6 +225,10 @@ export class BoxToastElement extends BaseElement {
|
|
|
181
225
|
this.dispatchEvent(new CustomEvent("dismiss", { bubbles: true, composed: true }));
|
|
182
226
|
this.hide();
|
|
183
227
|
});
|
|
228
|
+
this.actionSlot.addEventListener("slotchange", () => {
|
|
229
|
+
const hasContent = this.actionSlot.assignedNodes({ flatten: true }).length > 0;
|
|
230
|
+
this.actionSlot.classList.toggle("has-content", hasContent);
|
|
231
|
+
});
|
|
184
232
|
}
|
|
185
233
|
update() {
|
|
186
234
|
if (!this.toastEl) {
|
|
@@ -17,6 +17,12 @@ export declare class BoxCategorySelectorElement extends BaseElement {
|
|
|
17
17
|
private lastOptionsJson;
|
|
18
18
|
private hadFocus;
|
|
19
19
|
private rootEl;
|
|
20
|
+
private moreOpen;
|
|
21
|
+
private positionCleanup;
|
|
22
|
+
private readonly onDocumentPointerDown;
|
|
23
|
+
/** Max inline categories before the rest collapse into a "More" menu (0 = no limit). */
|
|
24
|
+
get maxLinks(): number;
|
|
25
|
+
set maxLinks(value: number);
|
|
20
26
|
get label(): string;
|
|
21
27
|
set label(value: string);
|
|
22
28
|
get options(): CategoryOption[];
|
|
@@ -25,9 +31,14 @@ export declare class BoxCategorySelectorElement extends BaseElement {
|
|
|
25
31
|
set value(nextValue: string);
|
|
26
32
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
27
33
|
private selectableOptions;
|
|
34
|
+
private splitOptions;
|
|
28
35
|
private select;
|
|
29
36
|
private moveSelection;
|
|
30
37
|
protected renderTemplate(): void;
|
|
38
|
+
private resolvedPlacement;
|
|
39
|
+
private openMore;
|
|
40
|
+
private closeMore;
|
|
41
|
+
disconnectedCallback(): void;
|
|
31
42
|
protected setupListeners(): void;
|
|
32
43
|
protected update(): void;
|
|
33
44
|
}
|