scb-wc-test 0.1.114 → 0.1.115

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.
@@ -1,142 +1,226 @@
1
- import { css as p, LitElement as h, html as l } from "lit";
2
- import { property as d, customElement as b } from "lit/decorators.js";
3
- import "../scb-icon-button/scb-icon-button.js";
1
+ import { css as m, LitElement as h, html as u } from "lit";
2
+ import { property as p, customElement as f } from "lit/decorators.js";
4
3
  import "../scb-divider/scb-divider.js";
5
4
  import "@material/web/focus/md-focus-ring.js";
6
- var m = Object.defineProperty, u = Object.getOwnPropertyDescriptor, c = (e, s, o, t) => {
7
- for (var i = t > 1 ? void 0 : t ? u(s, o) : s, a = e.length - 1, n; a >= 0; a--)
8
- (n = e[a]) && (i = (t ? n(s, o, i) : n(i)) || i);
9
- return t && i && m(s, o, i), i;
5
+ import "@material/web/icon/icon.js";
6
+ import "@material/web/ripple/ripple.js";
7
+ var v = Object.defineProperty, g = Object.getOwnPropertyDescriptor, c = (t, e, i, s) => {
8
+ for (var o = s > 1 ? void 0 : s ? g(e, i) : e, d = t.length - 1, l; d >= 0; d--)
9
+ (l = t[d]) && (o = (s ? l(e, i, o) : l(o)) || o);
10
+ return s && o && v(e, i, o), o;
10
11
  };
11
- let r = class extends h {
12
+ let a = class extends h {
12
13
  constructor() {
13
- super(...arguments), this.expanded = !1, this.label = "", this.supportingText = "", this.itemHref = "#", this.divider = !0, this._unique = crypto.randomUUID(), this._slotHasContent = !1, this._onSlotChange = () => {
14
- const e = this.renderRoot.querySelector("slot");
15
- this._slotHasContent = !!e && e.assignedNodes().length > 0, this.requestUpdate(), this._updateTabbable();
16
- }, this.toggleAccordion = () => {
17
- const e = this.closest("scb-toc");
18
- e != null && e.hasAttribute("detached") || e.querySelectorAll("scb-toc-item").forEach((t) => {
19
- t !== this && t.expanded && (t.expanded = !1, this.toggleBottom(t), t.dispatchEvent(
20
- new CustomEvent("expanded-changed", {
21
- detail: { expanded: !1 },
22
- bubbles: !0,
23
- composed: !0
24
- })
25
- ));
26
- });
27
- const s = this.expanded;
28
- this.expanded = !this.expanded, this.toggleBottom(this), s !== this.expanded && this.dispatchEvent(
29
- new CustomEvent("expanded-changed", {
30
- detail: { expanded: this.expanded },
31
- bubbles: !0,
32
- composed: !0
33
- })
34
- );
14
+ super(...arguments), this.expanded = !1, this.label = "", this.supportingText = "", this.itemHref = "#", this.divider = !0, this._unique = a._uid(), this._overflowVisibleTimer = null, this._slotHasContent = !1, this._onSlotChange = () => {
15
+ const t = this.renderRoot.querySelector("slot"), e = !!t && t.assignedElements({ flatten: !0 }).length > 0;
16
+ this._slotHasContent !== e && (this._slotHasContent = e, this.requestUpdate()), !this._slotHasContent && this.expanded && (this.expanded = !1), this._applyInertByExpanded();
17
+ }, this._toggleAccordion = () => {
18
+ if (!this._slotHasContent) return;
19
+ const t = !this.expanded;
20
+ t && this._collapseSiblingsIfNeeded(), this.expanded = t;
21
+ }, this._onKeyDown = (t) => {
22
+ var r;
23
+ const e = this.closest("scb-toc"), s = Array.from((e == null ? void 0 : e.querySelectorAll("scb-toc-item")) || []).map((n) => ({
24
+ host: n,
25
+ label: n.renderRoot.querySelector(".toc-item-label") || null
26
+ })).filter((n) => !!n.label), o = s.findIndex((n) => n.host === this);
27
+ if (o < 0) return;
28
+ const d = t.currentTarget, l = ((r = d == null ? void 0 : d.classList) == null ? void 0 : r.contains("toc-chevron-button")) ?? !1;
29
+ switch (t.key) {
30
+ case "Enter":
31
+ case " ":
32
+ if (!l) return;
33
+ t.preventDefault(), this._toggleAccordion();
34
+ break;
35
+ case "ArrowDown":
36
+ t.preventDefault(), o < s.length - 1 && s[o + 1].label.focus();
37
+ break;
38
+ case "ArrowUp":
39
+ t.preventDefault(), o > 0 && s[o - 1].label.focus();
40
+ break;
41
+ case "Home":
42
+ t.preventDefault(), s.length && s[0].label.focus();
43
+ break;
44
+ case "End":
45
+ t.preventDefault(), s.length && s[s.length - 1].label.focus();
46
+ break;
47
+ }
35
48
  };
36
49
  }
37
- connectedCallback() {
38
- super.connectedCallback(), this.addEventListener("slotchange", this._onSlotChange);
50
+ // Privat hjälpfunktion för unik ID-generering till aria-controls
51
+ static _uid() {
52
+ var t, e;
53
+ return ((e = (t = globalThis.crypto) == null ? void 0 : t.randomUUID) == null ? void 0 : e.call(t)) ?? `${a._uidPrefix}-${++a._uidSeq}`;
54
+ }
55
+ firstUpdated() {
56
+ this._onSlotChange(), this._applyInertByExpanded(), this._syncOverflowVisibilityByExpanded();
57
+ }
58
+ updated(t) {
59
+ t.has("expanded") && (this._applyInertByExpanded(), this._syncOverflowVisibilityByExpanded(), this.dispatchEvent(
60
+ new CustomEvent("expanded-changed", {
61
+ detail: { expanded: this.expanded, label: this.label },
62
+ bubbles: !0,
63
+ composed: !0
64
+ })
65
+ ));
39
66
  }
40
67
  disconnectedCallback() {
41
- super.disconnectedCallback(), this.removeEventListener("slotchange", this._onSlotChange);
68
+ super.disconnectedCallback(), this._overflowVisibleTimer !== null && (window.clearTimeout(this._overflowVisibleTimer), this._overflowVisibleTimer = null);
69
+ }
70
+ _applyInertByExpanded() {
71
+ const t = this.renderRoot.querySelector(".scb-toc-item-bottom");
72
+ t && (this.expanded ? (t.removeAttribute("inert"), t.setAttribute("aria-hidden", "false"), this._restoreTabIndexesIfNeeded()) : (t.setAttribute("inert", ""), t.setAttribute("aria-hidden", "true"), this._removeTabIndexesIfNeeded()));
73
+ }
74
+ _syncOverflowVisibilityByExpanded() {
75
+ if (this._overflowVisibleTimer !== null && (window.clearTimeout(this._overflowVisibleTimer), this._overflowVisibleTimer = null), this.removeAttribute("data-expanded-settled"), !this.expanded) return;
76
+ const t = this.renderRoot.querySelector(".scb-toc-item-bottom");
77
+ if (!t) {
78
+ this.setAttribute("data-expanded-settled", "");
79
+ return;
80
+ }
81
+ const e = this._getTransitionMs(t);
82
+ if (e <= 0) {
83
+ this.setAttribute("data-expanded-settled", "");
84
+ return;
85
+ }
86
+ this._overflowVisibleTimer = window.setTimeout(() => {
87
+ this._overflowVisibleTimer = null, this.expanded && this.setAttribute("data-expanded-settled", "");
88
+ }, e);
89
+ }
90
+ _getTransitionMs(t) {
91
+ const e = getComputedStyle(t), i = e.transitionDuration.split(",").map((r) => r.trim()), s = e.transitionDelay.split(",").map((r) => r.trim()), o = (r) => {
92
+ const n = parseFloat(r);
93
+ return Number.isFinite(n) ? r.endsWith("ms") ? n : r.endsWith("s") ? n * 1e3 : n : 0;
94
+ }, d = Math.max(i.length, s.length);
95
+ let l = 0;
96
+ for (let r = 0; r < d; r += 1) {
97
+ const n = o(i[r] ?? i[i.length - 1] ?? "0s"), b = o(s[r] ?? s[s.length - 1] ?? "0s");
98
+ l = Math.max(l, n + b);
99
+ }
100
+ return Math.ceil(l);
42
101
  }
43
- updated(e) {
44
- e.has("expanded") && this._updateTabbable();
102
+ _supportsInert() {
103
+ return "inert" in HTMLElement.prototype;
45
104
  }
46
- _updateTabbable() {
47
- if (typeof window > "u") return;
48
- const e = this.renderRoot.querySelector("slot");
49
- if (!e) return;
50
- e.assignedElements({ flatten: !0 }).forEach((o) => {
51
- var t;
52
- o.matches("a,button,input,select,textarea,[tabindex]") && (this.expanded ? o.removeAttribute("tabindex") : o.setAttribute("tabindex", "-1")), (t = o.querySelectorAll) == null || t.call(o, "a,button,input,select,textarea,[tabindex]").forEach((i) => {
53
- const a = i;
54
- this.expanded ? a.removeAttribute("tabindex") : a.setAttribute("tabindex", "-1");
55
- });
105
+ _collectFocusableFromSlot() {
106
+ const t = this.renderRoot.querySelector("slot");
107
+ if (!t) return [];
108
+ const e = t.assignedElements({ flatten: !0 }), i = [], s = (o) => {
109
+ o instanceof HTMLElement && o.matches("a,button,input,select,textarea,[tabindex]") && i.push(o);
110
+ };
111
+ return e.forEach((o) => {
112
+ var d;
113
+ s(o), (d = o.querySelectorAll) == null || d.call(o, "a,button,input,select,textarea,[tabindex]").forEach((l) => s(l));
114
+ }), i;
115
+ }
116
+ _removeTabIndexesIfNeeded() {
117
+ if (this._supportsInert()) return;
118
+ this._collectFocusableFromSlot().forEach((e) => {
119
+ const i = e.getAttribute("tabindex");
120
+ i !== null && e.setAttribute("data-scb-prev-tabindex", i), e.setAttribute("tabindex", "-1");
56
121
  });
57
122
  }
58
- // Animerar max-height vid öppning/stängning för snygg transition
59
- toggleBottom(e) {
60
- const s = e.renderRoot.querySelector(".scb-toc-item-bottom");
61
- s && (e.expanded ? (s.style.maxHeight = `${s.scrollHeight}px`, setTimeout(() => s.style.maxHeight = "unset", 160)) : (s.style.maxHeight = `${s.scrollHeight}px`, requestAnimationFrame(() => s.style.maxHeight = "0")));
123
+ _restoreTabIndexesIfNeeded() {
124
+ if (this._supportsInert()) return;
125
+ this._collectFocusableFromSlot().forEach((e) => {
126
+ const i = e.getAttribute("data-scb-prev-tabindex");
127
+ i !== null ? (e.setAttribute("tabindex", i), e.removeAttribute("data-scb-prev-tabindex")) : e.removeAttribute("tabindex");
128
+ });
62
129
  }
63
- /*
64
- Tangentbordsnavigering:
65
- Enter/Space togglar,
66
- ArrowUp/Down flyttar fokus,
67
- Home/End hoppar till första/sista item.
68
- */
69
- _onKeyDown(e) {
70
- const s = this.closest("scb-toc"), t = Array.from((s == null ? void 0 : s.querySelectorAll("scb-toc-item")) || []).map((n) => n.renderRoot.querySelector(".scb-toc-item")).filter((n) => !!n), i = e.currentTarget, a = t.indexOf(i);
71
- switch (e.key) {
72
- case "Enter":
73
- case " ":
74
- e.preventDefault(), this.toggleAccordion();
75
- break;
76
- case "ArrowDown":
77
- e.preventDefault(), a < t.length - 1 && t[a + 1].focus();
78
- break;
79
- case "ArrowUp":
80
- e.preventDefault(), a > 0 && t[a - 1].focus();
81
- break;
82
- case "Home":
83
- e.preventDefault(), t.length && t[0].focus();
84
- break;
85
- case "End":
86
- e.preventDefault(), t.length && t[t.length - 1].focus();
87
- break;
88
- }
130
+ _collapseSiblingsIfNeeded() {
131
+ const t = this.closest("scb-toc");
132
+ if (!t || t.hasAttribute("detached")) return;
133
+ t.querySelectorAll("scb-toc-item").forEach((i) => {
134
+ i !== this && i.expanded && (i.expanded = !1);
135
+ });
89
136
  }
90
137
  render() {
91
- const e = `bottom-${this._unique}`, s = `toc-label-${this._unique}`;
92
- return l`
93
- <div
94
- class="scb-toc-item"
95
- role="listitem"
96
- aria-expanded=${this.expanded}
97
- aria-controls=${e}
98
- >
138
+ const t = `bottom-${this._unique}`, e = `toc-label-${this._unique}`, i = `toc-chevron-${this._unique}`;
139
+ return u`
140
+ <div class="scb-toc-item" role="listitem">
99
141
  <div class="scb-toc-item-top">
100
142
  <div>
101
143
  <div class="toc-item-label-wrapper">
102
- <a id=${s} class="toc-item-label" href=${this.itemHref}>${this.label}</a>
103
- <md-focus-ring for=${s} inward></md-focus-ring>
144
+ <a
145
+ id=${e}
146
+ class="toc-item-label"
147
+ href=${this.itemHref}
148
+ @keydown=${this._onKeyDown}
149
+ >
150
+ ${this.label}
151
+ </a>
152
+ <md-focus-ring for=${e} inward></md-focus-ring>
104
153
  </div>
105
154
  <div class="supporting-text">${this.supportingText}</div>
106
155
  </div>
107
- ${this._slotHasContent ? l`
108
- <scb-icon-button
109
- icon=${this.expanded ? "keyboard_arrow_up" : "keyboard_arrow_down"}
110
- ariaLabel=${this.expanded ? "Collapse section" : "Expand section"}
111
- @click=${this.toggleAccordion}
112
- @keydown=${this._onKeyDown}
113
- ></scb-icon-button>
156
+
157
+ ${this._slotHasContent ? u`
158
+ <div class="toc-chevron-button-wrapper">
159
+ <button
160
+ id=${i}
161
+ class="toc-chevron-button"
162
+ type="button"
163
+ aria-label=${this.expanded ? "Fäll ihop avsnitt" : "Fäll ut avsnitt"}
164
+ aria-expanded=${this.expanded ? "true" : "false"}
165
+ aria-controls=${t}
166
+ @click=${this._toggleAccordion}
167
+ @keydown=${this._onKeyDown}
168
+ >
169
+ <md-ripple></md-ripple>
170
+ <md-icon class="toc-chevron-icon" aria-hidden="true">expand_more</md-icon>
171
+ </button>
172
+ <md-focus-ring for=${i} inward></md-focus-ring>
173
+ </div>
114
174
  ` : ""}
115
175
  </div>
116
- <div
117
- id=${e}
118
- class="scb-toc-item-bottom ${this.expanded ? "expanded" : ""}"
119
- >
120
- <slot @slotchange=${this._onSlotChange}></slot>
176
+
177
+ <div id=${t} class="scb-toc-item-bottom" role="region" aria-labelledby=${e}>
178
+ <div class="bottom-inner">
179
+ <slot @slotchange=${this._onSlotChange}></slot>
180
+ </div>
121
181
  </div>
122
- ${this.divider ? l`<scb-divider></scb-divider>` : ""}
182
+
183
+ ${this.divider ? u`<scb-divider></scb-divider>` : ""}
123
184
  </div>
124
185
  `;
125
186
  }
126
187
  };
127
- r.styles = [
128
- p`
188
+ a._uidPrefix = `uid-${Math.random().toString(36).slice(2)}`;
189
+ a._uidSeq = 0;
190
+ a.styles = [
191
+ m`
192
+ :host {
193
+ display: block;
194
+ --scb-toc-transition-duration: var(--motion-duration-medium, 150ms);
195
+ --scb-toc-transition-easing: var(
196
+ --motion-easing-emphasized-accelerate,
197
+ var(--motion-easing-emphasized, cubic-bezier(.69,.16,.2,.98))
198
+ );
199
+ --scb-toc-panel-padding-closed: var(--spacing-0, 0px);
200
+ color: var(--md-sys-color-on-surface);
201
+ font-family: var(--brand-font, 'Inter', sans-serif);
202
+ }
203
+
204
+ :host([expanded]) {
205
+ --scb-toc-transition-easing: var(
206
+ --motion-easing-emphasized-decelerate,
207
+ var(--motion-easing-emphasized, cubic-bezier(.69,.16,.2,.98))
208
+ );
209
+ }
210
+
129
211
  .scb-toc-item-top {
130
- padding: var(--spacing-5) var(--spacing-3);
212
+ padding: var(--spacing-5, 16px) var(--spacing-3, 8px);
131
213
  display: flex;
132
- gap: var(--spacing-5);
214
+ gap: var(--spacing-5, 16px);
133
215
  flex-direction: row;
134
216
  }
217
+
135
218
  .toc-item-label-wrapper {
136
219
  position: relative;
137
220
  display: block;
138
221
  width: fit-content;
139
222
  }
223
+
140
224
  .toc-item-label {
141
225
  display: block;
142
226
  width: fit-content;
@@ -147,71 +231,155 @@ r.styles = [
147
231
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
148
232
  text-decoration: underline;
149
233
  text-decoration-thickness: 1px;
150
- text-underline-offset: .1578em;
151
- margin-bottom: var(--spacing-3);
234
+ text-underline-offset: 0.1578em;
235
+ margin-bottom: var(--spacing-3, 8px);
152
236
  }
237
+
153
238
  .toc-item-label:hover {
154
239
  color: var(--md-sys-color-on-surface);
155
240
  text-decoration-thickness: 2px;
156
241
  }
242
+
157
243
  .toc-item-label:focus {
158
244
  color: var(--md-sys-color-on-surface);
159
245
  outline: none;
160
-
161
246
  }
247
+
162
248
  .toc-item-label-wrapper md-focus-ring {
163
249
  position: absolute;
164
250
  pointer-events: none;
165
- border-radius: var(--md-sys-shape-corner-small);
251
+ border-radius: var(--md-sys-shape-corner-small, 8px);
166
252
  --md-focus-ring-inward-offset: -7px;
167
253
  }
168
- scb-icon-button {
254
+
255
+ .supporting-text {
256
+ font-size: var(--md-sys-typescale-body-medium-size);
257
+ line-height: var(--md-sys-typescale-body-medium-line-height);
258
+ letter-spacing: var(--md-sys-typescale-body-medium-tracking);
259
+ color: var(--md-sys-color-on-surface-variant);
260
+ }
261
+
262
+ .toc-chevron-button-wrapper {
263
+ position: relative;
169
264
  margin-left: auto;
265
+ display: flex;
266
+ align-items: flex-start;
267
+ }
268
+
269
+ .toc-chevron-button {
270
+ position: relative;
271
+ display: inline-flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ inline-size: 40px;
275
+ block-size: 40px;
276
+ padding: 0;
277
+ border: 0;
278
+ background: transparent;
279
+ border-radius: 999px;
280
+ cursor: pointer;
281
+ color: inherit;
282
+ -webkit-tap-highlight-color: transparent;
283
+ }
284
+
285
+ .toc-chevron-button:focus {
286
+ outline: none;
287
+ }
288
+
289
+ .toc-chevron-button md-ripple {
290
+ border-radius: inherit;
170
291
  }
292
+
293
+ .toc-chevron-button-wrapper md-focus-ring {
294
+ position: absolute;
295
+ inset: 0;
296
+ pointer-events: none;
297
+ border-radius: 999px;
298
+ --md-focus-ring-inward-offset: -7px;
299
+ height: var(--icon-size-large, 40px);
300
+ }
301
+
302
+ .toc-chevron-icon {
303
+ transform: rotate(0deg);
304
+ transition: transform var(--motion-duration-medium, var(--scb-toc-transition-duration))
305
+ var(--motion-easing-standard, var(--scb-toc-transition-easing));
306
+ }
307
+
308
+ :host([expanded]) .toc-chevron-icon {
309
+ transform: rotate(180deg);
310
+ }
311
+
171
312
  scb-divider {
172
- margin: var(--spacing-0) var(--spacing-3);
313
+ margin: var(--spacing-0, 0px) var(--spacing-3, 8px);
173
314
  }
315
+
316
+ /* Animerar panelen utan JS genom grid-template-rows 0fr till 1fr */
174
317
  .scb-toc-item-bottom {
318
+ display: grid;
319
+ grid-template-rows: 0fr;
320
+ transition:
321
+ grid-template-rows var(--scb-toc-transition-duration) var(--scb-toc-transition-easing),
322
+ opacity var(--scb-toc-transition-duration) var(--scb-toc-transition-easing),
323
+ padding var(--scb-toc-transition-duration) var(--scb-toc-transition-easing);
324
+ opacity: 0;
325
+ overflow: hidden;
326
+ padding-top: var(--spacing-0, 0px);
327
+ padding-right: var(--spacing-5, 16px);
328
+ padding-bottom: var(--scb-toc-panel-padding-closed);
329
+ padding-left: var(--spacing-9, 32px);
175
330
  font-size: var(--md-sys-typescale-body-medium-size);
176
331
  line-height: var(--md-sys-typescale-body-medium-line-height); /* 150% */
177
332
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
178
333
  }
179
- .scb-toc-item-bottom {
180
- padding: var(--spacing-0) var(--spacing-5) var(--spacing-0) var(--spacing-9);
181
- max-height: var(--spacing-0);
334
+
335
+ :host([expanded]) .scb-toc-item-bottom {
336
+ grid-template-rows: 1fr;
337
+ opacity: 1;
338
+ padding-bottom: var(--spacing-5, 16px);
339
+ }
340
+
341
+ .bottom-inner {
182
342
  overflow: hidden;
183
- transition: .15s cubic-bezier(.69,.16,.2,.98);
184
- opacity: var(--spacing-0);
185
343
  }
186
- .scb-toc-item-bottom.expanded {
187
- opacity: 1;
188
- padding: var(--spacing-0) var(--spacing-5) var(--spacing-5) var(--spacing-9);
189
- max-height: 100%;
344
+
345
+ :host([expanded][data-expanded-settled]) .scb-toc-item-bottom {
346
+ overflow: visible;
190
347
  }
191
- .scb-toc-item {
192
- color: var(--md-sys-color-on-surface);
193
- font-family: var(--brand-font, 'Inter', sans-serif);
348
+
349
+ :host([expanded][data-expanded-settled]) .bottom-inner {
350
+ overflow: visible;
351
+ }
352
+
353
+ @media (prefers-reduced-motion: reduce) {
354
+ .scb-toc-item-bottom,
355
+ .toc-chevron-icon {
356
+ transition: none;
357
+ }
358
+
359
+ md-ripple {
360
+ display: none;
361
+ }
194
362
  }
195
363
  `
196
364
  ];
197
365
  c([
198
- d({ type: Boolean })
199
- ], r.prototype, "expanded", 2);
366
+ p({ type: Boolean, reflect: !0 })
367
+ ], a.prototype, "expanded", 2);
200
368
  c([
201
- d({ type: String, reflect: !0 })
202
- ], r.prototype, "label", 2);
369
+ p({ type: String, reflect: !0 })
370
+ ], a.prototype, "label", 2);
203
371
  c([
204
- d({ type: String, attribute: "supporting-text" })
205
- ], r.prototype, "supportingText", 2);
372
+ p({ type: String, attribute: "supporting-text" })
373
+ ], a.prototype, "supportingText", 2);
206
374
  c([
207
- d({ type: String, attribute: "item-href" })
208
- ], r.prototype, "itemHref", 2);
375
+ p({ type: String, attribute: "item-href" })
376
+ ], a.prototype, "itemHref", 2);
209
377
  c([
210
- d({ type: Boolean, attribute: "divider" })
211
- ], r.prototype, "divider", 2);
212
- r = c([
213
- b("scb-toc-item")
214
- ], r);
378
+ p({ type: Boolean, attribute: "divider" })
379
+ ], a.prototype, "divider", 2);
380
+ a = c([
381
+ f("scb-toc-item")
382
+ ], a);
215
383
  export {
216
- r as ScbTocItem
384
+ a as ScbTocItem
217
385
  };