@nectary/components 5.33.0 → 5.35.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/bundle.ts CHANGED
@@ -28,6 +28,9 @@ export * from './file-drop/index.js'
28
28
  export * from './file-picker/index.js'
29
29
  export * from './file-status/index.js'
30
30
  export * from './flag/index.js'
31
+ export * from './floating-panel/index.js'
32
+ export * from './floating-panel-button/index.js'
33
+ export * from './floating-panel-icon-button/index.js'
31
34
  export * from './grid-item/index.js'
32
35
  export * from './grid/index.js'
33
36
  export * from './help-tooltip/index.js'
@@ -14,7 +14,15 @@ class ButtonGroupItem extends NectaryElement {
14
14
  this.#$sinchButton = shadowRoot.querySelector("#sinch-button-element");
15
15
  }
16
16
  static get observedAttributes() {
17
- return ["type", "size", "text", "disabled", "toggled"];
17
+ return [
18
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: missing set type
19
+ "type",
20
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: missing set size
21
+ "size",
22
+ "text",
23
+ "disabled",
24
+ "toggled"
25
+ ];
18
26
  }
19
27
  attributeChangedCallback(name, oldVal, newVal) {
20
28
  updateAttribute(this.#$sinchButton, name, newVal);
package/card-v2/index.js CHANGED
@@ -92,7 +92,12 @@ class CardV2 extends NectaryElement {
92
92
  }
93
93
  }
94
94
  static get observedAttributes() {
95
- return ["clickable", "disabled", "selected"];
95
+ return [
96
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: clickable missing set/get pair
97
+ "clickable",
98
+ "disabled",
99
+ "selected"
100
+ ];
96
101
  }
97
102
  attributeChangedCallback(name, _oldVal, newVal) {
98
103
  switch (name) {
@@ -95,7 +95,9 @@ class DatePicker extends NectaryElement {
95
95
  "max",
96
96
  "locale",
97
97
  "range",
98
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: missing set prevYearAriaLabel
98
99
  "prev-year-aria-label",
100
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: missing set nextYearAriaLabel
99
101
  "next-year-aria-label",
100
102
  "prev-month-aria-label",
101
103
  "next-month-aria-label"
package/dialog/index.js CHANGED
@@ -68,6 +68,7 @@ class Dialog extends NectaryElement {
68
68
  return [
69
69
  "caption",
70
70
  "open",
71
+ // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: missing set closeAriaLabel
71
72
  "close-aria-label",
72
73
  "hide-close-button",
73
74
  "prevent-close"
@@ -0,0 +1 @@
1
+ export * from '../types';
@@ -0,0 +1,2 @@
1
+ import { defineCustomElement } from "../../utils/element.js";
2
+ defineCustomElement("sinch-floating-panel");
@@ -0,0 +1,41 @@
1
+ import { NectaryElement } from '../utils';
2
+ import '../action-menu';
3
+ import '../action-menu-option';
4
+ import '../floating-panel-icon-button';
5
+ import '../floating-panel-button';
6
+ import '../icon';
7
+ import '../popover';
8
+ export * from './types';
9
+ export declare class FloatingPanel extends NectaryElement {
10
+ #private;
11
+ constructor();
12
+ connectedCallback(): void;
13
+ disconnectedCallback(): void;
14
+ static get observedAttributes(): string[];
15
+ attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
16
+ get panelRect(): import("../types").TRect;
17
+ get closeButtonRect(): import("../types").TRect;
18
+ get ctaButtonRect(): import("../types").TRect;
19
+ set open(value: boolean);
20
+ get open(): boolean;
21
+ set noOfItemsSelected(value: string | null);
22
+ get noOfItemsSelected(): string | null;
23
+ set ctaLabel(value: string | null);
24
+ get ctaLabel(): string | null;
25
+ set itemsSelectedSingularLabel(value: string | null);
26
+ get itemsSelectedSingularLabel(): string | null;
27
+ set itemsSelectedPluralLabel(value: string | null);
28
+ get itemsSelectedPluralLabel(): string | null;
29
+ set itemsSelectedCompactLabel(value: string | null);
30
+ get itemsSelectedCompactLabel(): string | null;
31
+ set selectAllLabel(value: string | null);
32
+ get selectAllLabel(): string | null;
33
+ set selectAllShortLabel(value: string | null);
34
+ get selectAllShortLabel(): string | null;
35
+ set clearAllLabel(value: string | null);
36
+ get clearAllLabel(): string | null;
37
+ set allSelected(value: boolean);
38
+ get allSelected(): boolean;
39
+ set actions(value: string | null);
40
+ get actions(): string | null;
41
+ }
@@ -0,0 +1,502 @@
1
+ import { isSinchActionMenuOption } from "../action-menu-option/utils.js";
2
+ import { isAttrEqual, getBooleanAttribute, getAttribute, shouldReduceMotion, updateBooleanAttribute, updateAttribute } from "../utils/dom.js";
3
+ import { defineCustomElement, NectaryElement } from "../utils/element.js";
4
+ import { getRect } from "../utils/rect.js";
5
+ import { throttleAnimationFrame } from "../utils/throttle.js";
6
+ import { getReactEventHandler } from "../utils/get-react-event-handler.js";
7
+ import "../action-menu/index.js";
8
+ import "../action-menu-option/index.js";
9
+ import "../floating-panel-icon-button/index.js";
10
+ import "../floating-panel-button/index.js";
11
+ import "../icon/index.js";
12
+ import "../popover/index.js";
13
+ const templateHTML = '<style>:host{display:block;max-width:100%;min-width:0}@keyframes panel-slide-in{from{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes panel-slide-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(100%)}}dialog{display:flex;align-items:center;box-sizing:border-box;height:40px;padding:0;overflow:visible;width:fit-content;max-width:100%;min-width:0;background-color:var(--sinch-comp-floating-panel-color-background,var(--sinch-sys-color-primary-default));border:none;border-radius:var(--sinch-comp-floating-panel-shape-radius);box-shadow:var(--sinch-comp-floating-panel-shadow);color:var(--sinch-comp-floating-panel-color-text,var(--sinch-sys-color-primary-foreground))}dialog.closing,dialog.opening{overflow:hidden}dialog:not([open]){display:none}dialog.opening{animation:panel-slide-in .2s ease-out forwards}dialog.closing{animation:panel-slide-out .2s ease-in forwards}#items-selected{display:flex;align-items:baseline;gap:10px;padding:0 8px;white-space:nowrap}#cta{display:flex}#no-of-items-selected{font:var(--sinch-comp-floating-panel-font-bold)}#items-selected-label{font:var(--sinch-comp-floating-panel-font-regular)}#left-actions{display:flex;align-items:center}#divider{width:1px;align-self:stretch;background-color:var(--sinch-comp-floating-panel-color-divider)}#actions-end{display:flex;align-items:center;flex-shrink:1;min-width:0}#right-actions{display:flex;align-items:center}#right-actions-overflow{display:none;align-items:center;flex-shrink:0}:host([data-density=compact]) #right-actions,:host([data-density=extra-compact]) #right-actions{display:none}:host([data-density=compact]) #right-actions-overflow,:host([data-density=extra-compact]) #right-actions-overflow{display:flex}#actions-popover{display:block;--sinch-comp-pop-z-index:2147483000}#overflow-trigger{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}#overflow-menu-surface{box-sizing:border-box;display:block;min-width:var(--sinch-comp-floating-panel-overflow-menu-min-width,220px);width:max-content;max-width:min(var(--sinch-comp-floating-panel-overflow-menu-max-width,288px),calc(100vw - 24px))}#overflow-menu-surface sinch-action-menu{display:block;width:100%}#right-actions sinch-floating-panel-icon-button:last-child:active,#right-actions sinch-floating-panel-icon-button:last-child:focus,#right-actions sinch-floating-panel-icon-button:last-child:hover{--sinch-button-shape-radius-top-right:var(--sinch-comp-floating-panel-shape-radius);--sinch-button-shape-radius-bottom-right:var(--sinch-comp-floating-panel-shape-radius)}@media (prefers-reduced-motion){dialog.closing,dialog.opening{animation:none}}</style><dialog id="wrapper" role="toolbar" aria-label="Bulk actions"><div id="left-actions"><sinch-floating-panel-icon-button id="close-btn" icon="fa-xmark" aria-label="Close selection" class="edge-start"></sinch-floating-panel-icon-button></div><div id="divider" role="separator" aria-orientation="vertical"></div><div id="items-selected" aria-live="polite" aria-atomic="true"><span id="no-of-items-selected"></span> <span id="items-selected-label"></span></div><div id="cta"><sinch-floating-panel-button id="cta-btn" text=""></sinch-floating-panel-button></div><div id="actions-end"><div id="right-actions" role="group" aria-label="Actions"></div><div id="right-actions-overflow"><sinch-popover id="actions-popover" orientation="bottom-right" allow-scroll aria-label="More actions"><sinch-floating-panel-icon-button slot="target" id="overflow-trigger" icon="fa-ellipsis" aria-label="More actions" class="edge-end"></sinch-floating-panel-icon-button><div id="overflow-menu-surface" slot="content"><sinch-action-menu id="overflow-menu" aria-label="Actions" rows="5"></sinch-action-menu></div></sinch-popover></div></div></dialog>';
14
+ const template = document.createElement("template");
15
+ template.innerHTML = templateHTML;
16
+ const MAX_ACTIONS = 5;
17
+ const WIDTH_COMPACT = 400;
18
+ const WIDTH_EXTRA_COMPACT = 320;
19
+ const DEFAULT_ITEMS_SELECTED_SINGULAR_LABEL = "item selected";
20
+ const DEFAULT_ITEMS_SELECTED_PLURAL_LABEL = "items selected";
21
+ const DEFAULT_ITEMS_SELECTED_COMPACT_LABEL = "selected";
22
+ const DEFAULT_SELECT_ALL_LABEL = "Select all {count} items";
23
+ const DEFAULT_SELECT_ALL_SHORT_LABEL = "Select all";
24
+ const DEFAULT_CLEAR_ALL_LABEL = "Clear all";
25
+ const applyCountPlaceholder = (template2, count) => {
26
+ if (count === null || count === "") {
27
+ return template2.replace(/\{count\} ?/g, "").replace(/ {2,}/g, " ").trim();
28
+ }
29
+ return template2.replaceAll("{count}", count);
30
+ };
31
+ const sanitizeAttr = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
32
+ const isValidAction = (value) => {
33
+ if (typeof value !== "object" || value === null) {
34
+ return false;
35
+ }
36
+ const { name, icon, label } = value;
37
+ return typeof name === "string" && name !== "" && typeof icon === "string" && icon !== "" && (label === void 0 || typeof label === "string");
38
+ };
39
+ const DEFAULT_ACTIONS = [
40
+ { name: "export", icon: "fa-file-export", label: "Export" },
41
+ { name: "clone", icon: "fa-clone", label: "Duplicate" },
42
+ { name: "delete", icon: "fa-trash", label: "Delete" }
43
+ ];
44
+ class FloatingPanel extends NectaryElement {
45
+ #$dialog;
46
+ #$noOfItemsSelected;
47
+ #$itemsSelectedLabel;
48
+ #$rightActions;
49
+ #$rightActionsOverflow;
50
+ #$overflowMenu;
51
+ #$actionsPopover;
52
+ #$overflowTrigger;
53
+ #$closeBtn;
54
+ #$ctaBtn;
55
+ #controller = null;
56
+ #hideController = null;
57
+ #resizeObserver = null;
58
+ #resizeThrottle = null;
59
+ #overflowDismissController = null;
60
+ #density = "default";
61
+ /** After touch `pointerup` toggles the menu, ignore the synthetic `click` (WebKit). */
62
+ #suppressOverflowClickUntil = 0;
63
+ constructor() {
64
+ super();
65
+ const shadowRoot = this.attachShadow();
66
+ shadowRoot.appendChild(template.content.cloneNode(true));
67
+ this.#$dialog = shadowRoot.querySelector("#wrapper");
68
+ this.#$noOfItemsSelected = shadowRoot.querySelector("#no-of-items-selected");
69
+ this.#$itemsSelectedLabel = shadowRoot.querySelector("#items-selected-label");
70
+ this.#$rightActions = shadowRoot.querySelector("#right-actions");
71
+ this.#$rightActionsOverflow = shadowRoot.querySelector("#right-actions-overflow");
72
+ this.#$overflowMenu = shadowRoot.querySelector("#overflow-menu");
73
+ this.#$actionsPopover = shadowRoot.querySelector("#actions-popover");
74
+ this.#$overflowTrigger = shadowRoot.querySelector("#overflow-trigger");
75
+ this.#$closeBtn = shadowRoot.querySelector("#close-btn");
76
+ this.#$ctaBtn = shadowRoot.querySelector("#cta-btn");
77
+ }
78
+ connectedCallback() {
79
+ super.connectedCallback();
80
+ this.setAttribute("role", "region");
81
+ this.setAttribute("aria-label", "Bulk actions toolbar");
82
+ this.#controller = new AbortController();
83
+ const { signal } = this.#controller;
84
+ this.#$closeBtn.addEventListener("click", this.#onClose, { signal });
85
+ this.#$ctaBtn.addEventListener("click", this.#onSelectAll, { signal });
86
+ this.#$rightActions.addEventListener("click", this.#onActionClick, { signal });
87
+ this.#$overflowMenu.addEventListener("-click", this.#onOverflowMenuClick, { signal, capture: true });
88
+ this.shadowRoot.addEventListener("click", this.#onToolbarShadowClickCapture, { capture: true, signal });
89
+ this.shadowRoot.addEventListener("pointerup", this.#onOverflowTriggerPointerUp, { signal });
90
+ this.#$actionsPopover.addEventListener("-close", this.#onActionsPopoverClose, { signal });
91
+ this.#resizeThrottle = throttleAnimationFrame(() => {
92
+ if (!this.isDomConnected || !this.#$dialog.open) {
93
+ return;
94
+ }
95
+ const width = this.#$dialog.getBoundingClientRect().width;
96
+ this.#updateDensityFromWidth(width);
97
+ });
98
+ this.#resizeObserver = new ResizeObserver(() => {
99
+ this.#resizeThrottle?.fn();
100
+ });
101
+ this.#resizeObserver.observe(this.#$dialog);
102
+ this.addEventListener("-close", this.#onCloseReact, { signal });
103
+ this.addEventListener("-select-all", this.#onSelectAllReact, { signal });
104
+ this.addEventListener("-action", this.#onActionReact, { signal });
105
+ if (!this.hasAttribute("actions")) {
106
+ this.#renderActions(null);
107
+ }
108
+ if (this.hasAttribute("open")) {
109
+ this.#show();
110
+ }
111
+ this.#syncDensityAndLabel();
112
+ }
113
+ disconnectedCallback() {
114
+ super.disconnectedCallback();
115
+ this.#hideController?.abort();
116
+ this.#hideController = null;
117
+ this.#overflowDismissController?.abort();
118
+ this.#overflowDismissController = null;
119
+ this.#resizeThrottle?.cancel();
120
+ this.#resizeThrottle = null;
121
+ this.#resizeObserver?.disconnect();
122
+ this.#resizeObserver = null;
123
+ this.#controller.abort();
124
+ this.#controller = null;
125
+ }
126
+ static get observedAttributes() {
127
+ return [
128
+ "open",
129
+ "no-of-items-selected",
130
+ "cta-label",
131
+ "items-selected-singular-label",
132
+ "items-selected-plural-label",
133
+ "items-selected-compact-label",
134
+ "select-all-label",
135
+ "select-all-short-label",
136
+ "clear-all-label",
137
+ "actions",
138
+ "all-selected"
139
+ ];
140
+ }
141
+ attributeChangedCallback(name, oldVal, newVal) {
142
+ if (isAttrEqual(oldVal, newVal)) {
143
+ return;
144
+ }
145
+ switch (name) {
146
+ case "open": {
147
+ if (newVal !== null) {
148
+ this.#show();
149
+ } else {
150
+ this.#hide();
151
+ }
152
+ break;
153
+ }
154
+ case "no-of-items-selected": {
155
+ this.#$noOfItemsSelected.textContent = newVal;
156
+ this.#updateItemsSelectedLabel();
157
+ break;
158
+ }
159
+ case "cta-label":
160
+ case "select-all-label":
161
+ case "select-all-short-label":
162
+ case "clear-all-label": {
163
+ this.#updateCtaText(getBooleanAttribute(this, "all-selected"));
164
+ break;
165
+ }
166
+ case "items-selected-singular-label":
167
+ case "items-selected-plural-label":
168
+ case "items-selected-compact-label": {
169
+ this.#updateItemsSelectedLabel();
170
+ break;
171
+ }
172
+ case "actions": {
173
+ this.#renderActions(newVal);
174
+ break;
175
+ }
176
+ case "all-selected": {
177
+ this.#updateCtaText(newVal !== null);
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ #updateItemsSelectedLabel() {
183
+ if (this.#density === "extra-compact") {
184
+ this.#$itemsSelectedLabel.textContent = getAttribute(this, "items-selected-compact-label") ?? DEFAULT_ITEMS_SELECTED_COMPACT_LABEL;
185
+ return;
186
+ }
187
+ const raw = getAttribute(this, "no-of-items-selected");
188
+ const n = raw === null || raw === "" ? NaN : Number(raw);
189
+ const singular = Number.isFinite(n) && n === 1;
190
+ this.#$itemsSelectedLabel.textContent = singular ? getAttribute(this, "items-selected-singular-label") ?? DEFAULT_ITEMS_SELECTED_SINGULAR_LABEL : getAttribute(this, "items-selected-plural-label") ?? DEFAULT_ITEMS_SELECTED_PLURAL_LABEL;
191
+ }
192
+ #updateDensityFromWidth(width) {
193
+ if (!this.#$dialog.open || width <= 0) {
194
+ return;
195
+ }
196
+ let next;
197
+ if (width <= WIDTH_EXTRA_COMPACT) {
198
+ next = "extra-compact";
199
+ } else if (width <= WIDTH_COMPACT) {
200
+ next = "compact";
201
+ } else {
202
+ next = "default";
203
+ }
204
+ if (next === this.#density) {
205
+ return;
206
+ }
207
+ this.#density = next;
208
+ if (next === "default") {
209
+ this.removeAttribute("data-density");
210
+ this.#closeOverflowMenu();
211
+ } else {
212
+ this.setAttribute("data-density", next);
213
+ }
214
+ this.#syncDensityAndLabel();
215
+ }
216
+ #syncDensityAndLabel() {
217
+ this.#updateItemsSelectedLabel();
218
+ this.#updateCtaText(getBooleanAttribute(this, "all-selected"));
219
+ }
220
+ #show() {
221
+ this.#hideController?.abort();
222
+ this.#hideController = null;
223
+ this.#$dialog.show();
224
+ this.#$dialog.classList.remove("closing");
225
+ if (shouldReduceMotion()) {
226
+ queueMicrotask(() => {
227
+ const { width } = this.#$dialog.getBoundingClientRect();
228
+ this.#updateDensityFromWidth(width);
229
+ });
230
+ return;
231
+ }
232
+ this.#$dialog.classList.add("opening");
233
+ this.#$dialog.addEventListener("animationend", () => {
234
+ this.#$dialog.classList.remove("opening");
235
+ }, { once: true });
236
+ queueMicrotask(() => {
237
+ const { width } = this.#$dialog.getBoundingClientRect();
238
+ this.#updateDensityFromWidth(width);
239
+ });
240
+ }
241
+ #hide() {
242
+ this.#closeOverflowMenu();
243
+ this.#hideController?.abort();
244
+ if (shouldReduceMotion()) {
245
+ this.#$dialog.close();
246
+ return;
247
+ }
248
+ this.#hideController = new AbortController();
249
+ this.#$dialog.classList.remove("opening");
250
+ this.#$dialog.classList.add("closing");
251
+ this.#$dialog.addEventListener("animationend", () => {
252
+ this.#hideController = null;
253
+ this.#$dialog.classList.remove("closing");
254
+ this.#$dialog.close();
255
+ }, { once: true, signal: this.#hideController.signal });
256
+ }
257
+ #updateCtaText(allSelected) {
258
+ if (allSelected) {
259
+ this.#$ctaBtn.setAttribute("text", getAttribute(this, "clear-all-label") ?? DEFAULT_CLEAR_ALL_LABEL);
260
+ return;
261
+ }
262
+ if (this.#density !== "default") {
263
+ this.#$ctaBtn.setAttribute("text", getAttribute(this, "select-all-short-label") ?? DEFAULT_SELECT_ALL_SHORT_LABEL);
264
+ return;
265
+ }
266
+ const template2 = getAttribute(this, "select-all-label") ?? DEFAULT_SELECT_ALL_LABEL;
267
+ this.#$ctaBtn.setAttribute("text", applyCountPlaceholder(template2, getAttribute(this, "cta-label")));
268
+ }
269
+ #renderActions(json) {
270
+ this.#$rightActions.innerHTML = "";
271
+ this.#$overflowMenu.innerHTML = "";
272
+ let actions;
273
+ if (json === null) {
274
+ actions = DEFAULT_ACTIONS;
275
+ } else {
276
+ try {
277
+ const parsed = JSON.parse(json);
278
+ if (!Array.isArray(parsed)) {
279
+ return;
280
+ }
281
+ actions = parsed.filter(isValidAction);
282
+ } catch {
283
+ return;
284
+ }
285
+ }
286
+ const sliced = actions.slice(0, MAX_ACTIONS);
287
+ this.#$rightActions.innerHTML = sliced.map((action) => {
288
+ const safeName = sanitizeAttr(action.name);
289
+ const safeIcon = sanitizeAttr(action.icon);
290
+ const safeLabel = sanitizeAttr(action.label ?? action.name);
291
+ return `<sinch-floating-panel-icon-button data-action="${safeName}" icon="${safeIcon}" aria-label="${safeLabel}"></sinch-floating-panel-icon-button>`;
292
+ }).join("");
293
+ this.#$overflowMenu.innerHTML = sliced.map((action) => {
294
+ const safeName = sanitizeAttr(action.name);
295
+ const safeIcon = sanitizeAttr(action.icon);
296
+ const safeLabel = sanitizeAttr(action.label ?? action.name);
297
+ return `<sinch-action-menu-option data-action="${safeName}" text="${safeLabel}" aria-label="${safeLabel}"><sinch-icon icons-version="2" name="${safeIcon}" slot="icon"></sinch-icon></sinch-action-menu-option>`;
298
+ }).join("");
299
+ }
300
+ /**
301
+ * Fires the generic `-action` event (detail = action name) plus a per-action
302
+ * `-${action}` convenience event. The per-action event lets consumers bind
303
+ * directly to a specific action (e.g. `on-archive`) without switching on
304
+ * `e.detail` — and it works for any user-defined action, not just the
305
+ * built-in defaults. React handlers are forwarded by `#onActionReact`.
306
+ */
307
+ #dispatchAction(action) {
308
+ this.dispatchEvent(new CustomEvent("-action", { detail: action }));
309
+ this.dispatchEvent(new CustomEvent(`-${action}`));
310
+ }
311
+ #onClose = () => {
312
+ this.dispatchEvent(new CustomEvent("-close"));
313
+ };
314
+ #onSelectAll = () => {
315
+ this.dispatchEvent(new CustomEvent("-select-all"));
316
+ };
317
+ #onActionClick = (e) => {
318
+ const btn = e.target.closest("sinch-floating-panel-icon-button[data-action]");
319
+ if (btn === null) {
320
+ return;
321
+ }
322
+ const action = btn.dataset.action;
323
+ if (action === void 0) {
324
+ return;
325
+ }
326
+ this.#dispatchAction(action);
327
+ };
328
+ #onOverflowMenuClick = (e) => {
329
+ if (!isSinchActionMenuOption(e.target)) {
330
+ return;
331
+ }
332
+ const action = e.target.dataset.action;
333
+ if (action === void 0) {
334
+ return;
335
+ }
336
+ this.#closeOverflowMenu();
337
+ this.#dispatchAction(action);
338
+ };
339
+ /**
340
+ * Safari/WebKit often does not deliver a bubbling click from sinch-button (shadow) to the overflow host;
341
+ * capture on our shadow + composedPath() matches Chrome and Safari. Touch: pointerup (see below).
342
+ */
343
+ #onToolbarShadowClickCapture = (e) => {
344
+ if (this.#density === "default") {
345
+ return;
346
+ }
347
+ if (!(e instanceof MouseEvent)) {
348
+ return;
349
+ }
350
+ if (e.button !== 0) {
351
+ return;
352
+ }
353
+ if (!e.composedPath().includes(this.#$overflowTrigger)) {
354
+ return;
355
+ }
356
+ if (performance.now() < this.#suppressOverflowClickUntil) {
357
+ e.preventDefault();
358
+ e.stopPropagation();
359
+ return;
360
+ }
361
+ e.stopPropagation();
362
+ this.#toggleOverflowMenu();
363
+ };
364
+ #onOverflowTriggerPointerUp = (e) => {
365
+ if (this.#density === "default") {
366
+ return;
367
+ }
368
+ if (!(e instanceof PointerEvent)) {
369
+ return;
370
+ }
371
+ if (!e.isPrimary || e.pointerType === "mouse" && e.button !== 0) {
372
+ return;
373
+ }
374
+ if (e.pointerType === "mouse") {
375
+ return;
376
+ }
377
+ if (!e.composedPath().includes(this.#$overflowTrigger)) {
378
+ return;
379
+ }
380
+ e.preventDefault();
381
+ e.stopPropagation();
382
+ this.#suppressOverflowClickUntil = performance.now() + 400;
383
+ this.#toggleOverflowMenu();
384
+ };
385
+ #toggleOverflowMenu() {
386
+ if (this.#$actionsPopover.open) {
387
+ this.#closeOverflowMenu();
388
+ } else {
389
+ this.#$actionsPopover.open = true;
390
+ this.#overflowDismissController = new AbortController();
391
+ document.addEventListener("click", this.#onDocumentClickDismiss, {
392
+ signal: this.#overflowDismissController.signal,
393
+ capture: true
394
+ });
395
+ }
396
+ }
397
+ #closeOverflowMenu() {
398
+ this.#$actionsPopover.open = false;
399
+ this.#overflowDismissController?.abort();
400
+ this.#overflowDismissController = null;
401
+ }
402
+ #onDocumentClickDismiss = (e) => {
403
+ if (e.composedPath().includes(this.#$rightActionsOverflow)) {
404
+ return;
405
+ }
406
+ this.#closeOverflowMenu();
407
+ };
408
+ #onActionsPopoverClose = () => {
409
+ this.#closeOverflowMenu();
410
+ };
411
+ #onCloseReact = (e) => {
412
+ getReactEventHandler(this, "on-close")?.(e);
413
+ };
414
+ #onSelectAllReact = (e) => {
415
+ getReactEventHandler(this, "on-select-all")?.(e);
416
+ };
417
+ #onActionReact = (e) => {
418
+ getReactEventHandler(this, "on-action")?.(e);
419
+ if (e instanceof CustomEvent && typeof e.detail === "string") {
420
+ getReactEventHandler(this, `on-${e.detail}`)?.(e);
421
+ }
422
+ };
423
+ get panelRect() {
424
+ return getRect(this.#$dialog);
425
+ }
426
+ get closeButtonRect() {
427
+ return getRect(this.#$closeBtn);
428
+ }
429
+ get ctaButtonRect() {
430
+ return getRect(this.#$ctaBtn);
431
+ }
432
+ set open(value) {
433
+ updateBooleanAttribute(this, "open", value);
434
+ }
435
+ get open() {
436
+ return getBooleanAttribute(this, "open");
437
+ }
438
+ set noOfItemsSelected(value) {
439
+ updateAttribute(this, "no-of-items-selected", value);
440
+ }
441
+ get noOfItemsSelected() {
442
+ return getAttribute(this, "no-of-items-selected");
443
+ }
444
+ set ctaLabel(value) {
445
+ updateAttribute(this, "cta-label", value);
446
+ }
447
+ get ctaLabel() {
448
+ return getAttribute(this, "cta-label");
449
+ }
450
+ set itemsSelectedSingularLabel(value) {
451
+ updateAttribute(this, "items-selected-singular-label", value);
452
+ }
453
+ get itemsSelectedSingularLabel() {
454
+ return getAttribute(this, "items-selected-singular-label");
455
+ }
456
+ set itemsSelectedPluralLabel(value) {
457
+ updateAttribute(this, "items-selected-plural-label", value);
458
+ }
459
+ get itemsSelectedPluralLabel() {
460
+ return getAttribute(this, "items-selected-plural-label");
461
+ }
462
+ set itemsSelectedCompactLabel(value) {
463
+ updateAttribute(this, "items-selected-compact-label", value);
464
+ }
465
+ get itemsSelectedCompactLabel() {
466
+ return getAttribute(this, "items-selected-compact-label");
467
+ }
468
+ set selectAllLabel(value) {
469
+ updateAttribute(this, "select-all-label", value);
470
+ }
471
+ get selectAllLabel() {
472
+ return getAttribute(this, "select-all-label");
473
+ }
474
+ set selectAllShortLabel(value) {
475
+ updateAttribute(this, "select-all-short-label", value);
476
+ }
477
+ get selectAllShortLabel() {
478
+ return getAttribute(this, "select-all-short-label");
479
+ }
480
+ set clearAllLabel(value) {
481
+ updateAttribute(this, "clear-all-label", value);
482
+ }
483
+ get clearAllLabel() {
484
+ return getAttribute(this, "clear-all-label");
485
+ }
486
+ set allSelected(value) {
487
+ updateBooleanAttribute(this, "all-selected", value);
488
+ }
489
+ get allSelected() {
490
+ return getBooleanAttribute(this, "all-selected");
491
+ }
492
+ set actions(value) {
493
+ updateAttribute(this, "actions", value);
494
+ }
495
+ get actions() {
496
+ return getAttribute(this, "actions");
497
+ }
498
+ }
499
+ defineCustomElement("sinch-floating-panel", FloatingPanel);
500
+ export {
501
+ FloatingPanel
502
+ };