@vollowx/seele 0.12.7 → 0.12.9
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/README.md +2 -1
- package/package.json +4 -4
- package/src/all.js +4 -0
- package/src/base/list-item.js +2 -1
- package/src/base/list.js +10 -11
- package/src/base/menu.js +2 -3
- package/src/base/select.js +54 -54
- package/src/base/tab-panel.js +25 -0
- package/src/base/tab.js +59 -0
- package/src/base/tabs.js +144 -0
- package/src/base/toggle-button.js +3 -3
- package/src/base/tooltip.js +3 -3
- package/src/m3/field/field.js +6 -6
- package/src/m3/focus-ring/focus-ring.js +16 -19
- package/src/m3/list/list-item.js +17 -17
- package/src/m3/list/list.js +3 -7
- package/src/m3/menu/menu.js +4 -9
- package/src/m3/select/filled-select.js +1 -1
- package/src/m3/select/outlined-select.js +1 -1
- package/src/m3/select/select.js +3 -3
- package/src/m3/slider/slider.js +6 -6
- package/src/m3/tab/tab-panel-styles.css.js +2 -0
- package/src/m3/tab/tab-panel.js +14 -0
- package/src/m3/tab/tab-styles.css.js +2 -0
- package/src/m3/tab/tab.js +33 -0
- package/src/m3/tab/tabs-styles.css.js +2 -0
- package/src/m3/tab/tabs.js +48 -0
- package/src/m3/toolbar/toolbar.js +10 -10
- package/src/base/tab-group.js +0 -3
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@vollowx/seele)
|
|
4
4
|
[](https://builds.sr.ht/~lucaz/seele?)
|
|
5
|
+
[](https://www.webcomponents.org/element/@vollowx/seele)
|
|
5
6
|
|
|
6
7
|
Seele (**S**tandard **E**xtensible **Ele**ments) is a extensible
|
|
7
8
|
[Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
@@ -11,7 +12,7 @@ It also provides styled components in the following design guideline(s):
|
|
|
11
12
|
|
|
12
13
|
- [Material You Expressive](https://m3.material.io/)
|
|
13
14
|
|
|
14
|
-
Documentations and demos are at [seele
|
|
15
|
+
Documentations and demos are at [tideover.cc/seele/](https://tideover.cc/seele/).
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vollowx/seele",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.9",
|
|
4
4
|
"description": "A extensible Web Components library, with Material Design 3 components pre-provided.",
|
|
5
5
|
"author": "vollowx",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"lit",
|
|
28
28
|
"ui-components"
|
|
29
29
|
],
|
|
30
|
-
"homepage": "https://seele
|
|
30
|
+
"homepage": "https://tideover.cc/seele/",
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
33
33
|
"url": "https://github.com/vollowx/seele.git"
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
49
|
-
"@swc/core": "^1.15.
|
|
49
|
+
"@swc/core": "^1.15.43",
|
|
50
50
|
"@types/bun": "^1.3.14",
|
|
51
51
|
"@web/dev-server": "^0.4.6",
|
|
52
52
|
"@web/dev-server-esbuild": "^1.0.5",
|
|
53
53
|
"chokidar": "^5.0.0",
|
|
54
54
|
"lightningcss": "^1.32.0",
|
|
55
|
-
"prettier": "^3.
|
|
55
|
+
"prettier": "^3.9.1"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@floating-ui/dom": "^1.7.6",
|
package/src/all.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// TODO: export core, base elements, mixins and controllers
|
|
1
2
|
export { M3Autocomplete } from './m3/autocomplete/autocomplete.js';
|
|
2
3
|
export { M3Button } from './m3/button/common-button.js';
|
|
3
4
|
export { M3ButtonToggle } from './m3/button/common-button-toggle.js';
|
|
@@ -18,6 +19,9 @@ export { MdOutlinedSelect } from './m3/select/outlined-select.js';
|
|
|
18
19
|
export { M3Option } from './m3/select/option.js';
|
|
19
20
|
export { M3Slider } from './m3/slider/slider.js';
|
|
20
21
|
export { M3Switch } from './m3/switch/switch.js';
|
|
22
|
+
export { M3Tabs } from './m3/tab/tabs.js';
|
|
23
|
+
export { M3Tab } from './m3/tab/tab.js';
|
|
24
|
+
export { M3TabPanel } from './m3/tab/tab-panel.js';
|
|
21
25
|
export { M3FilledTextField } from './m3/text-field/filled-text-field.js';
|
|
22
26
|
export { M3OutlinedTextField } from './m3/text-field/outlined-text-field.js';
|
|
23
27
|
export { M3Toolbar } from './m3/toolbar/toolbar.js';
|
package/src/base/list-item.js
CHANGED
|
@@ -16,6 +16,7 @@ export class ListItem extends FormAssociated(InternalsAttached(LitElement)) {
|
|
|
16
16
|
}
|
|
17
17
|
connectedCallback() {
|
|
18
18
|
super.connectedCallback();
|
|
19
|
+
this.setAttribute('seele-base', 'option');
|
|
19
20
|
this[internals].role = this._role;
|
|
20
21
|
this.setAttribute('tabindex', '-1');
|
|
21
22
|
this.#updateInternals();
|
|
@@ -40,7 +41,7 @@ export class ListItem extends FormAssociated(InternalsAttached(LitElement)) {
|
|
|
40
41
|
this.focused = false;
|
|
41
42
|
}
|
|
42
43
|
constructor(...args){
|
|
43
|
-
super(...args), this.
|
|
44
|
+
super(...args), this._role = 'option', this.selected = false, this.focused = false;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
_ts_decorate([
|
package/src/base/list.js
CHANGED
|
@@ -16,9 +16,15 @@ const Base = InternalsAttached(LitElement);
|
|
|
16
16
|
get $items() {
|
|
17
17
|
return this.listController.items || [];
|
|
18
18
|
}
|
|
19
|
+
render() {
|
|
20
|
+
return html` ${this.renderItemSlot()} `;
|
|
21
|
+
}
|
|
22
|
+
renderItemSlot() {
|
|
23
|
+
return html`<slot part="items"></slot>`;
|
|
24
|
+
}
|
|
19
25
|
constructor(){
|
|
20
|
-
super(), this.
|
|
21
|
-
isItem: (item)=>
|
|
26
|
+
super(), this._scrollPadding = 0, this.noFocusControl = false, this.listController = new ListController(this, {
|
|
27
|
+
isItem: (item)=>item.getAttribute('seele-base') === 'option' && !item.hasAttribute('disabled') && !item.hidden,
|
|
22
28
|
getPossibleItems: ()=>this.slotItems,
|
|
23
29
|
blurItem: (item)=>{
|
|
24
30
|
item.focused = false;
|
|
@@ -52,12 +58,6 @@ const Base = InternalsAttached(LitElement);
|
|
|
52
58
|
this.addEventListener('mouseover', (e)=>this.#handleMouseOver(e));
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
|
-
render() {
|
|
56
|
-
return html` ${this.renderItemSlot()} `;
|
|
57
|
-
}
|
|
58
|
-
renderItemSlot() {
|
|
59
|
-
return html`<slot part="items"></slot>`;
|
|
60
|
-
}
|
|
61
61
|
updated(changed) {
|
|
62
62
|
// TODO: Find somewhere to put this
|
|
63
63
|
// this.listController.clearSearch();
|
|
@@ -107,7 +107,7 @@ const Base = InternalsAttached(LitElement);
|
|
|
107
107
|
}
|
|
108
108
|
#handleMouseOver(event) {
|
|
109
109
|
setFocusVisible(false);
|
|
110
|
-
const item = event.target.closest(
|
|
110
|
+
const item = event.target.closest('[seele-base="option"]');
|
|
111
111
|
if (item && this.listController.items.includes(item)) {
|
|
112
112
|
this.listController._focusItem(item);
|
|
113
113
|
}
|
|
@@ -133,8 +133,7 @@ const Base = InternalsAttached(LitElement);
|
|
|
133
133
|
}));
|
|
134
134
|
}
|
|
135
135
|
#getEventItem(event) {
|
|
136
|
-
|
|
137
|
-
return event.target.closest(selector);
|
|
136
|
+
return event.target.closest('[seele-base="option"]');
|
|
138
137
|
}
|
|
139
138
|
get currentIndex() {
|
|
140
139
|
return this.listController?.currentIndex;
|
package/src/base/menu.js
CHANGED
|
@@ -187,7 +187,7 @@ const Base = FocusDelegated(InternalsAttached(Attachable(LitElement)));
|
|
|
187
187
|
this.open = false;
|
|
188
188
|
}
|
|
189
189
|
constructor(...args){
|
|
190
|
-
super(...args), this.
|
|
190
|
+
super(...args), this._durations = {
|
|
191
191
|
show: 0,
|
|
192
192
|
hide: 0
|
|
193
193
|
}, this._scrollPadding = 0, this.type = 'menu', this.open = false, this.quick = false, this.offset = 0, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.keepOpenBlur = false, this.keepOpenSelect = false, this.keepOpenClickAway = false, this.noFocusControl = false, this.tabIndex = 0, this.$lastFocused = null, this.popoverController = new PopoverController(this, {
|
|
@@ -207,10 +207,9 @@ const Base = FocusDelegated(InternalsAttached(Attachable(LitElement)));
|
|
|
207
207
|
if (!this.keepOpenClickAway) this.open = false;
|
|
208
208
|
}
|
|
209
209
|
}), this.listController = new ListController(this, {
|
|
210
|
-
isItem: (item)=>
|
|
210
|
+
isItem: (item)=>item.getAttribute('seele-base') === 'option' && !item.hasAttribute('disabled') && !item.hidden,
|
|
211
211
|
getPossibleItems: ()=>this.slotItems,
|
|
212
212
|
blurItem: (item)=>{
|
|
213
|
-
console.log(item);
|
|
214
213
|
item.focused = false;
|
|
215
214
|
},
|
|
216
215
|
focusItem: (item)=>{
|
package/src/base/select.js
CHANGED
|
@@ -42,55 +42,6 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
42
42
|
get selectedOptions() {
|
|
43
43
|
return (this.getSelectedOptions() ?? []).map(([option])=>option);
|
|
44
44
|
}
|
|
45
|
-
constructor(){
|
|
46
|
-
super(), // Form-related
|
|
47
|
-
this.required = false, // Passed to field
|
|
48
|
-
this.error = false, // Passed to menu
|
|
49
|
-
this.quick = false, this.offset = 0, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.open = false, /**
|
|
50
|
-
* Text to display in the field. Only set for SSR.
|
|
51
|
-
*/ this.displayText = '', this[VALUE] = '', this.lastUserSetValue = null, this.lastUserSetSelectedIndex = null, this.lastSelectedOption = null, this.lastSelectedOptionRecords = [], this.#dispatchChangeEvent = ()=>{
|
|
52
|
-
this.dispatchEvent(new Event('input', {
|
|
53
|
-
bubbles: true,
|
|
54
|
-
composed: true
|
|
55
|
-
}));
|
|
56
|
-
this.dispatchEvent(new Event('change', {
|
|
57
|
-
bubbles: true
|
|
58
|
-
}));
|
|
59
|
-
};
|
|
60
|
-
if (!isServer) {
|
|
61
|
-
this.addEventListener('focusout', this.handleFocusOut);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async firstUpdated(changed) {
|
|
65
|
-
// If this has been handled on update already due to SSR, try again.
|
|
66
|
-
if (!this.lastSelectedOptionRecords.length) {
|
|
67
|
-
this.initUserSelection();
|
|
68
|
-
}
|
|
69
|
-
// Case for when the DOM is streaming, there are no children, and a child
|
|
70
|
-
// has [selected] set on it, we need to wait for DOM to render something.
|
|
71
|
-
if (!this.lastSelectedOptionRecords.length && !isServer && !this.$options.length) {
|
|
72
|
-
setTimeout(()=>{
|
|
73
|
-
this.updateValueAndDisplayText();
|
|
74
|
-
}, 0);
|
|
75
|
-
}
|
|
76
|
-
super.firstUpdated(changed);
|
|
77
|
-
}
|
|
78
|
-
update(changed) {
|
|
79
|
-
if (!this.hasUpdated) {
|
|
80
|
-
this.initUserSelection();
|
|
81
|
-
}
|
|
82
|
-
super.update(changed);
|
|
83
|
-
}
|
|
84
|
-
updated(changed) {
|
|
85
|
-
super.updated(changed);
|
|
86
|
-
if (changed.has('open')) {
|
|
87
|
-
if (this.open) {
|
|
88
|
-
this.#focusSelectedItemOrFirst();
|
|
89
|
-
} else {
|
|
90
|
-
this.$field.setAttribute('aria-activedescendant', '');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
45
|
render() {
|
|
95
46
|
return html`${this.renderField()}${this.renderMenu()}`;
|
|
96
47
|
}
|
|
@@ -145,31 +96,80 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
145
96
|
renderFieldContent() {
|
|
146
97
|
return html`<span part="value">${this.displayText || html` `}</span>`;
|
|
147
98
|
}
|
|
99
|
+
constructor(){
|
|
100
|
+
super(), // Form-related
|
|
101
|
+
this.required = false, // Passed to field
|
|
102
|
+
this.error = false, // Passed to menu
|
|
103
|
+
this.quick = false, this.offset = 0, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.open = false, /**
|
|
104
|
+
* Text to display in the field. Only set for SSR.
|
|
105
|
+
*/ this.displayText = '', this[VALUE] = '', this.lastUserSetValue = null, this.lastUserSetSelectedIndex = null, this.lastSelectedOption = null, this.lastSelectedOptionRecords = [], this.#dispatchChangeEvent = ()=>{
|
|
106
|
+
this.dispatchEvent(new Event('input', {
|
|
107
|
+
bubbles: true,
|
|
108
|
+
composed: true
|
|
109
|
+
}));
|
|
110
|
+
this.dispatchEvent(new Event('change', {
|
|
111
|
+
bubbles: true
|
|
112
|
+
}));
|
|
113
|
+
};
|
|
114
|
+
if (!isServer) {
|
|
115
|
+
this.addEventListener('focusout', this._handleFocusOut);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async firstUpdated(changed) {
|
|
119
|
+
// If this has been handled on update already due to SSR, try again.
|
|
120
|
+
if (!this.lastSelectedOptionRecords.length) {
|
|
121
|
+
this.initUserSelection();
|
|
122
|
+
}
|
|
123
|
+
// Case for when the DOM is streaming, there are no children, and a child
|
|
124
|
+
// has [selected] set on it, we need to wait for DOM to render something.
|
|
125
|
+
if (!this.lastSelectedOptionRecords.length && !isServer && !this.$options.length) {
|
|
126
|
+
setTimeout(()=>{
|
|
127
|
+
this.updateValueAndDisplayText();
|
|
128
|
+
}, 0);
|
|
129
|
+
}
|
|
130
|
+
super.firstUpdated(changed);
|
|
131
|
+
}
|
|
132
|
+
update(changed) {
|
|
133
|
+
if (!this.hasUpdated) {
|
|
134
|
+
this.initUserSelection();
|
|
135
|
+
}
|
|
136
|
+
super.update(changed);
|
|
137
|
+
}
|
|
138
|
+
updated(changed) {
|
|
139
|
+
super.updated(changed);
|
|
140
|
+
if (changed.has('open')) {
|
|
141
|
+
if (this.open) {
|
|
142
|
+
this.#focusSelectedItemOrFirst();
|
|
143
|
+
} else {
|
|
144
|
+
this.$field.setAttribute('aria-activedescendant', '');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
148
|
/**
|
|
149
149
|
* TODO: Handle clear action
|
|
150
|
-
*/
|
|
150
|
+
*/ _handleFieldKeydown(event) {
|
|
151
151
|
if (this.disabled) return;
|
|
152
152
|
const eventClone = new KeyboardEvent(event.type, event);
|
|
153
153
|
eventClone.preventDefault = ()=>event.preventDefault();
|
|
154
154
|
eventClone.stopPropagation = ()=>event.stopPropagation();
|
|
155
155
|
this.$menu.$menu.dispatchEvent(eventClone);
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
_handleMenuSelect(event) {
|
|
158
158
|
if (this.selectItem(event.detail.item)) {
|
|
159
159
|
this.#dispatchChangeEvent();
|
|
160
160
|
}
|
|
161
161
|
this.open = false;
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
_handleMenuItemFocus(event) {
|
|
164
164
|
this.$field.setAttribute('aria-activedescendant', event.detail.item.id);
|
|
165
165
|
}
|
|
166
|
-
|
|
166
|
+
_handleFocusOut(event) {
|
|
167
167
|
const relatedTarget = event.relatedTarget;
|
|
168
168
|
if (!this.contains(relatedTarget) && !this.$menu.contains(relatedTarget)) {
|
|
169
169
|
this.open = false;
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
|
|
172
|
+
_handleSlotChange() {
|
|
173
173
|
// When slots change, check for initially selected items if value is not set
|
|
174
174
|
if (!this.value) {
|
|
175
175
|
this.updateValueAndDisplayText();
|
package/src/base/tab-panel.js
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { InternalsAttached, internals } from './mixins/internals-attached.js';
|
|
5
|
+
import { genUniqueId } from '../core/unique-id.js';
|
|
6
|
+
export class TabPanel extends InternalsAttached(LitElement) {
|
|
7
|
+
render() {
|
|
8
|
+
return html`<slot></slot>`;
|
|
9
|
+
}
|
|
10
|
+
connectedCallback() {
|
|
11
|
+
super.connectedCallback();
|
|
12
|
+
this.setAttribute('seele-base', 'tabpanel');
|
|
13
|
+
this[internals].role = this._role;
|
|
14
|
+
if (!this.id) this.id = genUniqueId('tabpanel');
|
|
15
|
+
}
|
|
16
|
+
constructor(...args){
|
|
17
|
+
super(...args), this._role = 'tabpanel', this.value = '';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
_ts_decorate([
|
|
21
|
+
property({
|
|
22
|
+
type: String,
|
|
23
|
+
reflect: true
|
|
24
|
+
})
|
|
25
|
+
], TabPanel.prototype, "value", void 0);
|
package/src/base/tab.js
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { InternalsAttached, internals } from './mixins/internals-attached.js';
|
|
5
|
+
import { genUniqueId } from '../core/unique-id.js';
|
|
6
|
+
/**
|
|
7
|
+
* TODO: disabled
|
|
8
|
+
*/ export class Tab extends InternalsAttached(LitElement) {
|
|
9
|
+
render() {
|
|
10
|
+
return html`<slot></slot>`;
|
|
11
|
+
}
|
|
12
|
+
connectedCallback() {
|
|
13
|
+
super.connectedCallback();
|
|
14
|
+
this.setAttribute('seele-base', 'tab');
|
|
15
|
+
this[internals].role = this._role;
|
|
16
|
+
if (!this.id) this.id = genUniqueId('tab');
|
|
17
|
+
this.#updateInternals();
|
|
18
|
+
}
|
|
19
|
+
updated(changed) {
|
|
20
|
+
super.updated(changed);
|
|
21
|
+
if (changed.has('selected') || changed.has('focused')) {
|
|
22
|
+
this.#updateInternals();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
#updateInternals() {
|
|
26
|
+
this[internals].ariaSelected = this.selected ? 'true' : 'false';
|
|
27
|
+
this.focused ? this[internals].states.add('focused') : this[internals].states.delete('focused');
|
|
28
|
+
this.selected ? this[internals].states.add('selected') : this[internals].states.delete('selected');
|
|
29
|
+
}
|
|
30
|
+
focus() {
|
|
31
|
+
this.focused = true;
|
|
32
|
+
super.focus();
|
|
33
|
+
}
|
|
34
|
+
blur() {
|
|
35
|
+
this.focused = false;
|
|
36
|
+
super.blur();
|
|
37
|
+
}
|
|
38
|
+
constructor(...args){
|
|
39
|
+
super(...args), this._role = 'tab', this.selected = false, this.focused = false, this.value = '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
_ts_decorate([
|
|
43
|
+
property({
|
|
44
|
+
type: Boolean,
|
|
45
|
+
reflect: true
|
|
46
|
+
})
|
|
47
|
+
], Tab.prototype, "selected", void 0);
|
|
48
|
+
_ts_decorate([
|
|
49
|
+
property({
|
|
50
|
+
type: Boolean,
|
|
51
|
+
reflect: true
|
|
52
|
+
})
|
|
53
|
+
], Tab.prototype, "focused", void 0);
|
|
54
|
+
_ts_decorate([
|
|
55
|
+
property({
|
|
56
|
+
type: String,
|
|
57
|
+
reflect: true
|
|
58
|
+
})
|
|
59
|
+
], Tab.prototype, "value", void 0);
|
package/src/base/tabs.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { LitElement, html, isServer } from 'lit';
|
|
3
|
+
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
import { internals } from './mixins/internals-attached.js';
|
|
5
|
+
/**
|
|
6
|
+
* @fires {CustomEvent} tab-select
|
|
7
|
+
*/ export class Tabs extends LitElement {
|
|
8
|
+
get $tabs() {
|
|
9
|
+
return this.$slotItems.filter((item)=>item[internals].role === 'tab');
|
|
10
|
+
}
|
|
11
|
+
get $parent() {
|
|
12
|
+
return this.parentNode instanceof ShadowRoot ? this.parentNode.host : this.parentElement;
|
|
13
|
+
}
|
|
14
|
+
render() {
|
|
15
|
+
return html`
|
|
16
|
+
<div part="tablist" role="tablist">
|
|
17
|
+
<slot @slotchange=${this.#handleSlotChange}></slot>
|
|
18
|
+
</div>
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
super.connectedCallback();
|
|
23
|
+
if (!isServer) {
|
|
24
|
+
this.addEventListener('keydown', this.#handleKeyDown.bind(this));
|
|
25
|
+
this.addEventListener('click', this.#handleClick.bind(this));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
updated(changed) {
|
|
29
|
+
if (changed.has('selected') && this.selected) {
|
|
30
|
+
const targetTab = this.$tabs.find((t)=>t.value === this.selected);
|
|
31
|
+
if (targetTab && !targetTab.selected) {
|
|
32
|
+
this.selectTab(targetTab);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
#handleSlotChange;
|
|
37
|
+
#handleKeyDown;
|
|
38
|
+
#handleClick;
|
|
39
|
+
focusTab(tab) {
|
|
40
|
+
this.$tabs.forEach((t)=>{
|
|
41
|
+
t.blur();
|
|
42
|
+
t.tabIndex = -1;
|
|
43
|
+
});
|
|
44
|
+
tab.tabIndex = 0;
|
|
45
|
+
tab.focus();
|
|
46
|
+
}
|
|
47
|
+
selectTab(selectedTab) {
|
|
48
|
+
this.selected = selectedTab.value;
|
|
49
|
+
const parent = this.$parent;
|
|
50
|
+
const panels = Array.from(parent.querySelectorAll('[seele-base="tabpanel"]'));
|
|
51
|
+
this.$tabs.forEach((tab)=>{
|
|
52
|
+
tab.selected = tab === selectedTab;
|
|
53
|
+
tab.tabIndex = tab === selectedTab ? 0 : -1;
|
|
54
|
+
});
|
|
55
|
+
panels.forEach((panel)=>{
|
|
56
|
+
if (panel.value && selectedTab.value) {
|
|
57
|
+
panel.hidden = panel.value !== selectedTab.value;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.dispatchEvent(new CustomEvent('tab-select', {
|
|
61
|
+
detail: {
|
|
62
|
+
tab: selectedTab,
|
|
63
|
+
value: this.selected
|
|
64
|
+
},
|
|
65
|
+
bubbles: true,
|
|
66
|
+
composed: true
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
constructor(...args){
|
|
70
|
+
super(...args), this._role = 'tablist', this.switch = 'manual', this.selected = '', this.#handleSlotChange = (_)=>{
|
|
71
|
+
const parent = this.$parent;
|
|
72
|
+
this.$tabs.forEach((tab)=>{
|
|
73
|
+
const panel = parent.querySelector(`[seele-base="tab"][value="${tab.value}"]`);
|
|
74
|
+
if (!panel) console.error('[seele] cannot find a matching panel for tab ', tab);
|
|
75
|
+
// TODO: ariaLabelledByElements
|
|
76
|
+
tab.setAttribute('aria-controls', panel.id);
|
|
77
|
+
panel.setAttribute('aria-labelledby', tab.id);
|
|
78
|
+
});
|
|
79
|
+
}, this.#handleKeyDown = (event)=>{
|
|
80
|
+
const tabs = this.$tabs;
|
|
81
|
+
if (!tabs.length) return;
|
|
82
|
+
const activeElement = this.getRootNode().activeElement;
|
|
83
|
+
const currentIndex = tabs.findIndex((t)=>t === activeElement || t.focused);
|
|
84
|
+
let nextIndex = currentIndex !== -1 ? currentIndex : 0;
|
|
85
|
+
let handled = false;
|
|
86
|
+
switch(event.key){
|
|
87
|
+
case 'ArrowLeft':
|
|
88
|
+
nextIndex = currentIndex <= 0 ? tabs.length - 1 : currentIndex - 1;
|
|
89
|
+
handled = true;
|
|
90
|
+
break;
|
|
91
|
+
case 'ArrowRight':
|
|
92
|
+
nextIndex = currentIndex >= tabs.length - 1 ? 0 : currentIndex + 1;
|
|
93
|
+
handled = true;
|
|
94
|
+
break;
|
|
95
|
+
case 'Home':
|
|
96
|
+
nextIndex = 0;
|
|
97
|
+
handled = true;
|
|
98
|
+
break;
|
|
99
|
+
case 'End':
|
|
100
|
+
nextIndex = tabs.length - 1;
|
|
101
|
+
handled = true;
|
|
102
|
+
break;
|
|
103
|
+
case 'Enter':
|
|
104
|
+
case ' ':
|
|
105
|
+
if (currentIndex !== -1) {
|
|
106
|
+
this.selectTab(tabs[currentIndex]);
|
|
107
|
+
}
|
|
108
|
+
handled = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
if (handled) {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
event.stopPropagation();
|
|
114
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
115
|
+
this.focusTab(tabs[nextIndex]);
|
|
116
|
+
if (this.switch === 'auto') this.selectTab(tabs[nextIndex]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}, this.#handleClick = (event)=>{
|
|
120
|
+
const target = event.target;
|
|
121
|
+
const tab = target.closest('md-tab');
|
|
122
|
+
if (tab && this.$tabs.includes(tab)) {
|
|
123
|
+
this.focusTab(tab);
|
|
124
|
+
this.selectTab(tab);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
_ts_decorate([
|
|
130
|
+
property({
|
|
131
|
+
type: String
|
|
132
|
+
})
|
|
133
|
+
], Tabs.prototype, "switch", void 0);
|
|
134
|
+
_ts_decorate([
|
|
135
|
+
property({
|
|
136
|
+
type: String,
|
|
137
|
+
reflect: true
|
|
138
|
+
})
|
|
139
|
+
], Tabs.prototype, "selected", void 0);
|
|
140
|
+
_ts_decorate([
|
|
141
|
+
queryAssignedElements({
|
|
142
|
+
flatten: true
|
|
143
|
+
})
|
|
144
|
+
], Tabs.prototype, "$slotItems", void 0);
|
|
@@ -69,7 +69,7 @@ export class ToggleButton extends Base {
|
|
|
69
69
|
};
|
|
70
70
|
this[internals].role = 'button';
|
|
71
71
|
this.checked = this.hasAttribute('checked');
|
|
72
|
-
this.
|
|
72
|
+
this._updateInternals();
|
|
73
73
|
}
|
|
74
74
|
connectedCallback() {
|
|
75
75
|
super.connectedCallback();
|
|
@@ -91,10 +91,10 @@ export class ToggleButton extends Base {
|
|
|
91
91
|
}
|
|
92
92
|
updated(changed) {
|
|
93
93
|
if (changed.has('checked') || changed.has('disabled')) {
|
|
94
|
-
this.
|
|
94
|
+
this._updateInternals();
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
_updateInternals() {
|
|
98
98
|
this[internals].states.delete('unchecked');
|
|
99
99
|
this[internals].states.delete('checked');
|
|
100
100
|
this[internals].ariaPressed = this.checked ? 'true' : 'false';
|
package/src/base/tooltip.js
CHANGED
|
@@ -10,6 +10,9 @@ const Base = Attachable(InternalsAttached(LitElement));
|
|
|
10
10
|
/**
|
|
11
11
|
* TODO: Check if $control controls a menu/popover and keep hidden when it is open.
|
|
12
12
|
*/ export class Tooltip extends Base {
|
|
13
|
+
render() {
|
|
14
|
+
return html`<slot @slotchange="${this.#handleSlotChange}"></slot>`;
|
|
15
|
+
}
|
|
13
16
|
// Different from those in the popoverController, these timers are used to
|
|
14
17
|
// manage the delay before showing/hiding the tooltip.
|
|
15
18
|
#openTimer;
|
|
@@ -70,9 +73,6 @@ const Base = Attachable(InternalsAttached(LitElement));
|
|
|
70
73
|
};
|
|
71
74
|
this[internals].role = 'tooltip';
|
|
72
75
|
}
|
|
73
|
-
render() {
|
|
74
|
-
return html`<slot @slotchange="${this.#handleSlotChange}"></slot>`;
|
|
75
|
-
}
|
|
76
76
|
handleControlChange(prev = null, next = null) {
|
|
77
77
|
const eventHandlers = {
|
|
78
78
|
focusin: this.#handleFocusIn,
|
package/src/m3/field/field.js
CHANGED
|
@@ -12,12 +12,6 @@ import { fieldStyles } from './field-styles.css.js';
|
|
|
12
12
|
fieldStyles
|
|
13
13
|
];
|
|
14
14
|
}
|
|
15
|
-
update(changedProperties) {
|
|
16
|
-
if (changedProperties.has('label')) {
|
|
17
|
-
this.hasLabel = !!this.label;
|
|
18
|
-
}
|
|
19
|
-
super.update(changedProperties);
|
|
20
|
-
}
|
|
21
15
|
render() {
|
|
22
16
|
return html`
|
|
23
17
|
<div class="container">${this.renderContainerContent()}</div>
|
|
@@ -46,6 +40,12 @@ import { fieldStyles } from './field-styles.css.js';
|
|
|
46
40
|
@slotchange=${this.handleSlotChange}
|
|
47
41
|
></slot>`;
|
|
48
42
|
}
|
|
43
|
+
update(changedProperties) {
|
|
44
|
+
if (changedProperties.has('label')) {
|
|
45
|
+
this.hasLabel = !!this.label;
|
|
46
|
+
}
|
|
47
|
+
super.update(changedProperties);
|
|
48
|
+
}
|
|
49
49
|
handleSlotChange(e) {
|
|
50
50
|
const slot = e.target;
|
|
51
51
|
const hasContent = slot.assignedNodes({
|
|
@@ -7,28 +7,25 @@ import { Attachable } from '../../base/mixins/attachable.js';
|
|
|
7
7
|
import { InternalsAttached, internals } from '../../base/mixins/internals-attached.js';
|
|
8
8
|
import { focusRingStyles } from './focus-ring-styles.css.js';
|
|
9
9
|
export class M3FocusRing extends Attachable(InternalsAttached(LitElement)) {
|
|
10
|
-
constructor(){
|
|
11
|
-
super(), this.inward = false, this.#boundFocusIn = this.#handleFocusIn.bind(this), this.#boundFocusOut = this.#handleFocusOut.bind(this), this.#boundPointerDown = this.#handlePointerDown.bind(this);
|
|
12
|
-
this[internals].ariaHidden = 'true';
|
|
13
|
-
}
|
|
14
10
|
static{
|
|
15
11
|
this.styles = [
|
|
16
12
|
focusRingStyles
|
|
17
13
|
];
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
#handlePointerDown() {
|
|
30
|
-
this[internals].states.delete('visible');
|
|
15
|
+
constructor(){
|
|
16
|
+
super(), this.inward = false, this.#handleFocusIn = ()=>{
|
|
17
|
+
if (focusVisible) this[internals].states.add('visible');
|
|
18
|
+
else this[internals].states.delete('visible');
|
|
19
|
+
}, this.#handleFocusOut = ()=>{
|
|
20
|
+
this[internals].states.delete('visible');
|
|
21
|
+
}, this.#handlePointerDown = ()=>{
|
|
22
|
+
this[internals].states.delete('visible');
|
|
23
|
+
};
|
|
24
|
+
this[internals].ariaHidden = 'true';
|
|
31
25
|
}
|
|
26
|
+
#handleFocusIn;
|
|
27
|
+
#handleFocusOut;
|
|
28
|
+
#handlePointerDown;
|
|
32
29
|
visualFocus() {
|
|
33
30
|
this.#handleFocusIn();
|
|
34
31
|
}
|
|
@@ -37,9 +34,9 @@ export class M3FocusRing extends Attachable(InternalsAttached(LitElement)) {
|
|
|
37
34
|
}
|
|
38
35
|
handleControlChange(prev = null, next = null) {
|
|
39
36
|
const eventHandlers = {
|
|
40
|
-
focusin: this.#
|
|
41
|
-
focusout: this.#
|
|
42
|
-
pointerdown: this.#
|
|
37
|
+
focusin: this.#handleFocusIn,
|
|
38
|
+
focusout: this.#handleFocusOut,
|
|
39
|
+
pointerdown: this.#handlePointerDown
|
|
43
40
|
};
|
|
44
41
|
Object.keys(eventHandlers).forEach((eventName)=>{
|
|
45
42
|
prev?.removeEventListener(eventName, eventHandlers[eventName]);
|
package/src/m3/list/list-item.js
CHANGED
|
@@ -5,29 +5,12 @@ import { ListItem } from '../../base/list-item.js';
|
|
|
5
5
|
import '../item/item.js';
|
|
6
6
|
import { listItemStyles } from './list-item-styles.css.js';
|
|
7
7
|
export class M3ListItem extends ListItem {
|
|
8
|
-
constructor(){
|
|
9
|
-
super();
|
|
10
|
-
this.updateComplete.then(()=>{
|
|
11
|
-
this.ripple.$control = this;
|
|
12
|
-
this.focusRing.$control = this;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
8
|
static{
|
|
16
9
|
this.styles = [
|
|
17
10
|
...super.styles,
|
|
18
11
|
listItemStyles
|
|
19
12
|
];
|
|
20
13
|
}
|
|
21
|
-
updated(changed) {
|
|
22
|
-
super.updated(changed);
|
|
23
|
-
if (changed.has('focused')) {
|
|
24
|
-
if (this.focused) {
|
|
25
|
-
this.focusRing.visualFocus();
|
|
26
|
-
} else {
|
|
27
|
-
this.focusRing.visualBlur();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
14
|
render() {
|
|
32
15
|
return html`
|
|
33
16
|
<md-item>
|
|
@@ -48,6 +31,23 @@ export class M3ListItem extends ListItem {
|
|
|
48
31
|
</md-item>
|
|
49
32
|
`;
|
|
50
33
|
}
|
|
34
|
+
constructor(){
|
|
35
|
+
super();
|
|
36
|
+
this.updateComplete.then(()=>{
|
|
37
|
+
this.ripple.$control = this;
|
|
38
|
+
this.focusRing.$control = this;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
updated(changed) {
|
|
42
|
+
super.updated(changed);
|
|
43
|
+
if (changed.has('focused')) {
|
|
44
|
+
if (this.focused) {
|
|
45
|
+
this.focusRing.visualFocus();
|
|
46
|
+
} else {
|
|
47
|
+
this.focusRing.visualBlur();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
_ts_decorate([
|
|
53
53
|
query('md-ripple')
|
package/src/m3/list/list.js
CHANGED
|
@@ -4,18 +4,14 @@ import { List } from '../../base/list.js';
|
|
|
4
4
|
import { listStyles } from './list-styles.css.js';
|
|
5
5
|
export class M3List extends List {
|
|
6
6
|
static{
|
|
7
|
-
// FIXME: Might cause a long list to scroll more than expected
|
|
8
|
-
// override readonly _scrollPadding = 4;
|
|
9
7
|
this.styles = [
|
|
10
8
|
listStyles
|
|
11
9
|
];
|
|
12
10
|
}
|
|
13
11
|
constructor(...args){
|
|
14
|
-
super(...args),
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'md-list-item-radio'
|
|
18
|
-
], this.color = 'standard';
|
|
12
|
+
super(...args), // FIXME: Might cause a long list to scroll more than expected
|
|
13
|
+
// override readonly _scrollPadding = 4;
|
|
14
|
+
this.color = 'standard';
|
|
19
15
|
}
|
|
20
16
|
}
|
|
21
17
|
_ts_decorate([
|
package/src/m3/menu/menu.js
CHANGED
|
@@ -5,22 +5,17 @@ import { Menu } from '../../base/menu.js';
|
|
|
5
5
|
import { menuStyles } from './menu-styles.css.js';
|
|
6
6
|
export class M3Menu extends Menu {
|
|
7
7
|
static{
|
|
8
|
-
// FIXME: Might cause a long list to scroll more than expected
|
|
9
|
-
// override readonly _scrollPadding = 4;
|
|
10
8
|
this.styles = [
|
|
11
9
|
menuStyles
|
|
12
10
|
];
|
|
13
11
|
}
|
|
14
12
|
constructor(...args){
|
|
15
|
-
super(...args), this.
|
|
16
|
-
'md-menu-item',
|
|
17
|
-
'md-menu-item-checkbox',
|
|
18
|
-
'md-menu-item-radio',
|
|
19
|
-
'md-option'
|
|
20
|
-
], this._durations = {
|
|
13
|
+
super(...args), this._durations = {
|
|
21
14
|
show: 300,
|
|
22
15
|
hide: 150
|
|
23
|
-
},
|
|
16
|
+
}, // FIXME: Might cause a long list to scroll more than expected
|
|
17
|
+
// override readonly _scrollPadding = 4;
|
|
18
|
+
this.color = 'standard';
|
|
24
19
|
}
|
|
25
20
|
}
|
|
26
21
|
_ts_decorate([
|
|
@@ -23,7 +23,7 @@ export class MdFilledSelect extends M3Select {
|
|
|
23
23
|
.focused=${this.open || this.focused}
|
|
24
24
|
supportingtext=${this.supportingText}
|
|
25
25
|
@click=${this.toggle}
|
|
26
|
-
@keydown=${this.
|
|
26
|
+
@keydown=${this._handleFieldKeydown}
|
|
27
27
|
@focus=${()=>this.focused = true}
|
|
28
28
|
@blur=${()=>this.focused = false}
|
|
29
29
|
tabindex=${this.disabled ? '-1' : '0'}
|
|
@@ -23,7 +23,7 @@ export class MdOutlinedSelect extends M3Select {
|
|
|
23
23
|
.focused=${this.open || this.focused}
|
|
24
24
|
supportingtext=${this.supportingText}
|
|
25
25
|
@click=${this.toggle}
|
|
26
|
-
@keydown=${this.
|
|
26
|
+
@keydown=${this._handleFieldKeydown}
|
|
27
27
|
@focus=${()=>this.focused = true}
|
|
28
28
|
@blur=${()=>this.focused = false}
|
|
29
29
|
tabindex=${this.disabled ? '-1' : '0'}
|
package/src/m3/select/select.js
CHANGED
|
@@ -23,10 +23,10 @@ import { Select } from '../../base/select.js';
|
|
|
23
23
|
?open="${this.open}"
|
|
24
24
|
@open="${()=>this.open = true}"
|
|
25
25
|
@close="${()=>this.open = false}"
|
|
26
|
-
@item-focus="${this.
|
|
27
|
-
@select="${this.
|
|
26
|
+
@item-focus="${this._handleMenuItemFocus}"
|
|
27
|
+
@select="${this._handleMenuSelect}"
|
|
28
28
|
>
|
|
29
|
-
<slot part="items" @slotchange=${this.
|
|
29
|
+
<slot part="items" @slotchange=${this._handleSlotChange}></slot>
|
|
30
30
|
</md-menu>
|
|
31
31
|
`;
|
|
32
32
|
}
|
package/src/m3/slider/slider.js
CHANGED
|
@@ -74,12 +74,12 @@ export class M3Slider extends Slider {
|
|
|
74
74
|
<div class="inactive-after"></div>
|
|
75
75
|
</div>
|
|
76
76
|
${this.ticks ? html`
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
<div class="tickmarks">
|
|
78
|
+
<div class="inactive-before"></div>
|
|
79
|
+
<div class="active"></div>
|
|
80
|
+
<div class="inactive-after"></div>
|
|
81
|
+
</div>
|
|
82
|
+
` : nothing}
|
|
83
83
|
`;
|
|
84
84
|
}
|
|
85
85
|
renderLabel(value) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { customElement } from 'lit/decorators.js';
|
|
3
|
+
import { TabPanel } from '../../base/tab-panel.js';
|
|
4
|
+
import { tabPanelStyles } from './tab-panel-styles.css.js';
|
|
5
|
+
export class M3TabPanel extends TabPanel {
|
|
6
|
+
static{
|
|
7
|
+
this.styles = [
|
|
8
|
+
tabPanelStyles
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
M3TabPanel = _ts_decorate([
|
|
13
|
+
customElement('md-tab-panel')
|
|
14
|
+
], M3TabPanel);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const tabStyles = css`:host{--md-focus-ring-shape:8px;box-sizing:border-box;color:var(--md-sys-color-on-surface-variant);cursor:pointer;font:var(--md-sys-typography-title-small);user-select:none;outline:none;flex-shrink:0;justify-content:center;padding:0 16px;display:inline-flex;position:relative}:host([selected]){color:var(--_tab-selected-color)}:host([selected]) md-focus-ring{margin-bottom:4px}[part=content]{flex-direction:var(--_tab-flex-dir,row);height:var(--_tab-height,48px);justify-content:center;align-items:center;gap:8px;display:inline-flex}[part~=icon]{fill:currentColor;block-size:1em;inline-size:1em;font-size:24px}`;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { customElement, query } from 'lit/decorators.js';
|
|
4
|
+
import { Tab } from '../../base/tab.js';
|
|
5
|
+
import '../focus-ring/focus-ring.js';
|
|
6
|
+
import '../ripple/ripple.js';
|
|
7
|
+
import { targetStyles } from '../target-styles.css.js';
|
|
8
|
+
import { tabStyles } from './tab-styles.css.js';
|
|
9
|
+
export class M3Tab extends Tab {
|
|
10
|
+
static{
|
|
11
|
+
this.styles = [
|
|
12
|
+
targetStyles,
|
|
13
|
+
tabStyles
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
render() {
|
|
17
|
+
return html`
|
|
18
|
+
<md-focus-ring inward></md-focus-ring>
|
|
19
|
+
<md-ripple inward></md-ripple>
|
|
20
|
+
<span part="target"></span>
|
|
21
|
+
<div part="content">
|
|
22
|
+
<slot part="icon" name="icon" aria-hidden="true"></slot>
|
|
23
|
+
<slot part="label"></slot>
|
|
24
|
+
</div>
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
_ts_decorate([
|
|
29
|
+
query('[part="content"]')
|
|
30
|
+
], M3Tab.prototype, "$content", void 0);
|
|
31
|
+
M3Tab = _ts_decorate([
|
|
32
|
+
customElement('md-tab')
|
|
33
|
+
], M3Tab);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const tabsStyles = css`:host{--_tab-selected-color:var(--md-sys-color-primary);border-bottom:1px solid var(--md-sys-color-outline-variant);box-sizing:border-box;scroll-behavior:smooth;scrollbar-width:none;display:block;position:relative;overflow:auto hidden}[part=tablist]{display:flex}[part=indicator]{background-color:var(--md-sys-color-primary);height:3px;transition:var(--md-sys-motion-std-effects-slow-duration) var(--md-sys-motion-std-effects-slow);will-change:left, width;border-radius:3px 3px 0 0;position:absolute;bottom:0}::slotted(*){flex:1}:host([secondary]){--_tab-selected-color:var(--md-sys-color-on-surface)}:host([secondary]) [part=indicator]{border-radius:0;height:2px}:host([iconsabove]){--_tab-flex-dir:column;--_tab-height:64px}`;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { customElement, property, query } from 'lit/decorators.js';
|
|
4
|
+
import { Tabs } from '../../base/tabs.js';
|
|
5
|
+
import { tabsStyles } from './tabs-styles.css.js';
|
|
6
|
+
export class M3Tabs extends Tabs {
|
|
7
|
+
static{
|
|
8
|
+
this.styles = [
|
|
9
|
+
tabsStyles
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
render() {
|
|
13
|
+
return html`
|
|
14
|
+
${super.render()}
|
|
15
|
+
<div part="indicator" role="presentation"></div>
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
selectTab(selectedTab) {
|
|
19
|
+
super.selectTab(selectedTab);
|
|
20
|
+
requestAnimationFrame(()=>{
|
|
21
|
+
const left = selectedTab.offsetLeft + (this.secondary ? 0 : selectedTab.$content.offsetLeft);
|
|
22
|
+
const width = this.secondary ? selectedTab.offsetWidth : selectedTab.$content.offsetWidth;
|
|
23
|
+
this.$indicator.style.left = `${left}px`;
|
|
24
|
+
this.$indicator.style.width = `${width}px`;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
constructor(...args){
|
|
28
|
+
super(...args), this.secondary = false, this.iconsAbove = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
_ts_decorate([
|
|
32
|
+
query('[part="indicator"]')
|
|
33
|
+
], M3Tabs.prototype, "$indicator", void 0);
|
|
34
|
+
_ts_decorate([
|
|
35
|
+
property({
|
|
36
|
+
type: Boolean,
|
|
37
|
+
reflect: true
|
|
38
|
+
})
|
|
39
|
+
], M3Tabs.prototype, "secondary", void 0);
|
|
40
|
+
_ts_decorate([
|
|
41
|
+
property({
|
|
42
|
+
type: Boolean,
|
|
43
|
+
reflect: true
|
|
44
|
+
})
|
|
45
|
+
], M3Tabs.prototype, "iconsAbove", void 0);
|
|
46
|
+
M3Tabs = _ts_decorate([
|
|
47
|
+
customElement('md-tabs')
|
|
48
|
+
], M3Tabs);
|
|
@@ -9,16 +9,6 @@ export class M3Toolbar extends InternalsAttached(LitElement) {
|
|
|
9
9
|
toolbarStyles
|
|
10
10
|
];
|
|
11
11
|
}
|
|
12
|
-
constructor(){
|
|
13
|
-
super(), this.type = 'docked', this.color = 'standard', this.orientation = 'horizontal';
|
|
14
|
-
this[internals].role = 'toolbar';
|
|
15
|
-
this[internals].ariaOrientation = this.orientation;
|
|
16
|
-
}
|
|
17
|
-
updated(changed) {
|
|
18
|
-
if (changed.has('orientation')) {
|
|
19
|
-
this[internals].ariaOrientation = this.orientation;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
12
|
render() {
|
|
23
13
|
return html`
|
|
24
14
|
<div part="container">
|
|
@@ -29,6 +19,16 @@ export class M3Toolbar extends InternalsAttached(LitElement) {
|
|
|
29
19
|
</div>
|
|
30
20
|
`;
|
|
31
21
|
}
|
|
22
|
+
constructor(){
|
|
23
|
+
super(), this.type = 'docked', this.color = 'standard', this.orientation = 'horizontal';
|
|
24
|
+
this[internals].role = 'toolbar';
|
|
25
|
+
this[internals].ariaOrientation = this.orientation;
|
|
26
|
+
}
|
|
27
|
+
updated(changed) {
|
|
28
|
+
if (changed.has('orientation')) {
|
|
29
|
+
this[internals].ariaOrientation = this.orientation;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
_ts_decorate([
|
|
34
34
|
property({
|
package/src/base/tab-group.js
DELETED