luxen-ui 0.18.1 → 0.18.3
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/cdn/custom-elements.json +191 -176
- package/cdn/elements/dropdown/dropdown.d.ts +16 -1
- package/cdn/elements/dropdown/dropdown.d.ts.map +1 -1
- package/cdn/elements/dropdown/dropdown.js +1 -1
- package/cdn/elements/dropdown/dropdown.js.map +1 -1
- package/cdn/elements/tabs/tabs.d.ts +25 -1
- package/cdn/elements/tabs/tabs.d.ts.map +1 -1
- package/cdn/elements/tabs/tabs.js +1 -1
- package/cdn/elements/tabs/tabs.js.map +1 -1
- package/cdn/standalone.css +56 -2
- package/cdn/styles/elements/tabs/enclosed.css +9 -1
- package/cdn/styles/elements/tabs/line.css +47 -1
- package/dist/css/elements/tabs/enclosed.css +9 -1
- package/dist/css/elements/tabs/line.css +47 -1
- package/dist/custom-elements.json +191 -176
- package/dist/elements/dropdown/dropdown.d.ts +16 -1
- package/dist/elements/dropdown/dropdown.d.ts.map +1 -1
- package/dist/elements/dropdown/dropdown.js +46 -4
- package/dist/elements/tabs/tabs.d.ts +25 -1
- package/dist/elements/tabs/tabs.d.ts.map +1 -1
- package/dist/elements/tabs/tabs.js +118 -26
- package/dist/metadata/index.json +17 -2
- package/dist/metadata/tabs.json +16 -1
- package/dist/templates/elements/tabs.md +41 -8
- package/package.json +1 -1
|
@@ -33,12 +33,22 @@ export class TabsChangeEvent extends Event {
|
|
|
33
33
|
* </l-tabs>
|
|
34
34
|
* ```
|
|
35
35
|
*
|
|
36
|
-
* @
|
|
36
|
+
* @example
|
|
37
|
+
* A tab button marked `disabled` (or `aria-disabled="true"`) is skipped by
|
|
38
|
+
* arrow keys, `Home` and `End`, and cannot be selected by a click or by
|
|
39
|
+
* `value` — it still owns its panel, so the tab/panel order is unchanged.
|
|
40
|
+
* ```html
|
|
41
|
+
* <button disabled>Billing</button>
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @event change - Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.
|
|
37
45
|
*
|
|
38
46
|
* @cssproperty [--indicator-color=var(--l-color-text-primary)] - `line` variant: color of the active underline that slides under the selected tab.
|
|
39
47
|
* @cssproperty [--indicator-thickness=2px] - `line` variant: thickness of the active underline.
|
|
40
48
|
* @cssproperty [--track-color=var(--l-color-border)] - `line` variant: color of the static bottom border the tabs sit on.
|
|
41
49
|
* @cssproperty [--track-thickness=1px] - `line` variant: thickness of the static bottom border.
|
|
50
|
+
* @cssproperty [--hover-color=var(--l-color-bg-state-hover)] - `line` variant: fill of the pill that appears behind a hovered tab label.
|
|
51
|
+
* @cssproperty [--hover-inset=4px] - `line` variant: block-axis gap between the hover pill and the tab edges, keeping it detached from the bottom border.
|
|
42
52
|
*
|
|
43
53
|
* @customElement l-tabs
|
|
44
54
|
*/
|
|
@@ -67,10 +77,13 @@ export class Tabs extends LuxenElement {
|
|
|
67
77
|
if (!tab)
|
|
68
78
|
return;
|
|
69
79
|
const index = this._tabs.indexOf(tab);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
// A native `disabled` button dispatches no click at all, but an
|
|
81
|
+
// `aria-disabled` one does — `_selectTab` refuses it, and focus must not
|
|
82
|
+
// follow either.
|
|
83
|
+
if (index < 0 || this._isDisabled(tab))
|
|
84
|
+
return;
|
|
85
|
+
this._selectTab(index);
|
|
86
|
+
tab.focus();
|
|
74
87
|
};
|
|
75
88
|
this._onKeyDown = (e) => {
|
|
76
89
|
const target = e.target;
|
|
@@ -79,31 +92,35 @@ export class Tabs extends LuxenElement {
|
|
|
79
92
|
const isHorizontal = this.orientation === 'horizontal';
|
|
80
93
|
const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';
|
|
81
94
|
const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';
|
|
82
|
-
|
|
95
|
+
const current = this._tabs.indexOf(target);
|
|
96
|
+
if (current < 0)
|
|
97
|
+
return;
|
|
98
|
+
// Every move lands on an enabled tab: selection follows focus here
|
|
99
|
+
// (automatic activation), so a disabled target would split the two apart.
|
|
100
|
+
let index;
|
|
83
101
|
switch (e.key) {
|
|
84
102
|
case nextKey:
|
|
85
|
-
|
|
86
|
-
index = (index + 1) % this._tabs.length;
|
|
87
|
-
this._selectTab(index);
|
|
88
|
-
this._tabs[index].focus();
|
|
103
|
+
index = this._nextEnabled(current, 1);
|
|
89
104
|
break;
|
|
90
105
|
case prevKey:
|
|
91
|
-
|
|
92
|
-
index = (index - 1 + this._tabs.length) % this._tabs.length;
|
|
93
|
-
this._selectTab(index);
|
|
94
|
-
this._tabs[index].focus();
|
|
106
|
+
index = this._nextEnabled(current, -1);
|
|
95
107
|
break;
|
|
96
108
|
case 'Home':
|
|
97
|
-
|
|
98
|
-
this._selectTab(0);
|
|
99
|
-
this._tabs[0].focus();
|
|
109
|
+
index = this._edgeEnabled(1);
|
|
100
110
|
break;
|
|
101
111
|
case 'End':
|
|
102
|
-
|
|
103
|
-
this._selectTab(this._tabs.length - 1);
|
|
104
|
-
this._tabs[this._tabs.length - 1].focus();
|
|
112
|
+
index = this._edgeEnabled(-1);
|
|
105
113
|
break;
|
|
114
|
+
default:
|
|
115
|
+
return;
|
|
106
116
|
}
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
// Only reachable when every tab is disabled — then there is nowhere to go
|
|
119
|
+
// and focus must not be dragged onto a disabled tab.
|
|
120
|
+
if (!this._isSelectable(index))
|
|
121
|
+
return;
|
|
122
|
+
this._selectTab(index);
|
|
123
|
+
this._tabs[index].focus();
|
|
107
124
|
};
|
|
108
125
|
}
|
|
109
126
|
createRenderRoot() {
|
|
@@ -135,7 +152,20 @@ export class Tabs extends LuxenElement {
|
|
|
135
152
|
}
|
|
136
153
|
updated(changed) {
|
|
137
154
|
if (changed.has('value') && this._tabs.length) {
|
|
138
|
-
|
|
155
|
+
const requested = Number(this.value);
|
|
156
|
+
if (this._isSelectable(requested)) {
|
|
157
|
+
this._selectTab(requested, false);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// The target is disabled or out of range, so the selection stays put —
|
|
161
|
+
// and `value` must not keep claiming a tab that was never selected.
|
|
162
|
+
// Reverting here would schedule an update from within an update, so
|
|
163
|
+
// defer it (same reason as a vetoed dialog `show`).
|
|
164
|
+
const current = this._activeIndex();
|
|
165
|
+
queueMicrotask(() => {
|
|
166
|
+
this.value = String(current);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
139
169
|
}
|
|
140
170
|
if (changed.has('orientation') && this._tablistEl) {
|
|
141
171
|
this._tablistEl.setAttribute('aria-orientation', this.orientation);
|
|
@@ -153,9 +183,11 @@ export class Tabs extends LuxenElement {
|
|
|
153
183
|
this._tablistEl.setAttribute('aria-orientation', this.orientation);
|
|
154
184
|
// Buttons inside tablist become tabs
|
|
155
185
|
this._tabs = Array.from(this._tablistEl.querySelectorAll('button'));
|
|
156
|
-
// Remaining children become panels
|
|
186
|
+
// Remaining children become panels. Disabled buttons stay in `_tabs` so the
|
|
187
|
+
// tab↔panel pairing keeps following DOM order — they are filtered out at
|
|
188
|
+
// navigation time, not here.
|
|
157
189
|
this._panels = children.slice(1);
|
|
158
|
-
const activeIndex =
|
|
190
|
+
const activeIndex = this._resolveActiveIndex();
|
|
159
191
|
// Enhance tabs
|
|
160
192
|
for (let i = 0; i < this._tabs.length; i++) {
|
|
161
193
|
const tab = this._tabs[i];
|
|
@@ -181,6 +213,10 @@ export class Tabs extends LuxenElement {
|
|
|
181
213
|
}
|
|
182
214
|
}
|
|
183
215
|
}
|
|
216
|
+
// `value` may have pointed at a disabled tab: canonicalize it to the tab
|
|
217
|
+
// that is actually selected.
|
|
218
|
+
if (Number(this.value) !== activeIndex)
|
|
219
|
+
this.value = String(activeIndex);
|
|
184
220
|
// Attach listeners
|
|
185
221
|
this._tablistEl.addEventListener('click', this._onClick);
|
|
186
222
|
this._tablistEl.addEventListener('keydown', this._onKeyDown);
|
|
@@ -201,8 +237,15 @@ export class Tabs extends LuxenElement {
|
|
|
201
237
|
}
|
|
202
238
|
// --- Tab selection ---
|
|
203
239
|
_selectTab(index, emitEvent = true) {
|
|
204
|
-
|
|
240
|
+
// A disabled tab is never selectable: it cannot take focus, so selecting it
|
|
241
|
+
// would move the roving tabindex entry point onto an unfocusable button and
|
|
242
|
+
// drop the whole tablist out of the tab sequence (WCAG 2.1.1).
|
|
243
|
+
if (!this._isSelectable(index))
|
|
205
244
|
return;
|
|
245
|
+
// Whether this is an actual change — re-selecting the current tab must not
|
|
246
|
+
// re-fire `change` (the event is documented as firing when the active tab
|
|
247
|
+
// changes, and this matches native `change` on radios / `<select>`).
|
|
248
|
+
const changed = this._tabs[index].getAttribute('aria-selected') !== 'true';
|
|
206
249
|
for (let i = 0; i < this._tabs.length; i++) {
|
|
207
250
|
const tab = this._tabs[i];
|
|
208
251
|
const panel = this._panels[i];
|
|
@@ -215,16 +258,65 @@ export class Tabs extends LuxenElement {
|
|
|
215
258
|
}
|
|
216
259
|
this.value = String(index);
|
|
217
260
|
this._updateIndicator();
|
|
218
|
-
if (emitEvent) {
|
|
261
|
+
if (emitEvent && changed) {
|
|
219
262
|
const name = this._tabs[index]?.getAttribute('name') ?? null;
|
|
220
263
|
this.dispatchEvent(new TabsChangeEvent(index, name));
|
|
221
264
|
}
|
|
222
265
|
}
|
|
266
|
+
// --- Disabled tabs ---
|
|
267
|
+
/**
|
|
268
|
+
* Both spellings count: native `disabled` (unfocusable) and `aria-disabled`
|
|
269
|
+
* (focusable, so a user can still land on it — navigation must move off it).
|
|
270
|
+
*/
|
|
271
|
+
_isDisabled(tab) {
|
|
272
|
+
return tab.disabled || tab.getAttribute('aria-disabled') === 'true';
|
|
273
|
+
}
|
|
274
|
+
_isSelectable(index) {
|
|
275
|
+
return (Number.isInteger(index) &&
|
|
276
|
+
index >= 0 &&
|
|
277
|
+
index < this._tabs.length &&
|
|
278
|
+
!this._isDisabled(this._tabs[index]));
|
|
279
|
+
}
|
|
280
|
+
/** Index of the currently selected tab, read back from the DOM. */
|
|
281
|
+
_activeIndex() {
|
|
282
|
+
const i = this._tabs.findIndex((t) => t.getAttribute('aria-selected') === 'true');
|
|
283
|
+
return i >= 0 ? i : 0;
|
|
284
|
+
}
|
|
285
|
+
/** Resolve the initial selection from `value`, falling back to the first enabled tab. */
|
|
286
|
+
_resolveActiveIndex() {
|
|
287
|
+
const requested = Number(this.value) || 0;
|
|
288
|
+
if (this._isSelectable(requested))
|
|
289
|
+
return requested;
|
|
290
|
+
const firstEnabled = this._tabs.findIndex((t) => !this._isDisabled(t));
|
|
291
|
+
// Every tab disabled: nothing is focusable anyway, so index 0 is as good as
|
|
292
|
+
// any and the tablist is simply unreachable — which is what was asked for.
|
|
293
|
+
return firstEnabled >= 0 ? firstEnabled : 0;
|
|
294
|
+
}
|
|
295
|
+
/** Index of the next enabled tab from `from`, moving by `dir`, wrapping. */
|
|
296
|
+
_nextEnabled(from, dir) {
|
|
297
|
+
const len = this._tabs.length;
|
|
298
|
+
for (let step = 1; step <= len; step++) {
|
|
299
|
+
const i = (from + dir * step + len * step) % len;
|
|
300
|
+
if (!this._isDisabled(this._tabs[i]))
|
|
301
|
+
return i;
|
|
302
|
+
}
|
|
303
|
+
return from;
|
|
304
|
+
}
|
|
305
|
+
/** Index of the first (`dir` 1) or last (`dir` -1) enabled tab. */
|
|
306
|
+
_edgeEnabled(dir) {
|
|
307
|
+
const len = this._tabs.length;
|
|
308
|
+
const start = dir === 1 ? 0 : len - 1;
|
|
309
|
+
for (let i = start; i >= 0 && i < len; i += dir) {
|
|
310
|
+
if (!this._isDisabled(this._tabs[i]))
|
|
311
|
+
return i;
|
|
312
|
+
}
|
|
313
|
+
return start;
|
|
314
|
+
}
|
|
223
315
|
// --- Indicator ---
|
|
224
316
|
_updateIndicator() {
|
|
225
317
|
if (!this._tablistEl)
|
|
226
318
|
return;
|
|
227
|
-
const activeTab = this._tabs[
|
|
319
|
+
const activeTab = this._tabs[this._activeIndex()];
|
|
228
320
|
if (!activeTab)
|
|
229
321
|
return;
|
|
230
322
|
const isVertical = this.orientation === 'vertical';
|
package/dist/metadata/index.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.18.
|
|
2
|
+
"version": "0.18.3",
|
|
3
3
|
"elements": [
|
|
4
4
|
{
|
|
5
5
|
"name": "alert",
|
|
@@ -5325,7 +5325,7 @@
|
|
|
5325
5325
|
"events": [
|
|
5326
5326
|
{
|
|
5327
5327
|
"name": "change",
|
|
5328
|
-
"description": "Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.",
|
|
5328
|
+
"description": "Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.",
|
|
5329
5329
|
"cancelable": false
|
|
5330
5330
|
}
|
|
5331
5331
|
],
|
|
@@ -5353,6 +5353,16 @@
|
|
|
5353
5353
|
"name": "--track-thickness",
|
|
5354
5354
|
"default": "1px",
|
|
5355
5355
|
"description": "`line` variant: thickness of the static bottom border."
|
|
5356
|
+
},
|
|
5357
|
+
{
|
|
5358
|
+
"name": "--hover-color",
|
|
5359
|
+
"default": "var(--l-color-bg-state-hover)",
|
|
5360
|
+
"description": "`line` variant: fill of the pill that appears behind a hovered tab label."
|
|
5361
|
+
},
|
|
5362
|
+
{
|
|
5363
|
+
"name": "--hover-inset",
|
|
5364
|
+
"default": "4px",
|
|
5365
|
+
"description": "`line` variant: block-axis gap between the hover pill and the tab edges, keeping it detached from the bottom border."
|
|
5356
5366
|
}
|
|
5357
5367
|
],
|
|
5358
5368
|
"commands": [],
|
|
@@ -5361,6 +5371,11 @@
|
|
|
5361
5371
|
"title": "Mark the initially-active tab with `aria-selected=\"true\"` so it is styled as",
|
|
5362
5372
|
"language": "html",
|
|
5363
5373
|
"code": "selected before the script upgrades the element (progressive enhancement).\n```html\n<l-tabs variant=\"enclosed\">\n <div>\n <button aria-selected=\"true\">Tab 1</button>\n <button>Tab 2</button>\n </div>\n <div>Content 1</div>\n <div>Content 2</div>\n</l-tabs>\n```"
|
|
5374
|
+
},
|
|
5375
|
+
{
|
|
5376
|
+
"title": "A tab button marked `disabled` (or `aria-disabled=\"true\"`) is skipped by",
|
|
5377
|
+
"language": "html",
|
|
5378
|
+
"code": "arrow keys, `Home` and `End`, and cannot be selected by a click or by\n`value` — it still owns its panel, so the tab/panel order is unchanged.\n```html\n<button disabled>Billing</button>\n```"
|
|
5364
5379
|
}
|
|
5365
5380
|
]
|
|
5366
5381
|
},
|
package/dist/metadata/tabs.json
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"events": [
|
|
56
56
|
{
|
|
57
57
|
"name": "change",
|
|
58
|
-
"description": "Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.",
|
|
58
|
+
"description": "Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.",
|
|
59
59
|
"cancelable": false
|
|
60
60
|
}
|
|
61
61
|
],
|
|
@@ -83,6 +83,16 @@
|
|
|
83
83
|
"name": "--track-thickness",
|
|
84
84
|
"default": "1px",
|
|
85
85
|
"description": "`line` variant: thickness of the static bottom border."
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "--hover-color",
|
|
89
|
+
"default": "var(--l-color-bg-state-hover)",
|
|
90
|
+
"description": "`line` variant: fill of the pill that appears behind a hovered tab label."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "--hover-inset",
|
|
94
|
+
"default": "4px",
|
|
95
|
+
"description": "`line` variant: block-axis gap between the hover pill and the tab edges, keeping it detached from the bottom border."
|
|
86
96
|
}
|
|
87
97
|
],
|
|
88
98
|
"commands": [],
|
|
@@ -91,6 +101,11 @@
|
|
|
91
101
|
"title": "Mark the initially-active tab with `aria-selected=\"true\"` so it is styled as",
|
|
92
102
|
"language": "html",
|
|
93
103
|
"code": "selected before the script upgrades the element (progressive enhancement).\n```html\n<l-tabs variant=\"enclosed\">\n <div>\n <button aria-selected=\"true\">Tab 1</button>\n <button>Tab 2</button>\n </div>\n <div>Content 1</div>\n <div>Content 2</div>\n</l-tabs>\n```"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"title": "A tab button marked `disabled` (or `aria-disabled=\"true\"`) is skipped by",
|
|
107
|
+
"language": "html",
|
|
108
|
+
"code": "arrow keys, `Home` and `End`, and cannot be selected by a click or by\n`value` — it still owns its panel, so the tab/panel order is unchanged.\n```html\n<button disabled>Billing</button>\n```"
|
|
94
109
|
}
|
|
95
110
|
]
|
|
96
111
|
}
|
|
@@ -54,7 +54,7 @@ Add `variant="line"` for a tablist with a sliding underline indicator.
|
|
|
54
54
|
</l-tabs>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Restyle the active underline and the static bottom border
|
|
57
|
+
Hovering a tab reveals a subtle pill behind its label, detached from the bottom border. Restyle it with `--hover-color` (and `--hover-inset` for the gap), along with the active underline and the static bottom border via `--indicator-color`, `--indicator-thickness`, `--track-color` and `--track-thickness`.
|
|
58
58
|
|
|
59
59
|
```html
|
|
60
60
|
<l-tabs
|
|
@@ -63,6 +63,7 @@ Restyle the active underline and the static bottom border with `--indicator-colo
|
|
|
63
63
|
--indicator-color: var(--l-color-bg-fill-info-strong);
|
|
64
64
|
--indicator-thickness: 3px;
|
|
65
65
|
--track-color: var(--l-color-divider);
|
|
66
|
+
--hover-color: var(--l-color-bg-fill-info-soft);
|
|
66
67
|
"
|
|
67
68
|
>
|
|
68
69
|
<div>
|
|
@@ -131,6 +132,35 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
131
132
|
</l-tabs>
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
### Disabled tab
|
|
136
|
+
|
|
137
|
+
Add `disabled` to a tab button to take it out of play. It is skipped by arrow keys, `Home` and `End`, it cannot be selected by a click or by `value`, and it never shows the hover pill. Use `aria-disabled="true"` instead if the tab should stay focusable so keyboard users can still discover it.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<l-tabs variant="line">
|
|
141
|
+
<div>
|
|
142
|
+
<button
|
|
143
|
+
name="account"
|
|
144
|
+
aria-selected="true"
|
|
145
|
+
>
|
|
146
|
+
Account
|
|
147
|
+
</button>
|
|
148
|
+
<button name="password">Password</button>
|
|
149
|
+
<button
|
|
150
|
+
name="billing"
|
|
151
|
+
disabled
|
|
152
|
+
>
|
|
153
|
+
Billing
|
|
154
|
+
</button>
|
|
155
|
+
<button name="notifications">Notifications</button>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
|
|
158
|
+
<div class="p-4 text-sm text-secondary">Change your password here.</div>
|
|
159
|
+
<div class="p-4 text-sm text-secondary">Upgrade your plan to manage billing.</div>
|
|
160
|
+
<div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
|
|
161
|
+
</l-tabs>
|
|
162
|
+
```
|
|
163
|
+
|
|
134
164
|
## Accessibility
|
|
135
165
|
|
|
136
166
|
### Criteria
|
|
@@ -139,6 +169,7 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
139
169
|
- **Linked controls** — Each tab has `aria-controls` pointing to its panel; each panel has `aria-labelledby` pointing back to its tab
|
|
140
170
|
- **Selection state** — Active tab has `aria-selected="true"`; inactive tabs have `aria-selected="false"`
|
|
141
171
|
- **Focus management** — Roving tabindex — active tab has `tabindex="0"`, others `tabindex="-1"`. Panels have `tabindex="0"` for keyboard access
|
|
172
|
+
- **Disabled tabs** — Tabs marked `disabled` or `aria-disabled="true"` are skipped by arrow keys, `Home` and `End`, and never take the roving `tabindex="0"` — so the tablist always keeps a reachable entry point
|
|
142
173
|
- **Hidden content** — Inactive panels use the `hidden` attribute
|
|
143
174
|
- **Motion** — Indicator animation respects `prefers-reduced-motion`
|
|
144
175
|
|
|
@@ -150,12 +181,12 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
150
181
|
|
|
151
182
|
### Keyboard interactions
|
|
152
183
|
|
|
153
|
-
- `ArrowRight` — Moves focus to the next tab and activates it (horizontal orientation)
|
|
154
|
-
- `ArrowLeft` — Moves focus to the previous tab and activates it (horizontal orientation)
|
|
155
|
-
- `ArrowDown` — Moves focus to the next tab and activates it (vertical orientation)
|
|
156
|
-
- `ArrowUp` — Moves focus to the previous tab and activates it (vertical orientation)
|
|
157
|
-
- `Home` — Moves focus to the first tab and activates it
|
|
158
|
-
- `End` — Moves focus to the last tab and activates it
|
|
184
|
+
- `ArrowRight` — Moves focus to the next enabled tab and activates it (horizontal orientation)
|
|
185
|
+
- `ArrowLeft` — Moves focus to the previous enabled tab and activates it (horizontal orientation)
|
|
186
|
+
- `ArrowDown` — Moves focus to the next enabled tab and activates it (vertical orientation)
|
|
187
|
+
- `ArrowUp` — Moves focus to the previous enabled tab and activates it (vertical orientation)
|
|
188
|
+
- `Home` — Moves focus to the first enabled tab and activates it
|
|
189
|
+
- `End` — Moves focus to the last enabled tab and activates it
|
|
159
190
|
- `Tab` — Moves focus out of the tablist to the active panel
|
|
160
191
|
|
|
161
192
|
## API reference
|
|
@@ -183,7 +214,7 @@ import 'luxen-ui/tabs';
|
|
|
183
214
|
|
|
184
215
|
### Events
|
|
185
216
|
|
|
186
|
-
- **change** — Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.
|
|
217
|
+
- **change** — Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.
|
|
187
218
|
|
|
188
219
|
### CSS custom properties
|
|
189
220
|
|
|
@@ -191,3 +222,5 @@ import 'luxen-ui/tabs';
|
|
|
191
222
|
- `--indicator-thickness` (default: `2px`) — `line` variant: thickness of the active underline.
|
|
192
223
|
- `--track-color` (default: `var(--l-color-border)`) — `line` variant: color of the static bottom border the tabs sit on.
|
|
193
224
|
- `--track-thickness` (default: `1px`) — `line` variant: thickness of the static bottom border.
|
|
225
|
+
- `--hover-color` (default: `var(--l-color-bg-state-hover)`) — `line` variant: fill of the pill that appears behind a hovered tab label.
|
|
226
|
+
- `--hover-inset` (default: `4px`) — `line` variant: block-axis gap between the hover pill and the tab edges, keeping it detached from the bottom border.
|
package/package.json
CHANGED