@waggylabs/yumekit 0.5.1 → 0.5.2
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/CHANGELOG.md +10 -0
- package/README.md +53 -62
- package/dist/ai/llm.txt +2 -1
- package/dist/ai/skill/reference.md +1 -0
- package/dist/components/y-code.js +9 -2
- package/dist/components/y-tabs/y-tabs.d.ts +17 -0
- package/dist/components/y-tabs.d.ts +17 -0
- package/dist/components/y-tabs.js +193 -8
- package/dist/index.js +202 -10
- package/dist/react.d.ts +1 -0
- package/dist/yumekit.min.js +1 -1
- package/llm.txt +2 -1
- package/package.json +1 -1
- package/scripts/install-ai-docs.js +93 -72
package/CHANGELOG.md
CHANGED
|
@@ -31,6 +31,16 @@ Delete any empty sections before publishing.
|
|
|
31
31
|
<!-- ### Security -->
|
|
32
32
|
<!-- Vulnerability patches or hardening changes -->
|
|
33
33
|
|
|
34
|
+
## [0.5.2] - 2026-06-27
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- `y-tabs` gains an `overflow` attribute: `scroll` (default) keeps tabs on a single line and shows prev/next arrow buttons when the strip overflows its container, while `wrap` lets tabs flow onto multiple rows.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- `y-code` now scrolls its content within a height-constrained container — set a CSS `height` or `max-height` and the code area scrolls, where previously vertical scrolling only kicked in with `max-lines`.
|
|
43
|
+
|
|
34
44
|
## [0.5.1] - 2026-06-20
|
|
35
45
|
|
|
36
46
|
### Added
|
package/README.md
CHANGED
|
@@ -73,68 +73,59 @@ Then use the `<y-theme>` component to apply a theme:
|
|
|
73
73
|
|
|
74
74
|
## Components
|
|
75
75
|
|
|
76
|
-
| Component
|
|
77
|
-
|
|
|
78
|
-
| Animate
|
|
79
|
-
| App Bar
|
|
80
|
-
| Avatar
|
|
81
|
-
| Avatar Group
|
|
82
|
-
| Badge
|
|
83
|
-
| Banner
|
|
84
|
-
| Break
|
|
85
|
-
| Breadcrumbs
|
|
86
|
-
| Button
|
|
87
|
-
| Button Group
|
|
88
|
-
| Card
|
|
89
|
-
| Checkbox
|
|
90
|
-
| Code
|
|
91
|
-
| Color
|
|
92
|
-
| Color Picker
|
|
93
|
-
| Data Grid
|
|
94
|
-
| Date
|
|
95
|
-
| DatePicker
|
|
96
|
-
| Dialog
|
|
97
|
-
| Dock
|
|
98
|
-
| Drawer
|
|
99
|
-
| Droplist
|
|
100
|
-
| Gallery
|
|
101
|
-
| Grid
|
|
102
|
-
| Help
|
|
103
|
-
| Icon
|
|
104
|
-
| Input
|
|
105
|
-
| Masonry
|
|
106
|
-
| Menu
|
|
107
|
-
| Paginator
|
|
108
|
-
| Panel Bar
|
|
109
|
-
| Popover
|
|
110
|
-
| Progress
|
|
111
|
-
| Radio
|
|
112
|
-
| Rating
|
|
113
|
-
| Select
|
|
114
|
-
| Shape
|
|
115
|
-
| Sidebar
|
|
116
|
-
| Slider
|
|
117
|
-
| Splitter
|
|
118
|
-
| Stack
|
|
119
|
-
| Stepper
|
|
120
|
-
| Switch
|
|
121
|
-
| Table
|
|
122
|
-
| Tabs
|
|
123
|
-
| Tag
|
|
124
|
-
| Textarea
|
|
125
|
-
| Theme
|
|
126
|
-
| Toast
|
|
127
|
-
| Tooltip
|
|
128
|
-
| Tree
|
|
129
|
-
|
|
130
|
-
### Sub-elements
|
|
131
|
-
|
|
132
|
-
These are building blocks used inside their parent component, not standalone components (and not included in the count above):
|
|
133
|
-
|
|
134
|
-
| Element | Used inside | Description |
|
|
135
|
-
| --------------- | -------------- | -------------------------------- |
|
|
136
|
-
| `<y-panel>` | `<y-panelbar>` | Individual accordion panel |
|
|
137
|
-
| `<y-tree-item>` | `<y-tree>` | Individual node within a tree |
|
|
76
|
+
| Component | Element | Description |
|
|
77
|
+
| ------------ | ------------------ | ------------------------------------------------------ |
|
|
78
|
+
| Animate | `<y-animate>` | Scroll/viewport-triggered animation wrapper |
|
|
79
|
+
| App Bar | `<y-appbar>` | Top or side navigation bar |
|
|
80
|
+
| Avatar | `<y-avatar>` | User avatar with shape and color variants |
|
|
81
|
+
| Avatar Group | `<y-avatar-group>` | Overlapping avatar group with overflow count |
|
|
82
|
+
| Badge | `<y-badge>` | Status badge or label |
|
|
83
|
+
| Banner | `<y-banner>` | Full-width inline message / alert banner |
|
|
84
|
+
| Break | `<y-break>` | Divider with optional centered label, icon, or slot |
|
|
85
|
+
| Breadcrumbs | `<y-breadcrumbs>` | Navigation breadcrumb trail with collapse support |
|
|
86
|
+
| Button | `<y-button>` | Button with icon, size, and style variants |
|
|
87
|
+
| Button Group | `<y-button-group>` | Groups buttons (or inputs) into a connected toolbar |
|
|
88
|
+
| Card | `<y-card>` | Content card container |
|
|
89
|
+
| Checkbox | `<y-checkbox>` | Form checkbox input |
|
|
90
|
+
| Code | `<y-code>` | Code block with built-in syntax highlighting |
|
|
91
|
+
| Color | `<y-color>` | Color swatch / value display |
|
|
92
|
+
| Color Picker | `<y-colorpicker>` | Interactive color picker |
|
|
93
|
+
| Data Grid | `<y-data-grid>` | Data grid with sorting, filtering, editing, pagination |
|
|
94
|
+
| Date | `<y-date>` | Date input |
|
|
95
|
+
| DatePicker | `<y-datepicker>` | A date and time picker |
|
|
96
|
+
| Dialog | `<y-dialog>` | Modal dialog |
|
|
97
|
+
| Dock | `<y-dock>` | Fixed navigation dock |
|
|
98
|
+
| Drawer | `<y-drawer>` | Side drawer / sidebar |
|
|
99
|
+
| Droplist | `<y-droplist>` | Drag-and-drop reorderable list |
|
|
100
|
+
| Gallery | `<y-gallery>` | Media gallery with lightbox |
|
|
101
|
+
| Grid | `<y-grid>` | CSS Grid layout container |
|
|
102
|
+
| Help | `<y-help>` | Guided product tour / onboarding walkthrough |
|
|
103
|
+
| Icon | `<y-icon>` | SVG icon display |
|
|
104
|
+
| Input | `<y-input>` | Text input field |
|
|
105
|
+
| Masonry | `<y-masonry>` | JS-positioned masonry layout |
|
|
106
|
+
| Menu | `<y-menu>` | Dropdown navigation menu |
|
|
107
|
+
| Paginator | `<y-paginator>` | Pagination controls |
|
|
108
|
+
| Panel Bar | `<y-panelbar>` | Accordion panel group |
|
|
109
|
+
| Popover | `<y-popover>` | Anchored floating popover |
|
|
110
|
+
| Progress | `<y-progress>` | Progress bar |
|
|
111
|
+
| Radio | `<y-radio>` | Radio button input |
|
|
112
|
+
| Rating | `<y-rating>` | Star / icon rating input |
|
|
113
|
+
| Select | `<y-select>` | Select / dropdown input |
|
|
114
|
+
| Shape | `<y-shape>` | Decorative CSS shape container |
|
|
115
|
+
| Sidebar | `<y-sidebar>` | Collapsible app sidebar navigation |
|
|
116
|
+
| Slider | `<y-slider>` | Range slider input |
|
|
117
|
+
| Splitter | `<y-splitter>` | Two-pane container with a draggable resize handle |
|
|
118
|
+
| Stack | `<y-stack>` | Flexbox layout container (row or column) |
|
|
119
|
+
| Stepper | `<y-stepper>` | Multi-step wizard with sequential flow |
|
|
120
|
+
| Switch | `<y-switch>` | Toggle switch |
|
|
121
|
+
| Table | `<y-table>` | Sortable data table |
|
|
122
|
+
| Tabs | `<y-tabs>` | Tabbed interface |
|
|
123
|
+
| Tag | `<y-tag>` | Tag / chip label |
|
|
124
|
+
| Textarea | `<y-textarea>` | Multi-line text input |
|
|
125
|
+
| Theme | `<y-theme>` | Theme provider |
|
|
126
|
+
| Toast | `<y-toast>` | Notification toast |
|
|
127
|
+
| Tooltip | `<y-tooltip>` | Tooltip / popover |
|
|
128
|
+
| Tree | `<y-tree>` | Hierarchical tree view |
|
|
138
129
|
|
|
139
130
|
---
|
|
140
131
|
|
package/dist/ai/llm.txt
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A modern, framework-agnostic Web Components UI kit providing 51 production-ready custom HTML elements. Built entirely on native web standards with zero external runtime dependencies.
|
|
4
4
|
|
|
5
5
|
## Version
|
|
6
|
-
0.5.
|
|
6
|
+
0.5.2
|
|
7
7
|
|
|
8
8
|
## License
|
|
9
9
|
MIT
|
|
@@ -1657,6 +1657,7 @@ Attributes:
|
|
|
1657
1657
|
- `position` — `"top"` (default) | `"bottom"` | `"left"` | `"right"`
|
|
1658
1658
|
- `size` — `"small"` | `"medium"` | `"large"`
|
|
1659
1659
|
- `variant` — `"default"` (default, bordered boxes) | `"accent"` (minimal tabs; the active tab shows a primary-colored indicator border on its content-facing edge — bottom for `top` tabs, top for `bottom`, etc. — like Material/Carbon)
|
|
1660
|
+
- `overflow` — `"scroll"` (default; tabs stay on one line and prev/next arrow buttons appear when the strip overflows its container) | `"wrap"` (tabs flow onto multiple rows, or columns for `left`/`right` positions)
|
|
1660
1661
|
|
|
1661
1662
|
Methods: `activateTab(id)`
|
|
1662
1663
|
|
|
@@ -1363,6 +1363,7 @@ Methods: `.show(opts)` — **single options object**: `{ message, color, duratio
|
|
|
1363
1363
|
| `position` | `top` (default) \| `bottom` \| `left` \| `right` |
|
|
1364
1364
|
| `size` | `small` \| `medium` \| `large` |
|
|
1365
1365
|
| `variant` | `default` (bordered boxes) \| `accent` (minimal tabs; active tab shows a primary indicator border on its content-facing edge, like Material/Carbon) |
|
|
1366
|
+
| `overflow` | `scroll` (default; one line + prev/next arrows when the strip overflows) \| `wrap` (tabs flow onto multiple rows/columns) |
|
|
1366
1367
|
|
|
1367
1368
|
Options object shape: `{"id":"tab1","label":"Tab 1","slot":"tab1","disabled":false,"leftIcon":"home","rightIcon":"arrow-right"}` — `id`, `label`, and `slot` are required; `disabled`, `leftIcon`, `rightIcon` are optional.
|
|
1368
1369
|
|
|
@@ -1947,7 +1947,8 @@ class YumeCode extends HTMLElement {
|
|
|
1947
1947
|
_buildStyles() {
|
|
1948
1948
|
return `
|
|
1949
1949
|
:host {
|
|
1950
|
-
display:
|
|
1950
|
+
display: flex;
|
|
1951
|
+
flex-direction: column;
|
|
1951
1952
|
font-family: var(--component-code-font-family, var(--font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace));
|
|
1952
1953
|
color: var(--component-code-text-color, var(--base-content--, #1a1a1a));
|
|
1953
1954
|
background: var(--component-code-bg-color, var(--base-background-component, #f6f8fa));
|
|
@@ -1997,13 +1998,19 @@ class YumeCode extends HTMLElement {
|
|
|
1997
1998
|
.pre-wrap {
|
|
1998
1999
|
position: relative;
|
|
1999
2000
|
overflow: hidden;
|
|
2001
|
+
display: flex;
|
|
2002
|
+
flex-direction: column;
|
|
2003
|
+
flex: 1 1 auto;
|
|
2004
|
+
min-height: 0;
|
|
2000
2005
|
}
|
|
2001
2006
|
|
|
2002
2007
|
pre.code {
|
|
2003
2008
|
margin: 0;
|
|
2004
2009
|
padding: var(--spacing-small, 8px) 0;
|
|
2010
|
+
flex: 1 1 auto;
|
|
2011
|
+
min-height: 0;
|
|
2005
2012
|
overflow-x: ${this.wrap ? "hidden" : "auto"};
|
|
2006
|
-
overflow-y:
|
|
2013
|
+
overflow-y: auto;
|
|
2007
2014
|
font-size: var(--component-code-font-size, 0.9em);
|
|
2008
2015
|
line-height: var(--component-code-line-height, 1.55);
|
|
2009
2016
|
tab-size: 4;
|
|
@@ -2,11 +2,22 @@ export class YumeTabs extends HTMLElement {
|
|
|
2
2
|
static get observedAttributes(): string[];
|
|
3
3
|
_activeTab: string;
|
|
4
4
|
_warnedSlots: Set<any>;
|
|
5
|
+
_resizeObserver: ResizeObserver;
|
|
6
|
+
_onTablistScroll(): void;
|
|
5
7
|
connectedCallback(): void;
|
|
8
|
+
disconnectedCallback(): void;
|
|
6
9
|
attributeChangedCallback(name: any, oldVal: any, newVal: any): void;
|
|
7
10
|
set options(val: Array<any>);
|
|
8
11
|
/** @type {Array<Object>} Tab definitions. Each object: `{ id, label, slot, disabled?, leftIcon?, rightIcon? }`. `leftIcon`/`rightIcon` are `y-icon` names rendered inside the tab button. Use the `tab-content-{id}` slot to supply fully custom tab button content instead. */
|
|
9
12
|
get options(): Array<any>;
|
|
13
|
+
set overflow(val: "scroll" | "wrap");
|
|
14
|
+
/**
|
|
15
|
+
* @type {"scroll"|"wrap"} How a tab strip wider (or taller) than its
|
|
16
|
+
* container behaves. `"scroll"` keeps tabs on a single line and reveals
|
|
17
|
+
* prev/next arrow buttons when the strip overflows; `"wrap"` lets tabs flow
|
|
18
|
+
* onto multiple rows (or columns, for left/right positions).
|
|
19
|
+
*/
|
|
20
|
+
get overflow(): "scroll" | "wrap";
|
|
10
21
|
set position(val: "top" | "bottom" | "left" | "right");
|
|
11
22
|
/** @type {"top"|"bottom"|"left"|"right"} Which edge the tab strip is placed on. */
|
|
12
23
|
get position(): "top" | "bottom" | "left" | "right";
|
|
@@ -27,6 +38,8 @@ export class YumeTabs extends HTMLElement {
|
|
|
27
38
|
activateTab(id: string): void;
|
|
28
39
|
render(): void;
|
|
29
40
|
_appendDeprecatedIconSlot(parent: any, side: any, tabId: any): void;
|
|
41
|
+
_buildScrollButton(direction: any): HTMLElement;
|
|
42
|
+
_buildTabStrip(tablist: any): HTMLElement;
|
|
30
43
|
_createIcon(name: any): HTMLElement;
|
|
31
44
|
_createPanel(slotName: any): HTMLElement;
|
|
32
45
|
_createTabButton(tab: any): HTMLElement;
|
|
@@ -34,5 +47,9 @@ export class YumeTabs extends HTMLElement {
|
|
|
34
47
|
_getStyles(): string;
|
|
35
48
|
_handleTabKeydown(e: any, buttons: any): void;
|
|
36
49
|
_resolveActiveTab(tabs: any): void;
|
|
50
|
+
_scrollTabs(direction: any): void;
|
|
37
51
|
_setupEvents(): void;
|
|
52
|
+
_setupScroll(): void;
|
|
53
|
+
_teardownScroll(): void;
|
|
54
|
+
_updateScrollButtons(): void;
|
|
38
55
|
}
|
|
@@ -2,11 +2,22 @@ export class YumeTabs extends HTMLElement {
|
|
|
2
2
|
static get observedAttributes(): string[];
|
|
3
3
|
_activeTab: string;
|
|
4
4
|
_warnedSlots: Set<any>;
|
|
5
|
+
_resizeObserver: ResizeObserver;
|
|
6
|
+
_onTablistScroll(): void;
|
|
5
7
|
connectedCallback(): void;
|
|
8
|
+
disconnectedCallback(): void;
|
|
6
9
|
attributeChangedCallback(name: any, oldVal: any, newVal: any): void;
|
|
7
10
|
set options(val: Array<any>);
|
|
8
11
|
/** @type {Array<Object>} Tab definitions. Each object: `{ id, label, slot, disabled?, leftIcon?, rightIcon? }`. `leftIcon`/`rightIcon` are `y-icon` names rendered inside the tab button. Use the `tab-content-{id}` slot to supply fully custom tab button content instead. */
|
|
9
12
|
get options(): Array<any>;
|
|
13
|
+
set overflow(val: "scroll" | "wrap");
|
|
14
|
+
/**
|
|
15
|
+
* @type {"scroll"|"wrap"} How a tab strip wider (or taller) than its
|
|
16
|
+
* container behaves. `"scroll"` keeps tabs on a single line and reveals
|
|
17
|
+
* prev/next arrow buttons when the strip overflows; `"wrap"` lets tabs flow
|
|
18
|
+
* onto multiple rows (or columns, for left/right positions).
|
|
19
|
+
*/
|
|
20
|
+
get overflow(): "scroll" | "wrap";
|
|
10
21
|
set position(val: "top" | "bottom" | "left" | "right");
|
|
11
22
|
/** @type {"top"|"bottom"|"left"|"right"} Which edge the tab strip is placed on. */
|
|
12
23
|
get position(): "top" | "bottom" | "left" | "right";
|
|
@@ -27,6 +38,8 @@ export class YumeTabs extends HTMLElement {
|
|
|
27
38
|
activateTab(id: string): void;
|
|
28
39
|
render(): void;
|
|
29
40
|
_appendDeprecatedIconSlot(parent: any, side: any, tabId: any): void;
|
|
41
|
+
_buildScrollButton(direction: any): HTMLElement;
|
|
42
|
+
_buildTabStrip(tablist: any): HTMLElement;
|
|
30
43
|
_createIcon(name: any): HTMLElement;
|
|
31
44
|
_createPanel(slotName: any): HTMLElement;
|
|
32
45
|
_createTabButton(tab: any): HTMLElement;
|
|
@@ -34,5 +47,9 @@ export class YumeTabs extends HTMLElement {
|
|
|
34
47
|
_getStyles(): string;
|
|
35
48
|
_handleTabKeydown(e: any, buttons: any): void;
|
|
36
49
|
_resolveActiveTab(tabs: any): void;
|
|
50
|
+
_scrollTabs(direction: any): void;
|
|
37
51
|
_setupEvents(): void;
|
|
52
|
+
_setupScroll(): void;
|
|
53
|
+
_teardownScroll(): void;
|
|
54
|
+
_updateScrollButtons(): void;
|
|
38
55
|
}
|
|
@@ -362,7 +362,7 @@ if (!customElements.get("y-icon")) {
|
|
|
362
362
|
|
|
363
363
|
class YumeTabs extends HTMLElement {
|
|
364
364
|
static get observedAttributes() {
|
|
365
|
-
return ["options", "size", "position", "variant"];
|
|
365
|
+
return ["options", "size", "position", "variant", "overflow"];
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// -------------------------------------------------------------------------
|
|
@@ -374,15 +374,23 @@ class YumeTabs extends HTMLElement {
|
|
|
374
374
|
this.attachShadow({ mode: "open" });
|
|
375
375
|
this._activeTab = "";
|
|
376
376
|
this._warnedSlots = new Set();
|
|
377
|
+
this._resizeObserver = null;
|
|
378
|
+
this._onTablistScroll = this._onTablistScroll.bind(this);
|
|
377
379
|
}
|
|
378
380
|
|
|
379
381
|
connectedCallback() {
|
|
380
382
|
if (!this.hasAttribute("size")) this.setAttribute("size", "medium");
|
|
381
383
|
if (!this.hasAttribute("position"))
|
|
382
384
|
this.setAttribute("position", "top");
|
|
385
|
+
if (!this.hasAttribute("overflow"))
|
|
386
|
+
this.setAttribute("overflow", "scroll");
|
|
383
387
|
this.render();
|
|
384
388
|
}
|
|
385
389
|
|
|
390
|
+
disconnectedCallback() {
|
|
391
|
+
this._teardownScroll();
|
|
392
|
+
}
|
|
393
|
+
|
|
386
394
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
387
395
|
if (oldVal === newVal) return;
|
|
388
396
|
if (name === "options") this._warnedSlots.clear();
|
|
@@ -408,6 +416,19 @@ class YumeTabs extends HTMLElement {
|
|
|
408
416
|
this.render();
|
|
409
417
|
}
|
|
410
418
|
|
|
419
|
+
/**
|
|
420
|
+
* @type {"scroll"|"wrap"} How a tab strip wider (or taller) than its
|
|
421
|
+
* container behaves. `"scroll"` keeps tabs on a single line and reveals
|
|
422
|
+
* prev/next arrow buttons when the strip overflows; `"wrap"` lets tabs flow
|
|
423
|
+
* onto multiple rows (or columns, for left/right positions).
|
|
424
|
+
*/
|
|
425
|
+
get overflow() {
|
|
426
|
+
return this.getAttribute("overflow") === "wrap" ? "wrap" : "scroll";
|
|
427
|
+
}
|
|
428
|
+
set overflow(val) {
|
|
429
|
+
this.setAttribute("overflow", val === "wrap" ? "wrap" : "scroll");
|
|
430
|
+
}
|
|
431
|
+
|
|
411
432
|
/** @type {"top"|"bottom"|"left"|"right"} Which edge the tab strip is placed on. */
|
|
412
433
|
get position() {
|
|
413
434
|
const pos = this.getAttribute("position");
|
|
@@ -466,6 +487,7 @@ class YumeTabs extends HTMLElement {
|
|
|
466
487
|
|
|
467
488
|
const activeDef = tabs.find((t) => t.id === this._activeTab);
|
|
468
489
|
|
|
490
|
+
this._teardownScroll();
|
|
469
491
|
this.shadowRoot.innerHTML = "";
|
|
470
492
|
|
|
471
493
|
const style = document.createElement("style");
|
|
@@ -478,11 +500,12 @@ class YumeTabs extends HTMLElement {
|
|
|
478
500
|
part: "tablist",
|
|
479
501
|
});
|
|
480
502
|
tabs.forEach((tab) => tablist.appendChild(this._createTabButton(tab)));
|
|
481
|
-
this.shadowRoot.appendChild(tablist);
|
|
482
503
|
|
|
504
|
+
this.shadowRoot.appendChild(this._buildTabStrip(tablist));
|
|
483
505
|
this.shadowRoot.appendChild(this._createPanel(activeDef?.slot || ""));
|
|
484
506
|
|
|
485
507
|
this._setupEvents();
|
|
508
|
+
this._setupScroll();
|
|
486
509
|
}
|
|
487
510
|
|
|
488
511
|
// -------------------------------------------------------------------------
|
|
@@ -507,6 +530,47 @@ class YumeTabs extends HTMLElement {
|
|
|
507
530
|
parent.appendChild(createElement("slot", { name: slotName, class: "icon-slot" }));
|
|
508
531
|
}
|
|
509
532
|
|
|
533
|
+
_buildScrollButton(direction) {
|
|
534
|
+
const vertical = this.position === "left" || this.position === "right";
|
|
535
|
+
const icon =
|
|
536
|
+
direction === "prev"
|
|
537
|
+
? vertical
|
|
538
|
+
? "chevron-up"
|
|
539
|
+
: "chevron-left"
|
|
540
|
+
: vertical
|
|
541
|
+
? "chevron-down"
|
|
542
|
+
: "chevron-right";
|
|
543
|
+
const btn = createElement(
|
|
544
|
+
"button",
|
|
545
|
+
{
|
|
546
|
+
type: "button",
|
|
547
|
+
class: `scroll-btn scroll-${direction}`,
|
|
548
|
+
part: `scroll-button scroll-${direction}`,
|
|
549
|
+
"aria-label":
|
|
550
|
+
direction === "prev"
|
|
551
|
+
? "Scroll tabs backward"
|
|
552
|
+
: "Scroll tabs forward",
|
|
553
|
+
tabindex: "-1",
|
|
554
|
+
hidden: "",
|
|
555
|
+
},
|
|
556
|
+
[createElement("y-icon", { name: icon, size: this.size, "aria-hidden": "true" })],
|
|
557
|
+
);
|
|
558
|
+
btn.addEventListener("click", () => this._scrollTabs(direction));
|
|
559
|
+
return btn;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
_buildTabStrip(tablist) {
|
|
563
|
+
const strip = createElement("div", { class: "tabstrip", part: "tabstrip" });
|
|
564
|
+
if (this.overflow === "scroll") {
|
|
565
|
+
strip.appendChild(this._buildScrollButton("prev"));
|
|
566
|
+
strip.appendChild(tablist);
|
|
567
|
+
strip.appendChild(this._buildScrollButton("next"));
|
|
568
|
+
} else {
|
|
569
|
+
strip.appendChild(tablist);
|
|
570
|
+
}
|
|
571
|
+
return strip;
|
|
572
|
+
}
|
|
573
|
+
|
|
510
574
|
_createIcon(name) {
|
|
511
575
|
return createElement("y-icon", { name, size: this.size });
|
|
512
576
|
}
|
|
@@ -587,16 +651,63 @@ class YumeTabs extends HTMLElement {
|
|
|
587
651
|
:host([position="left"]) { flex-direction: row; }
|
|
588
652
|
:host([position="right"]) { flex-direction: row-reverse; }
|
|
589
653
|
|
|
654
|
+
.tabstrip {
|
|
655
|
+
display: flex;
|
|
656
|
+
position: relative;
|
|
657
|
+
z-index: 1;
|
|
658
|
+
min-width: 0;
|
|
659
|
+
min-height: 0;
|
|
660
|
+
}
|
|
661
|
+
:host([position="left"]) .tabstrip,
|
|
662
|
+
:host([position="right"]) .tabstrip { flex-direction: column; }
|
|
663
|
+
:host([position="top"]) .tabstrip { margin-bottom: -1px; }
|
|
664
|
+
:host([position="bottom"]) .tabstrip { margin-top: -1px; }
|
|
665
|
+
:host([position="left"]) .tabstrip { margin-right: -1px; }
|
|
666
|
+
:host([position="right"]) .tabstrip { margin-left: -1px; }
|
|
667
|
+
|
|
590
668
|
.tablist {
|
|
591
669
|
display: flex;
|
|
592
670
|
gap: 0;
|
|
593
671
|
position: relative;
|
|
594
|
-
|
|
672
|
+
min-width: 0;
|
|
673
|
+
min-height: 0;
|
|
674
|
+
}
|
|
675
|
+
:host([position="left"]) .tablist,
|
|
676
|
+
:host([position="right"]) .tablist { flex-direction: column; }
|
|
677
|
+
|
|
678
|
+
/* Scroll mode: tabs stay on one line; arrow buttons drive scrolling,
|
|
679
|
+
so the native scrollbar is hidden. */
|
|
680
|
+
:host([overflow="scroll"]) .tablist {
|
|
681
|
+
flex: 1 1 auto;
|
|
682
|
+
overflow: auto;
|
|
683
|
+
scrollbar-width: none;
|
|
684
|
+
-ms-overflow-style: none;
|
|
685
|
+
}
|
|
686
|
+
:host([overflow="scroll"]) .tablist::-webkit-scrollbar { display: none; }
|
|
687
|
+
|
|
688
|
+
/* Wrap mode: tabs flow onto multiple rows (or columns). */
|
|
689
|
+
:host([overflow="wrap"]) .tablist { flex-wrap: wrap; }
|
|
690
|
+
|
|
691
|
+
.scroll-btn {
|
|
692
|
+
flex: 0 0 auto;
|
|
693
|
+
display: inline-flex;
|
|
694
|
+
align-items: center;
|
|
695
|
+
justify-content: center;
|
|
696
|
+
padding: 0 4px;
|
|
697
|
+
background: var(--component-tabs-inactive-background, var(--component-tabs-border-color));
|
|
698
|
+
color: var(--component-tabs-color);
|
|
699
|
+
border: 1px solid var(--component-tabs-border-color);
|
|
700
|
+
border-width: var(--component-tabs-border-width, var(--component-tab-border-width, 1px));
|
|
701
|
+
cursor: pointer;
|
|
702
|
+
font-family: inherit;
|
|
703
|
+
}
|
|
704
|
+
.scroll-btn[hidden] { display: none; }
|
|
705
|
+
.scroll-btn:hover { background: var(--component-tabs-background); }
|
|
706
|
+
.scroll-btn:focus-visible {
|
|
707
|
+
outline: 2px solid var(--component-tabs-accent);
|
|
708
|
+
outline-offset: -1px;
|
|
595
709
|
}
|
|
596
|
-
:host([
|
|
597
|
-
:host([position="bottom"]) .tablist { margin-top: -1px; margin-bottom: 0; }
|
|
598
|
-
:host([position="left"]) .tablist { flex-direction: column; margin-right: -1px; margin-left: 0; }
|
|
599
|
-
:host([position="right"]) .tablist { flex-direction: column; margin-left: -1px; margin-right: 0; }
|
|
710
|
+
:host([variant="accent"]) .scroll-btn { background: transparent; border: none; }
|
|
600
711
|
|
|
601
712
|
:host([position="top"]) .tablist button { border-bottom: none; }
|
|
602
713
|
:host([position="bottom"]) .tablist button { border-top: none; }
|
|
@@ -712,6 +823,10 @@ class YumeTabs extends HTMLElement {
|
|
|
712
823
|
}
|
|
713
824
|
}
|
|
714
825
|
|
|
826
|
+
_onTablistScroll() {
|
|
827
|
+
this._updateScrollButtons();
|
|
828
|
+
}
|
|
829
|
+
|
|
715
830
|
_resolveActiveTab(tabs) {
|
|
716
831
|
const currentInvalid =
|
|
717
832
|
!this._activeTab ||
|
|
@@ -721,8 +836,26 @@ class YumeTabs extends HTMLElement {
|
|
|
721
836
|
}
|
|
722
837
|
}
|
|
723
838
|
|
|
839
|
+
_scrollTabs(direction) {
|
|
840
|
+
const tablist = this.shadowRoot.querySelector(".tablist");
|
|
841
|
+
if (!tablist) return;
|
|
842
|
+
|
|
843
|
+
const vertical = this.position === "left" || this.position === "right";
|
|
844
|
+
const amount =
|
|
845
|
+
(vertical ? tablist.clientHeight : tablist.clientWidth) * 0.75;
|
|
846
|
+
const delta = direction === "prev" ? -amount : amount;
|
|
847
|
+
|
|
848
|
+
tablist.scrollBy(
|
|
849
|
+
vertical
|
|
850
|
+
? { top: delta, behavior: "smooth" }
|
|
851
|
+
: { left: delta, behavior: "smooth" },
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
724
855
|
_setupEvents() {
|
|
725
|
-
const buttons = Array.from(
|
|
856
|
+
const buttons = Array.from(
|
|
857
|
+
this.shadowRoot.querySelectorAll(".tablist button"),
|
|
858
|
+
);
|
|
726
859
|
buttons.forEach((button) => {
|
|
727
860
|
if (button.disabled) return;
|
|
728
861
|
button.addEventListener("click", () =>
|
|
@@ -733,6 +866,58 @@ class YumeTabs extends HTMLElement {
|
|
|
733
866
|
);
|
|
734
867
|
});
|
|
735
868
|
}
|
|
869
|
+
|
|
870
|
+
_setupScroll() {
|
|
871
|
+
if (this.overflow !== "scroll") return;
|
|
872
|
+
|
|
873
|
+
const tablist = this.shadowRoot.querySelector(".tablist");
|
|
874
|
+
if (!tablist) return;
|
|
875
|
+
|
|
876
|
+
tablist.addEventListener("scroll", this._onTablistScroll, {
|
|
877
|
+
passive: true,
|
|
878
|
+
});
|
|
879
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
880
|
+
this._resizeObserver = new ResizeObserver(() =>
|
|
881
|
+
this._updateScrollButtons(),
|
|
882
|
+
);
|
|
883
|
+
this._resizeObserver.observe(tablist);
|
|
884
|
+
this._resizeObserver.observe(this);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
this._updateScrollButtons();
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
_teardownScroll() {
|
|
891
|
+
this._resizeObserver?.disconnect();
|
|
892
|
+
this._resizeObserver = null;
|
|
893
|
+
const tablist = this.shadowRoot?.querySelector(".tablist");
|
|
894
|
+
tablist?.removeEventListener("scroll", this._onTablistScroll);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
_updateScrollButtons() {
|
|
898
|
+
const tablist = this.shadowRoot.querySelector(".tablist");
|
|
899
|
+
const prev = this.shadowRoot.querySelector(".scroll-prev");
|
|
900
|
+
const next = this.shadowRoot.querySelector(".scroll-next");
|
|
901
|
+
if (!tablist || !prev || !next) return;
|
|
902
|
+
|
|
903
|
+
const vertical = this.position === "left" || this.position === "right";
|
|
904
|
+
const scrollSize = vertical
|
|
905
|
+
? tablist.scrollHeight
|
|
906
|
+
: tablist.scrollWidth;
|
|
907
|
+
const clientSize = vertical
|
|
908
|
+
? tablist.clientHeight
|
|
909
|
+
: tablist.clientWidth;
|
|
910
|
+
const scrollPos = vertical ? tablist.scrollTop : tablist.scrollLeft;
|
|
911
|
+
|
|
912
|
+
if (scrollSize - clientSize <= 1) {
|
|
913
|
+
prev.hidden = true;
|
|
914
|
+
next.hidden = true;
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
prev.hidden = scrollPos <= 1;
|
|
919
|
+
next.hidden = scrollPos >= scrollSize - clientSize - 1;
|
|
920
|
+
}
|
|
736
921
|
}
|
|
737
922
|
|
|
738
923
|
if (!customElements.get("y-tabs")) {
|