@api-client/ui 0.5.32 → 0.5.33
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/build/src/elements/navigation/internals/Navigation.d.ts +68 -0
- package/build/src/elements/navigation/internals/Navigation.d.ts.map +1 -0
- package/build/src/elements/navigation/internals/Navigation.js +205 -0
- package/build/src/elements/navigation/internals/Navigation.js.map +1 -0
- package/build/src/elements/navigation/internals/Navigation.styles.d.ts +3 -0
- package/build/src/elements/navigation/internals/Navigation.styles.d.ts.map +1 -0
- package/build/src/elements/navigation/internals/Navigation.styles.js +24 -0
- package/build/src/elements/navigation/internals/Navigation.styles.js.map +1 -0
- package/build/src/elements/navigation/internals/NavigationItem.d.ts +57 -2
- package/build/src/elements/navigation/internals/NavigationItem.d.ts.map +1 -1
- package/build/src/elements/navigation/internals/NavigationItem.js +73 -18
- package/build/src/elements/navigation/internals/NavigationItem.js.map +1 -1
- package/build/src/elements/navigation/ui-navigation.d.ts +11 -0
- package/build/src/elements/navigation/ui-navigation.d.ts.map +1 -0
- package/build/src/elements/navigation/ui-navigation.js +27 -0
- package/build/src/elements/navigation/ui-navigation.js.map +1 -0
- package/build/src/md/input/Input.d.ts +2 -0
- package/build/src/md/input/Input.d.ts.map +1 -1
- package/build/src/types/aria.d.ts +28 -0
- package/build/src/types/aria.d.ts.map +1 -0
- package/build/src/types/aria.js +2 -0
- package/build/src/types/aria.js.map +1 -0
- package/build/src/types/role.d.ts +1 -16
- package/build/src/types/role.d.ts.map +1 -1
- package/build/src/types/role.js.map +1 -1
- package/build/test/elements/navigation/Navigation.test.d.ts +3 -0
- package/build/test/elements/navigation/Navigation.test.d.ts.map +1 -0
- package/build/test/elements/navigation/Navigation.test.js +113 -0
- package/build/test/elements/navigation/Navigation.test.js.map +1 -0
- package/demo/elements/index.html +2 -0
- package/demo/elements/navigation/navigation.html +20 -0
- package/demo/elements/navigation/navigation.ts +45 -0
- package/package.json +1 -1
- package/src/elements/navigation/internals/Navigation.styles.ts +24 -0
- package/src/elements/navigation/internals/Navigation.ts +181 -0
- package/src/elements/navigation/internals/NavigationItem.ts +74 -5
- package/src/elements/navigation/ui-navigation.ts +15 -0
- package/src/types/aria.ts +141 -0
- package/src/types/role.ts +1 -129
- package/test/elements/navigation/Navigation.test.ts +120 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LitElement, type TemplateResult } from 'lit';
|
|
2
|
+
import type NavigationItem from './NavigationItem.js';
|
|
3
|
+
import { AriaCurrent } from '../../../types/aria.js';
|
|
4
|
+
/**
|
|
5
|
+
* NavigationList
|
|
6
|
+
*
|
|
7
|
+
* A semantic, accessible navigation container for `ui-navigation-item` elements.
|
|
8
|
+
* Handles keyboard navigation (roving tabindex), focus management, and ARIA roles.
|
|
9
|
+
*
|
|
10
|
+
* ## Usage Example
|
|
11
|
+
*
|
|
12
|
+
* ```html
|
|
13
|
+
* <ui-navigation aria-label="Main navigation" current="page">
|
|
14
|
+
* <ui-navigation-item selected>
|
|
15
|
+
* <span slot="icon" aria-hidden="true">🏠</span>
|
|
16
|
+
* Home
|
|
17
|
+
* </ui-navigation-item>
|
|
18
|
+
* <ui-navigation-item>
|
|
19
|
+
* <span slot="icon" aria-hidden="true">🔍</span>
|
|
20
|
+
* Search
|
|
21
|
+
* </ui-navigation-item>
|
|
22
|
+
* <ui-navigation-item>
|
|
23
|
+
* <span slot="icon" aria-hidden="true">📁</span>
|
|
24
|
+
* Files
|
|
25
|
+
* </ui-navigation-item>
|
|
26
|
+
* <ui-navigation-item iconOnly disabled aria-disabled="true" tabindex="-1">
|
|
27
|
+
* <span slot="icon" aria-hidden="true">⚙️</span>
|
|
28
|
+
* </ui-navigation-item>
|
|
29
|
+
* </ui-navigation>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @slot - Place `ui-navigation-item` elements as direct children.
|
|
33
|
+
* @fires select - Dispatched when an item is selected via click or keyboard.
|
|
34
|
+
* The `detail` object contains the selected `item`.
|
|
35
|
+
*/
|
|
36
|
+
export default class Navigation extends LitElement {
|
|
37
|
+
/**
|
|
38
|
+
* Orientation of the navigation: 'horizontal' or 'vertical'.
|
|
39
|
+
* @attribute
|
|
40
|
+
*/
|
|
41
|
+
accessor orientation: 'horizontal' | 'vertical';
|
|
42
|
+
/**
|
|
43
|
+
* The current item in the navigation, used for ARIA attributes.
|
|
44
|
+
* Can be 'page', 'step', 'location', 'date', 'time', 'true', 'false', or ''.
|
|
45
|
+
* @attribute
|
|
46
|
+
*/
|
|
47
|
+
accessor current: AriaCurrent;
|
|
48
|
+
/**
|
|
49
|
+
* Query all ui-navigation-item children
|
|
50
|
+
*/
|
|
51
|
+
accessor _items: NavigationItem[];
|
|
52
|
+
render(): TemplateResult;
|
|
53
|
+
constructor();
|
|
54
|
+
firstUpdated(): void;
|
|
55
|
+
private _onSlotChange;
|
|
56
|
+
/**
|
|
57
|
+
* Sets the initial `tabindex` for the items. The first selected item,
|
|
58
|
+
* or the first item if none are selected, receives `tabindex="0"`.
|
|
59
|
+
* All other items receive `tabindex="-1"`.
|
|
60
|
+
*/
|
|
61
|
+
private _setTabIndexes;
|
|
62
|
+
private _getEnabledItems;
|
|
63
|
+
private _onClick;
|
|
64
|
+
private _selectItem;
|
|
65
|
+
private _updateFocus;
|
|
66
|
+
private _onKeyDown;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=Navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Navigation.d.ts","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/Navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAA;AAE3D,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,UAAU;IAChD;;;OAGG;IACwC,QAAQ,CAAC,WAAW,EAAE,YAAY,GAAG,UAAU,CAAa;IAEvG;;;;OAIG;IACwC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAK;IAE7E;;OAEG;IACuC,QAAQ,CAAC,MAAM,EAAG,cAAc,EAAE,CAAA;IAEnE,MAAM,IAAI,cAAc;;IAcxB,YAAY;IAIrB,OAAO,CAAC,aAAa,CAEpB;IAED;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ,CAMf;IAED,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,UAAU;CAyCnB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { html, LitElement } from 'lit';
|
|
3
|
+
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
let Navigation = (() => {
|
|
5
|
+
let _classSuper = LitElement;
|
|
6
|
+
let _orientation_decorators;
|
|
7
|
+
let _orientation_initializers = [];
|
|
8
|
+
let _orientation_extraInitializers = [];
|
|
9
|
+
let _current_decorators;
|
|
10
|
+
let _current_initializers = [];
|
|
11
|
+
let _current_extraInitializers = [];
|
|
12
|
+
let __items_decorators;
|
|
13
|
+
let __items_initializers = [];
|
|
14
|
+
let __items_extraInitializers = [];
|
|
15
|
+
return class Navigation extends _classSuper {
|
|
16
|
+
static {
|
|
17
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
18
|
+
_orientation_decorators = [property({ type: String, reflect: true })];
|
|
19
|
+
_current_decorators = [property({ type: String, reflect: true })];
|
|
20
|
+
__items_decorators = [queryAssignedElements({ flatten: true })];
|
|
21
|
+
__esDecorate(this, null, _orientation_decorators, { kind: "accessor", name: "orientation", static: false, private: false, access: { has: obj => "orientation" in obj, get: obj => obj.orientation, set: (obj, value) => { obj.orientation = value; } }, metadata: _metadata }, _orientation_initializers, _orientation_extraInitializers);
|
|
22
|
+
__esDecorate(this, null, _current_decorators, { kind: "accessor", name: "current", static: false, private: false, access: { has: obj => "current" in obj, get: obj => obj.current, set: (obj, value) => { obj.current = value; } }, metadata: _metadata }, _current_initializers, _current_extraInitializers);
|
|
23
|
+
__esDecorate(this, null, __items_decorators, { kind: "accessor", name: "_items", static: false, private: false, access: { has: obj => "_items" in obj, get: obj => obj._items, set: (obj, value) => { obj._items = value; } }, metadata: _metadata }, __items_initializers, __items_extraInitializers);
|
|
24
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
25
|
+
}
|
|
26
|
+
#orientation_accessor_storage = __runInitializers(this, _orientation_initializers, 'vertical'
|
|
27
|
+
/**
|
|
28
|
+
* The current item in the navigation, used for ARIA attributes.
|
|
29
|
+
* Can be 'page', 'step', 'location', 'date', 'time', 'true', 'false', or ''.
|
|
30
|
+
* @attribute
|
|
31
|
+
*/
|
|
32
|
+
);
|
|
33
|
+
/**
|
|
34
|
+
* Orientation of the navigation: 'horizontal' or 'vertical'.
|
|
35
|
+
* @attribute
|
|
36
|
+
*/
|
|
37
|
+
get orientation() { return this.#orientation_accessor_storage; }
|
|
38
|
+
set orientation(value) { this.#orientation_accessor_storage = value; }
|
|
39
|
+
#current_accessor_storage = (__runInitializers(this, _orientation_extraInitializers), __runInitializers(this, _current_initializers, ''
|
|
40
|
+
/**
|
|
41
|
+
* Query all ui-navigation-item children
|
|
42
|
+
*/
|
|
43
|
+
));
|
|
44
|
+
/**
|
|
45
|
+
* The current item in the navigation, used for ARIA attributes.
|
|
46
|
+
* Can be 'page', 'step', 'location', 'date', 'time', 'true', 'false', or ''.
|
|
47
|
+
* @attribute
|
|
48
|
+
*/
|
|
49
|
+
get current() { return this.#current_accessor_storage; }
|
|
50
|
+
set current(value) { this.#current_accessor_storage = value; }
|
|
51
|
+
#_items_accessor_storage = (__runInitializers(this, _current_extraInitializers), __runInitializers(this, __items_initializers, void 0));
|
|
52
|
+
/**
|
|
53
|
+
* Query all ui-navigation-item children
|
|
54
|
+
*/
|
|
55
|
+
get _items() { return this.#_items_accessor_storage; }
|
|
56
|
+
set _items(value) { this.#_items_accessor_storage = value; }
|
|
57
|
+
render() {
|
|
58
|
+
return html `
|
|
59
|
+
<nav aria-label="${this.getAttribute('aria-label') || 'Navigation'}">
|
|
60
|
+
<slot @slotchange="${this._onSlotChange}"></slot>
|
|
61
|
+
</nav>
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
constructor() {
|
|
65
|
+
super();
|
|
66
|
+
this.addEventListener('keydown', this._onKeyDown.bind(this));
|
|
67
|
+
this.addEventListener('click', this._onClick.bind(this));
|
|
68
|
+
}
|
|
69
|
+
firstUpdated() {
|
|
70
|
+
this._setTabIndexes();
|
|
71
|
+
}
|
|
72
|
+
_onSlotChange = (__runInitializers(this, __items_extraInitializers), () => {
|
|
73
|
+
this._setTabIndexes();
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* Sets the initial `tabindex` for the items. The first selected item,
|
|
77
|
+
* or the first item if none are selected, receives `tabindex="0"`.
|
|
78
|
+
* All other items receive `tabindex="-1"`.
|
|
79
|
+
*/
|
|
80
|
+
_setTabIndexes() {
|
|
81
|
+
const items = this._getEnabledItems();
|
|
82
|
+
if (!items.length) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
items.forEach((item) => {
|
|
86
|
+
if (item.disabled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
item.setAttribute('tabindex', item.selected ? '0' : '-1');
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
_getEnabledItems() {
|
|
93
|
+
return (this._items || []).filter((item) => !item.disabled);
|
|
94
|
+
}
|
|
95
|
+
_onClick = (e) => {
|
|
96
|
+
const target = e.target;
|
|
97
|
+
const item = target.closest('ui-navigation-item');
|
|
98
|
+
if (item && this._items.includes(item)) {
|
|
99
|
+
this._selectItem(item);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
_selectItem(itemToSelect) {
|
|
103
|
+
if (itemToSelect.disabled) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this._items.forEach((item) => {
|
|
107
|
+
item.selected = item === itemToSelect;
|
|
108
|
+
if (item.hasAttribute('aria-current')) {
|
|
109
|
+
item.removeAttribute('aria-current');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (this.current) {
|
|
113
|
+
itemToSelect.setAttribute('aria-current', this.current);
|
|
114
|
+
}
|
|
115
|
+
this._updateFocus(itemToSelect);
|
|
116
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
117
|
+
detail: { item: itemToSelect },
|
|
118
|
+
bubbles: false,
|
|
119
|
+
composed: true,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
_updateFocus(itemToFocus) {
|
|
123
|
+
this._setTabIndexes();
|
|
124
|
+
itemToFocus.focus();
|
|
125
|
+
}
|
|
126
|
+
_onKeyDown(e) {
|
|
127
|
+
const items = this._getEnabledItems();
|
|
128
|
+
if (!items.length) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const activeElement = this.getRootNode().activeElement;
|
|
132
|
+
if (!activeElement) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Find which of our items is the host of the active element.
|
|
136
|
+
// This is necessary because of focus delegation in ui-navigation-item.
|
|
137
|
+
const activeItem = items.find((item) => item === activeElement || item.shadowRoot?.contains(activeElement));
|
|
138
|
+
if (!activeItem) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
this._selectItem(activeItem);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const idx = items.indexOf(activeItem);
|
|
147
|
+
let nextIdx = idx;
|
|
148
|
+
if (this.orientation === 'horizontal') {
|
|
149
|
+
if (e.key === 'ArrowRight')
|
|
150
|
+
nextIdx = (idx + 1) % items.length;
|
|
151
|
+
if (e.key === 'ArrowLeft')
|
|
152
|
+
nextIdx = (idx - 1 + items.length) % items.length;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
if (e.key === 'ArrowDown')
|
|
156
|
+
nextIdx = (idx + 1) % items.length;
|
|
157
|
+
if (e.key === 'ArrowUp')
|
|
158
|
+
nextIdx = (idx - 1 + items.length) % items.length;
|
|
159
|
+
}
|
|
160
|
+
if (e.key === 'Home')
|
|
161
|
+
nextIdx = 0;
|
|
162
|
+
if (e.key === 'End')
|
|
163
|
+
nextIdx = items.length - 1;
|
|
164
|
+
if (nextIdx !== idx) {
|
|
165
|
+
items[nextIdx].focus();
|
|
166
|
+
items.forEach((item, i) => item.setAttribute('tabindex', i === nextIdx ? '0' : '-1'));
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
})();
|
|
172
|
+
/**
|
|
173
|
+
* NavigationList
|
|
174
|
+
*
|
|
175
|
+
* A semantic, accessible navigation container for `ui-navigation-item` elements.
|
|
176
|
+
* Handles keyboard navigation (roving tabindex), focus management, and ARIA roles.
|
|
177
|
+
*
|
|
178
|
+
* ## Usage Example
|
|
179
|
+
*
|
|
180
|
+
* ```html
|
|
181
|
+
* <ui-navigation aria-label="Main navigation" current="page">
|
|
182
|
+
* <ui-navigation-item selected>
|
|
183
|
+
* <span slot="icon" aria-hidden="true">🏠</span>
|
|
184
|
+
* Home
|
|
185
|
+
* </ui-navigation-item>
|
|
186
|
+
* <ui-navigation-item>
|
|
187
|
+
* <span slot="icon" aria-hidden="true">🔍</span>
|
|
188
|
+
* Search
|
|
189
|
+
* </ui-navigation-item>
|
|
190
|
+
* <ui-navigation-item>
|
|
191
|
+
* <span slot="icon" aria-hidden="true">📁</span>
|
|
192
|
+
* Files
|
|
193
|
+
* </ui-navigation-item>
|
|
194
|
+
* <ui-navigation-item iconOnly disabled aria-disabled="true" tabindex="-1">
|
|
195
|
+
* <span slot="icon" aria-hidden="true">⚙️</span>
|
|
196
|
+
* </ui-navigation-item>
|
|
197
|
+
* </ui-navigation>
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* @slot - Place `ui-navigation-item` elements as direct children.
|
|
201
|
+
* @fires select - Dispatched when an item is selected via click or keyboard.
|
|
202
|
+
* The `detail` object contains the selected `item`.
|
|
203
|
+
*/
|
|
204
|
+
export default Navigation;
|
|
205
|
+
//# sourceMappingURL=Navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Navigation.js","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/Navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, LitElement, type TemplateResult } from 'lit'\nimport { property, queryAssignedElements } from 'lit/decorators.js'\nimport type NavigationItem from './NavigationItem.js'\nimport { AriaCurrent } from '../../../types/aria.js'\n\n/**\n * NavigationList\n *\n * A semantic, accessible navigation container for `ui-navigation-item` elements.\n * Handles keyboard navigation (roving tabindex), focus management, and ARIA roles.\n *\n * ## Usage Example\n *\n * ```html\n * <ui-navigation aria-label=\"Main navigation\" current=\"page\">\n * <ui-navigation-item selected>\n * <span slot=\"icon\" aria-hidden=\"true\">🏠</span>\n * Home\n * </ui-navigation-item>\n * <ui-navigation-item>\n * <span slot=\"icon\" aria-hidden=\"true\">🔍</span>\n * Search\n * </ui-navigation-item>\n * <ui-navigation-item>\n * <span slot=\"icon\" aria-hidden=\"true\">📁</span>\n * Files\n * </ui-navigation-item>\n * <ui-navigation-item iconOnly disabled aria-disabled=\"true\" tabindex=\"-1\">\n * <span slot=\"icon\" aria-hidden=\"true\">⚙️</span>\n * </ui-navigation-item>\n * </ui-navigation>\n * ```\n *\n * @slot - Place `ui-navigation-item` elements as direct children.\n * @fires select - Dispatched when an item is selected via click or keyboard.\n * The `detail` object contains the selected `item`.\n */\nexport default class Navigation extends LitElement {\n /**\n * Orientation of the navigation: 'horizontal' or 'vertical'.\n * @attribute\n */\n @property({ type: String, reflect: true }) accessor orientation: 'horizontal' | 'vertical' = 'vertical'\n\n /**\n * The current item in the navigation, used for ARIA attributes.\n * Can be 'page', 'step', 'location', 'date', 'time', 'true', 'false', or ''.\n * @attribute\n */\n @property({ type: String, reflect: true }) accessor current: AriaCurrent = ''\n\n /**\n * Query all ui-navigation-item children\n */\n @queryAssignedElements({ flatten: true }) accessor _items!: NavigationItem[]\n\n override render(): TemplateResult {\n return html`\n <nav aria-label=\"${this.getAttribute('aria-label') || 'Navigation'}\">\n <slot @slotchange=\"${this._onSlotChange}\"></slot>\n </nav>\n `\n }\n\n constructor() {\n super()\n this.addEventListener('keydown', this._onKeyDown.bind(this))\n this.addEventListener('click', this._onClick.bind(this))\n }\n\n override firstUpdated() {\n this._setTabIndexes()\n }\n\n private _onSlotChange = () => {\n this._setTabIndexes()\n }\n\n /**\n * Sets the initial `tabindex` for the items. The first selected item,\n * or the first item if none are selected, receives `tabindex=\"0\"`.\n * All other items receive `tabindex=\"-1\"`.\n */\n private _setTabIndexes() {\n const items = this._getEnabledItems()\n if (!items.length) {\n return\n }\n items.forEach((item) => {\n if (item.disabled) {\n return\n }\n item.setAttribute('tabindex', item.selected ? '0' : '-1')\n })\n }\n\n private _getEnabledItems(): NavigationItem[] {\n return (this._items || []).filter((item) => !item.disabled)\n }\n\n private _onClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n const item = target.closest('ui-navigation-item')\n if (item && this._items.includes(item)) {\n this._selectItem(item)\n }\n }\n\n private _selectItem(itemToSelect: NavigationItem) {\n if (itemToSelect.disabled) {\n return\n }\n\n this._items.forEach((item) => {\n item.selected = item === itemToSelect\n if (item.hasAttribute('aria-current')) {\n item.removeAttribute('aria-current')\n }\n })\n\n if (this.current) {\n itemToSelect.setAttribute('aria-current', this.current)\n }\n this._updateFocus(itemToSelect)\n\n this.dispatchEvent(\n new CustomEvent('select', {\n detail: { item: itemToSelect },\n bubbles: false,\n composed: true,\n })\n )\n }\n\n private _updateFocus(itemToFocus: NavigationItem) {\n this._setTabIndexes()\n itemToFocus.focus()\n }\n\n private _onKeyDown(e: KeyboardEvent) {\n const items = this._getEnabledItems()\n if (!items.length) {\n return\n }\n\n const activeElement = (this.getRootNode() as Document | ShadowRoot).activeElement\n if (!activeElement) {\n return\n }\n\n // Find which of our items is the host of the active element.\n // This is necessary because of focus delegation in ui-navigation-item.\n const activeItem = items.find((item) => item === activeElement || item.shadowRoot?.contains(activeElement))\n if (!activeItem) {\n return\n }\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this._selectItem(activeItem)\n return\n }\n\n const idx = items.indexOf(activeItem)\n let nextIdx = idx\n if (this.orientation === 'horizontal') {\n if (e.key === 'ArrowRight') nextIdx = (idx + 1) % items.length\n if (e.key === 'ArrowLeft') nextIdx = (idx - 1 + items.length) % items.length\n } else {\n if (e.key === 'ArrowDown') nextIdx = (idx + 1) % items.length\n if (e.key === 'ArrowUp') nextIdx = (idx - 1 + items.length) % items.length\n }\n if (e.key === 'Home') nextIdx = 0\n if (e.key === 'End') nextIdx = items.length - 1\n if (nextIdx !== idx) {\n items[nextIdx].focus()\n items.forEach((item, i) => item.setAttribute('tabindex', i === nextIdx ? '0' : '-1'))\n e.preventDefault()\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Navigation.styles.d.ts","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/Navigation.styles.ts"],"names":[],"mappings":";AAEA,wBAqBC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export default css `
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
outline: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
nav {
|
|
9
|
+
display: flex;
|
|
10
|
+
gap: 8px;
|
|
11
|
+
list-style: none;
|
|
12
|
+
padding: 0;
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:host([orientation='horizontal']) nav {
|
|
17
|
+
flex-direction: row;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:host([orientation='vertical']) nav {
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
//# sourceMappingURL=Navigation.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Navigation.styles.js","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/Navigation.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;CAqBjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: block;\n outline: none;\n }\n\n nav {\n display: flex;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n\n :host([orientation='horizontal']) nav {\n flex-direction: row;\n }\n\n :host([orientation='vertical']) nav {\n flex-direction: column;\n }\n`\n"]}
|
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
2
|
import { UiElement } from '../../../md/UiElement.js';
|
|
3
|
+
/**
|
|
4
|
+
* NavigationItem
|
|
5
|
+
*
|
|
6
|
+
* An element to be placed inside a navigation bar. This component is designed for use with navigation bars and supports
|
|
7
|
+
* icon-only, selected, and disabled states. It is built with accessibility and composability in mind.
|
|
8
|
+
*
|
|
9
|
+
* ## Accessibility
|
|
10
|
+
*
|
|
11
|
+
* - Uses a native `<button>` element for proper semantics and keyboard accessibility.
|
|
12
|
+
* - The icon is marked with `role="presentation"` to indicate it is decorative.
|
|
13
|
+
* - Focus ring and ripple effects are included for visual feedback.
|
|
14
|
+
*
|
|
15
|
+
* ## Usage Example
|
|
16
|
+
*
|
|
17
|
+
* ### Accessible Navigation Example
|
|
18
|
+
*
|
|
19
|
+
* ```html
|
|
20
|
+
* <nav aria-label="Main navigation">
|
|
21
|
+
* <ul style="display: flex; gap: 8px; list-style: none; padding: 0; margin: 0;">
|
|
22
|
+
* <li>
|
|
23
|
+
* <navigation-item selected aria-current="page">
|
|
24
|
+
* <span slot="icon" aria-hidden="true">🏠</span>
|
|
25
|
+
* Home
|
|
26
|
+
* </navigation-item>
|
|
27
|
+
* </li>
|
|
28
|
+
* <li>
|
|
29
|
+
* <navigation-item>
|
|
30
|
+
* <span slot="icon" aria-hidden="true">🔍</span>
|
|
31
|
+
* Search
|
|
32
|
+
* </navigation-item>
|
|
33
|
+
* </li>
|
|
34
|
+
* <li>
|
|
35
|
+
* <navigation-item>
|
|
36
|
+
* <span slot="icon" aria-hidden="true">📁</span>
|
|
37
|
+
* Files
|
|
38
|
+
* </navigation-item>
|
|
39
|
+
* </li>
|
|
40
|
+
* <li>
|
|
41
|
+
* <navigation-item iconOnly disabled aria-disabled="true" tabindex="-1">
|
|
42
|
+
* <span slot="icon" aria-hidden="true">⚙️</span>
|
|
43
|
+
* </navigation-item>
|
|
44
|
+
* </li>
|
|
45
|
+
* </ul>
|
|
46
|
+
* </nav>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* This example demonstrates a horizontal navigation bar using semantic HTML. Each `navigation-item`
|
|
50
|
+
* is placed inside a list item for proper structure. The `aria-current="page"` attribute marks the current
|
|
51
|
+
* page, and `aria-disabled="true"` with `tabindex="-1"` ensures the disabled item is not focusable.
|
|
52
|
+
* Icons use `aria-hidden="true"` for accessibility.
|
|
53
|
+
*
|
|
54
|
+
* @slot icon - A slot for the icon element
|
|
55
|
+
* @slot - The default slot for the label
|
|
56
|
+
*/
|
|
3
57
|
export default class NavigationItem extends UiElement {
|
|
4
58
|
static shadowRootOptions: ShadowRootInit;
|
|
59
|
+
get disabled(): boolean;
|
|
5
60
|
/**
|
|
6
|
-
* When set, the
|
|
61
|
+
* When set, the navigation item is in a disabled state.
|
|
7
62
|
* @attribute
|
|
8
63
|
*/
|
|
9
|
-
|
|
64
|
+
set disabled(value: boolean);
|
|
10
65
|
/**
|
|
11
66
|
* Whether the navigation item is selected.
|
|
12
67
|
* @attribute
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationItem.d.ts","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/NavigationItem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,
|
|
1
|
+
{"version":3,"file":"NavigationItem.d.ts","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/NavigationItem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,EAAW,MAAM,KAAK,CAAA;AAInD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,SAAS;IACnD,OAAgB,iBAAiB,EAAE,cAAc,CAGhD;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,IACI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAI1B;IAED;;;OAGG;IACyC,QAAQ,CAAC,QAAQ,UAAQ;IACrE;;;;OAIG;IACyC,QAAQ,CAAC,QAAQ,UAAQ;IAErE;;OAEG;IACM,SAAS,CAAC,QAAQ,CAAC,OAAO,UAAQ;IAE3C;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;cAK3B,MAAM,IAAI,cAAc;IAqB3C,SAAS,CAAC,UAAU,IAAI,cAAc;CAKvC"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
-
import { html } from 'lit';
|
|
2
|
+
import { html, nothing } from 'lit';
|
|
3
3
|
import { property, state } from 'lit/decorators.js';
|
|
4
4
|
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
import { when } from 'lit/directives/when.js';
|
|
5
6
|
import { UiElement } from '../../../md/UiElement.js';
|
|
7
|
+
import { isDisabled, setDisabled } from '../../../lib/disabled.js';
|
|
6
8
|
let NavigationItem = (() => {
|
|
7
9
|
let _classSuper = UiElement;
|
|
8
|
-
let
|
|
9
|
-
let
|
|
10
|
-
let _disabled_extraInitializers = [];
|
|
10
|
+
let _instanceExtraInitializers = [];
|
|
11
|
+
let _set_disabled_decorators;
|
|
11
12
|
let _selected_decorators;
|
|
12
13
|
let _selected_initializers = [];
|
|
13
14
|
let _selected_extraInitializers = [];
|
|
@@ -20,11 +21,11 @@ let NavigationItem = (() => {
|
|
|
20
21
|
return class NavigationItem extends _classSuper {
|
|
21
22
|
static {
|
|
22
23
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
23
|
-
|
|
24
|
+
_set_disabled_decorators = [property({ reflect: true, type: Boolean })];
|
|
24
25
|
_selected_decorators = [property({ reflect: true, type: Boolean })];
|
|
25
26
|
_iconOnly_decorators = [property({ reflect: true, type: Boolean })];
|
|
26
27
|
_hasIcon_decorators = [state()];
|
|
27
|
-
__esDecorate(this, null,
|
|
28
|
+
__esDecorate(this, null, _set_disabled_decorators, { kind: "setter", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
28
29
|
__esDecorate(this, null, _selected_decorators, { kind: "accessor", name: "selected", static: false, private: false, access: { has: obj => "selected" in obj, get: obj => obj.selected, set: (obj, value) => { obj.selected = value; } }, metadata: _metadata }, _selected_initializers, _selected_extraInitializers);
|
|
29
30
|
__esDecorate(this, null, _iconOnly_decorators, { kind: "accessor", name: "iconOnly", static: false, private: false, access: { has: obj => "iconOnly" in obj, get: obj => obj.iconOnly, set: (obj, value) => { obj.iconOnly = value; } }, metadata: _metadata }, _iconOnly_initializers, _iconOnly_extraInitializers);
|
|
30
31
|
__esDecorate(this, null, _hasIcon_decorators, { kind: "accessor", name: "hasIcon", static: false, private: false, access: { has: obj => "hasIcon" in obj, get: obj => obj.hasIcon, set: (obj, value) => { obj.hasIcon = value; } }, metadata: _metadata }, _hasIcon_initializers, _hasIcon_extraInitializers);
|
|
@@ -34,19 +35,19 @@ let NavigationItem = (() => {
|
|
|
34
35
|
mode: 'open',
|
|
35
36
|
delegatesFocus: true,
|
|
36
37
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @attribute
|
|
41
|
-
*/
|
|
42
|
-
);
|
|
38
|
+
get disabled() {
|
|
39
|
+
return isDisabled(this);
|
|
40
|
+
}
|
|
43
41
|
/**
|
|
44
|
-
* When set, the
|
|
42
|
+
* When set, the navigation item is in a disabled state.
|
|
45
43
|
* @attribute
|
|
46
44
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
set disabled(value) {
|
|
46
|
+
const old = isDisabled(this);
|
|
47
|
+
setDisabled(this, value);
|
|
48
|
+
this.requestUpdate('disabled', old);
|
|
49
|
+
}
|
|
50
|
+
#selected_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _selected_initializers, false
|
|
50
51
|
/**
|
|
51
52
|
* When set, the navigation item is rendered as an icon-only button.
|
|
52
53
|
* The width of the button is set to 40px.
|
|
@@ -97,10 +98,10 @@ let NavigationItem = (() => {
|
|
|
97
98
|
'icon-only': this.iconOnly,
|
|
98
99
|
});
|
|
99
100
|
return html `
|
|
100
|
-
<button class="${containerClasses}" id="button">
|
|
101
|
+
<button class="${containerClasses}" id="button" ?disabled="${this.disabled}" aria-disabled="${this.disabled}">
|
|
101
102
|
<md-focus-ring part="focus-ring" for="button"></md-focus-ring>
|
|
102
103
|
<md-ripple></md-ripple>
|
|
103
|
-
${this.renderIcon()}${this.iconOnly
|
|
104
|
+
${this.renderIcon()}${when(this.iconOnly, () => nothing, () => html `<slot></slot>`)}
|
|
104
105
|
</button>
|
|
105
106
|
`;
|
|
106
107
|
}
|
|
@@ -115,5 +116,59 @@ let NavigationItem = (() => {
|
|
|
115
116
|
}
|
|
116
117
|
};
|
|
117
118
|
})();
|
|
119
|
+
/**
|
|
120
|
+
* NavigationItem
|
|
121
|
+
*
|
|
122
|
+
* An element to be placed inside a navigation bar. This component is designed for use with navigation bars and supports
|
|
123
|
+
* icon-only, selected, and disabled states. It is built with accessibility and composability in mind.
|
|
124
|
+
*
|
|
125
|
+
* ## Accessibility
|
|
126
|
+
*
|
|
127
|
+
* - Uses a native `<button>` element for proper semantics and keyboard accessibility.
|
|
128
|
+
* - The icon is marked with `role="presentation"` to indicate it is decorative.
|
|
129
|
+
* - Focus ring and ripple effects are included for visual feedback.
|
|
130
|
+
*
|
|
131
|
+
* ## Usage Example
|
|
132
|
+
*
|
|
133
|
+
* ### Accessible Navigation Example
|
|
134
|
+
*
|
|
135
|
+
* ```html
|
|
136
|
+
* <nav aria-label="Main navigation">
|
|
137
|
+
* <ul style="display: flex; gap: 8px; list-style: none; padding: 0; margin: 0;">
|
|
138
|
+
* <li>
|
|
139
|
+
* <navigation-item selected aria-current="page">
|
|
140
|
+
* <span slot="icon" aria-hidden="true">🏠</span>
|
|
141
|
+
* Home
|
|
142
|
+
* </navigation-item>
|
|
143
|
+
* </li>
|
|
144
|
+
* <li>
|
|
145
|
+
* <navigation-item>
|
|
146
|
+
* <span slot="icon" aria-hidden="true">🔍</span>
|
|
147
|
+
* Search
|
|
148
|
+
* </navigation-item>
|
|
149
|
+
* </li>
|
|
150
|
+
* <li>
|
|
151
|
+
* <navigation-item>
|
|
152
|
+
* <span slot="icon" aria-hidden="true">📁</span>
|
|
153
|
+
* Files
|
|
154
|
+
* </navigation-item>
|
|
155
|
+
* </li>
|
|
156
|
+
* <li>
|
|
157
|
+
* <navigation-item iconOnly disabled aria-disabled="true" tabindex="-1">
|
|
158
|
+
* <span slot="icon" aria-hidden="true">⚙️</span>
|
|
159
|
+
* </navigation-item>
|
|
160
|
+
* </li>
|
|
161
|
+
* </ul>
|
|
162
|
+
* </nav>
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* This example demonstrates a horizontal navigation bar using semantic HTML. Each `navigation-item`
|
|
166
|
+
* is placed inside a list item for proper structure. The `aria-current="page"` attribute marks the current
|
|
167
|
+
* page, and `aria-disabled="true"` with `tabindex="-1"` ensures the disabled item is not focusable.
|
|
168
|
+
* Icons use `aria-hidden="true"` for accessibility.
|
|
169
|
+
*
|
|
170
|
+
* @slot icon - A slot for the icon element
|
|
171
|
+
* @slot - The default slot for the label
|
|
172
|
+
*/
|
|
118
173
|
export default NavigationItem;
|
|
119
174
|
//# sourceMappingURL=NavigationItem.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationItem.js","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/NavigationItem.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"NavigationItem.js","sourceRoot":"","sources":["../../../../../src/elements/navigation/internals/NavigationItem.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult, nothing } from 'lit'\nimport { property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { when } from 'lit/directives/when.js'\nimport { UiElement } from '../../../md/UiElement.js'\nimport { isDisabled, setDisabled } from '../../../lib/disabled.js'\n\n/**\n * NavigationItem\n *\n * An element to be placed inside a navigation bar. This component is designed for use with navigation bars and supports\n * icon-only, selected, and disabled states. It is built with accessibility and composability in mind.\n *\n * ## Accessibility\n *\n * - Uses a native `<button>` element for proper semantics and keyboard accessibility.\n * - The icon is marked with `role=\"presentation\"` to indicate it is decorative.\n * - Focus ring and ripple effects are included for visual feedback.\n *\n * ## Usage Example\n *\n * ### Accessible Navigation Example\n *\n * ```html\n * <nav aria-label=\"Main navigation\">\n * <ul style=\"display: flex; gap: 8px; list-style: none; padding: 0; margin: 0;\">\n * <li>\n * <navigation-item selected aria-current=\"page\">\n * <span slot=\"icon\" aria-hidden=\"true\">🏠</span>\n * Home\n * </navigation-item>\n * </li>\n * <li>\n * <navigation-item>\n * <span slot=\"icon\" aria-hidden=\"true\">🔍</span>\n * Search\n * </navigation-item>\n * </li>\n * <li>\n * <navigation-item>\n * <span slot=\"icon\" aria-hidden=\"true\">📁</span>\n * Files\n * </navigation-item>\n * </li>\n * <li>\n * <navigation-item iconOnly disabled aria-disabled=\"true\" tabindex=\"-1\">\n * <span slot=\"icon\" aria-hidden=\"true\">⚙️</span>\n * </navigation-item>\n * </li>\n * </ul>\n * </nav>\n * ```\n *\n * This example demonstrates a horizontal navigation bar using semantic HTML. Each `navigation-item`\n * is placed inside a list item for proper structure. The `aria-current=\"page\"` attribute marks the current\n * page, and `aria-disabled=\"true\"` with `tabindex=\"-1\"` ensures the disabled item is not focusable.\n * Icons use `aria-hidden=\"true\"` for accessibility.\n *\n * @slot icon - A slot for the icon element\n * @slot - The default slot for the label\n */\nexport default class NavigationItem extends UiElement {\n static override shadowRootOptions: ShadowRootInit = {\n mode: 'open',\n delegatesFocus: true,\n }\n\n get disabled(): boolean {\n return isDisabled(this)\n }\n\n /**\n * When set, the navigation item is in a disabled state.\n * @attribute\n */\n @property({ reflect: true, type: Boolean })\n set disabled(value: boolean) {\n const old = isDisabled(this)\n setDisabled(this, value)\n this.requestUpdate('disabled', old)\n }\n\n /**\n * Whether the navigation item is selected.\n * @attribute\n */\n @property({ reflect: true, type: Boolean }) accessor selected = false\n /**\n * When set, the navigation item is rendered as an icon-only button.\n * The width of the button is set to 40px.\n * @attribute\n */\n @property({ reflect: true, type: Boolean }) accessor iconOnly = false\n\n /**\n * Determines when the element has an icon in the \"icon\" slot.\n */\n @state() protected accessor hasIcon = false\n\n /**\n * Sets the `_hasIcon` state property when the \"icon\" slot change event is dispatched.\n */\n protected handleIconSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement\n this.hasIcon = !!slot.assignedNodes().length\n }\n\n protected override render(): TemplateResult {\n const { pressed = false } = this\n const containerClasses = classMap({\n 'button': true,\n 'with-icon': this.hasIcon,\n 'pressed': pressed,\n 'icon-only': this.iconOnly,\n })\n return html`\n <button class=\"${containerClasses}\" id=\"button\" ?disabled=\"${this.disabled}\" aria-disabled=\"${this.disabled}\">\n <md-focus-ring part=\"focus-ring\" for=\"button\"></md-focus-ring>\n <md-ripple></md-ripple>\n ${this.renderIcon()}${when(\n this.iconOnly,\n () => nothing,\n () => html`<slot></slot>`\n )}\n </button>\n `\n }\n\n protected renderIcon(): TemplateResult {\n return html`<span role=\"presentation\" class=\"icon\"\n ><slot name=\"icon\" @slotchange=\"${this.handleIconSlotChange}\"></slot\n ></span>`\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CSSResultOrNative } from 'lit';
|
|
2
|
+
import Element from './internals/Navigation.js';
|
|
3
|
+
export declare class NavigationListElement extends Element {
|
|
4
|
+
static styles: CSSResultOrNative[];
|
|
5
|
+
}
|
|
6
|
+
declare global {
|
|
7
|
+
interface HTMLElementTagNameMap {
|
|
8
|
+
'ui-navigation': NavigationListElement;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ui-navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-navigation.d.ts","sourceRoot":"","sources":["../../../../src/elements/navigation/ui-navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,KAAK,CAAA;AAE5C,OAAO,OAAO,MAAM,2BAA2B,CAAA;AAG/C,qBACa,qBAAsB,SAAQ,OAAO;IAChD,OAAgB,MAAM,EAAE,iBAAiB,EAAE,CAAW;CACvD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,qBAAqB,CAAA;KACvC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { customElement } from 'lit/decorators.js';
|
|
3
|
+
import Element from './internals/Navigation.js';
|
|
4
|
+
import styles from './internals/Navigation.styles.js';
|
|
5
|
+
let NavigationListElement = (() => {
|
|
6
|
+
let _classDecorators = [customElement('ui-navigation')];
|
|
7
|
+
let _classDescriptor;
|
|
8
|
+
let _classExtraInitializers = [];
|
|
9
|
+
let _classThis;
|
|
10
|
+
let _classSuper = Element;
|
|
11
|
+
var NavigationListElement = class extends _classSuper {
|
|
12
|
+
static { _classThis = this; }
|
|
13
|
+
static {
|
|
14
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
15
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
16
|
+
NavigationListElement = _classThis = _classDescriptor.value;
|
|
17
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
18
|
+
}
|
|
19
|
+
static styles = [styles];
|
|
20
|
+
static {
|
|
21
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return NavigationListElement = _classThis;
|
|
25
|
+
})();
|
|
26
|
+
export { NavigationListElement };
|
|
27
|
+
//# sourceMappingURL=ui-navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-navigation.js","sourceRoot":"","sources":["../../../../src/elements/navigation/ui-navigation.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,OAAO,MAAM,2BAA2B,CAAA;AAC/C,OAAO,MAAM,MAAM,kCAAkC,CAAA;IAGxC,qBAAqB;4BADjC,aAAa,CAAC,eAAe,CAAC;;;;sBACY,OAAO;qCAAf,SAAQ,WAAO;;;;YAAlD,6KAEC;;;;QADC,MAAM,CAAU,MAAM,GAAwB,CAAC,MAAM,CAAC,CAAA;;YAD3C,uDAAqB;;;;;SAArB,qBAAqB","sourcesContent":["import type { CSSResultOrNative } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport Element from './internals/Navigation.js'\nimport styles from './internals/Navigation.styles.js'\n\n@customElement('ui-navigation')\nexport class NavigationListElement extends Element {\n static override styles: CSSResultOrNative[] = [styles]\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ui-navigation': NavigationListElement\n }\n}\n"]}
|
|
@@ -15,6 +15,8 @@ export default abstract class Input extends UiElement {
|
|
|
15
15
|
mode: ShadowRootMode;
|
|
16
16
|
serializable?: boolean;
|
|
17
17
|
slotAssignment?: SlotAssignmentMode;
|
|
18
|
+
customElements?: CustomElementRegistry;
|
|
19
|
+
registry?: CustomElementRegistry;
|
|
18
20
|
};
|
|
19
21
|
static readonly formAssociated = true;
|
|
20
22
|
get form(): HTMLFormElement | null;
|