@vollowx/seele 0.13.3 → 0.13.5
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 +5 -3
- package/package.json +2 -3
- package/src/all.js +34 -34
- package/src/base/autocomplete.js +7 -0
- package/src/base/button.js +6 -0
- package/src/base/checkbox.js +7 -1
- package/src/base/list.js +4 -4
- package/src/base/radio.js +162 -0
- package/src/base/tabs.js +3 -5
- package/src/base/toggle-button.js +15 -7
- package/src/core/decorators.js +1 -1
- package/src/m3/button/common-button-styles.css.js +1 -1
- package/src/m3/button/common-button-toggle.js +4 -3
- package/src/m3/button/common-button.js +4 -3
- package/src/m3/button/icon-button-styles.css.js +1 -1
- package/src/m3/button/icon-button-toggle.js +1 -1
- package/src/m3/button/icon-button.js +1 -1
- package/src/m3/button/shared-button-styles.css.js +1 -1
- package/src/m3/button/shared-button-toggle-styles.css.js +1 -1
- package/src/m3/button-group/connected-button-group-styles.css.js +2 -0
- package/src/m3/button-group/connected-button-group.js +37 -0
- package/src/m3/button-group/standard-button-group-styles.css.js +1 -1
- package/src/m3/button-group/standard-button-group.js +11 -10
- package/src/m3/checkbox/checkbox.js +12 -1
- package/src/m3/dialog/dialog-spring.js +179 -0
- package/src/m3/dialog/dialog.js +5 -4
- package/src/m3/fab/fab-styles.css.js +1 -1
- package/src/m3/fab/fab.js +2 -1
- package/src/m3/radio/radio-styles.css.js +2 -0
- package/src/m3/radio/radio.js +22 -0
- package/src/m3/system/motion.js +27 -0
- package/src/win98/button/radio.js +8 -0
- package/src/win98/radio/radio.js +8 -0
package/README.md
CHANGED
|
@@ -51,20 +51,22 @@ Check out [Overview](./docs/00-Overview.md).
|
|
|
51
51
|
seele relies on the following modern web standards:
|
|
52
52
|
|
|
53
53
|
- [`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals), Baseline 2023
|
|
54
|
-
- [Constructable Stylesheets](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet), Baseline 2024, C[^1] 90, F 126
|
|
55
54
|
- [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:dir), Baseline 2023, C 120, F 49
|
|
55
|
+
- [Constructable Stylesheets](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet), Baseline 2024, C[^1] 90, F 126
|
|
56
56
|
- [`:state()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:state), Baseline 2024, C 125, F 126
|
|
57
|
+
- [`ariaActiveDescendantElement`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaActiveDescendantElement) and other similar ones, Baseline 2025, C 135, F 136
|
|
57
58
|
|
|
58
59
|
And all that result in:
|
|
59
60
|
|
|
60
|
-
- Chromium: >=
|
|
61
|
-
- Firefox: >=
|
|
61
|
+
- Chromium: >= 135
|
|
62
|
+
- Firefox: >= 136
|
|
62
63
|
|
|
63
64
|
It is 2026 now, you don't really need to worry about this. But in the future,
|
|
64
65
|
these following web features might be used and require higher browser versions:
|
|
65
66
|
|
|
66
67
|
- [anchor()](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor), Baseline 2026, C 125, F 147, will remove the dependency `floating-dom`
|
|
67
68
|
- [`::view-*`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::view-transition), Baseline 2025, C 111, F 144, will optimize some animations for menu, dialog, etc
|
|
69
|
+
- [`CSSNumericValue`](https://developer.mozilla.org/en-US/docs/Web/API/CSSNumericValue), not Baseline, C 66, F Nightly, worked around currently
|
|
68
70
|
|
|
69
71
|
[^1]: C = Chromium, F = Firefox.
|
|
70
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vollowx/seele",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.5",
|
|
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",
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
"module": "./src/all.js",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./src/all.js",
|
|
12
|
-
"
|
|
13
|
-
"./m3/*": "./src/m3/*"
|
|
12
|
+
"./*.js": "./src/*.js"
|
|
14
13
|
},
|
|
15
14
|
"files": [
|
|
16
15
|
"src/**/*.js",
|
package/src/all.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import './m3/autocomplete/autocomplete.js';
|
|
2
|
+
import './m3/badge/badge.js';
|
|
3
|
+
import './m3/button/common-button.js';
|
|
4
|
+
import './m3/button/common-button-toggle.js';
|
|
5
|
+
import './m3/button/icon-button.js';
|
|
6
|
+
import './m3/button/icon-button-toggle.js';
|
|
7
|
+
import './m3/button-group/standard-button-group.js';
|
|
8
|
+
import './m3/button-group/connected-button-group.js';
|
|
9
|
+
import './m3/checkbox/checkbox.js';
|
|
10
|
+
import './m3/dialog/dialog.js';
|
|
11
|
+
import './m3/divider/divider.js';
|
|
12
|
+
import './m3/fab/fab.js';
|
|
13
|
+
import './m3/focus-ring/focus-ring.js';
|
|
14
|
+
import './m3/item/item.js';
|
|
15
|
+
import './m3/list/list.js';
|
|
16
|
+
import './m3/list/list-item.js';
|
|
17
|
+
import './m3/loading-indicator/loading-indicator.js';
|
|
18
|
+
import './m3/menu/menu.js';
|
|
19
|
+
import './m3/menu/menu-item.js';
|
|
20
|
+
import './m3/radio/radio.js';
|
|
21
|
+
import './m3/ripple/ripple.js';
|
|
22
|
+
import './m3/select/filled-select.js';
|
|
23
|
+
import './m3/select/outlined-select.js';
|
|
24
|
+
import './m3/select/option.js';
|
|
25
|
+
import './m3/slider/slider.js';
|
|
26
|
+
import './m3/switch/switch.js';
|
|
27
|
+
import './m3/tab/tabs.js';
|
|
28
|
+
import './m3/tab/tab.js';
|
|
29
|
+
import './m3/tab/tab-panel.js';
|
|
30
|
+
import './m3/text-field/filled-text-field.js';
|
|
31
|
+
import './m3/text-field/outlined-text-field.js';
|
|
32
|
+
import './m3/toolbar/toolbar.js';
|
|
33
|
+
import './m3/tooltip/tooltip.js';
|
|
34
|
+
import './win98/button/button.js';
|
package/src/base/autocomplete.js
CHANGED
|
@@ -111,11 +111,18 @@ const Base = FocusDelegated(InternalsAttached(LitElement));
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
handleMenuSelect(event) {
|
|
114
|
+
// FIXME: pressing <Enter> in a 'both' mode autocomplete triggers this
|
|
115
|
+
// function, which overrides the current value in the text field
|
|
116
|
+
// since the selected value in the menu is not the same as the
|
|
117
|
+
// one in autocomplete
|
|
118
|
+
//
|
|
119
|
+
// if (this.mode === 'list') {
|
|
114
120
|
const selectedItem = event.detail.item;
|
|
115
121
|
const newValue = selectedItem.getAttribute('value') || selectedItem.textContent?.trim() || '';
|
|
116
122
|
if (this.$input) {
|
|
117
123
|
this.$input.value = newValue;
|
|
118
124
|
}
|
|
125
|
+
// }
|
|
119
126
|
if (!this.keepOpenSelect) {
|
|
120
127
|
this.open = false;
|
|
121
128
|
}
|
package/src/base/button.js
CHANGED
|
@@ -28,12 +28,18 @@ export class Button extends Base {
|
|
|
28
28
|
};
|
|
29
29
|
this[internals].role = 'button';
|
|
30
30
|
this.updateState();
|
|
31
|
+
// This is set to prevent a first-paint-time transition for styles like
|
|
32
|
+
// `border-radius` on `:host`. Also in `ToggleButton`.
|
|
33
|
+
// Double `requestAnimationFrame` is necessary since the callback inside is
|
|
34
|
+
// called before the nth frame is printed.
|
|
35
|
+
this.setAttribute('notransition', '');
|
|
31
36
|
}
|
|
32
37
|
connectedCallback() {
|
|
33
38
|
super.connectedCallback();
|
|
34
39
|
this.addEventListener('keydown', this.#handleKeyDown);
|
|
35
40
|
this.addEventListener('keyup', this.#handleKeyUp);
|
|
36
41
|
this.addEventListener('click', this.#handleClick);
|
|
42
|
+
requestAnimationFrame(()=>requestAnimationFrame(()=>this.removeAttribute('notransition')));
|
|
37
43
|
}
|
|
38
44
|
disconnectedCallback() {
|
|
39
45
|
super.disconnectedCallback();
|
package/src/base/checkbox.js
CHANGED
|
@@ -45,10 +45,10 @@ export class Checkbox extends Base {
|
|
|
45
45
|
this.addEventListener('keyup', this.#handleKeyUp);
|
|
46
46
|
}
|
|
47
47
|
disconnectedCallback() {
|
|
48
|
-
super.disconnectedCallback();
|
|
49
48
|
this.removeEventListener('click', this.#handleClick);
|
|
50
49
|
this.removeEventListener('keydown', this.#handleKeyDown);
|
|
51
50
|
this.removeEventListener('keyup', this.#handleKeyUp);
|
|
51
|
+
super.disconnectedCallback();
|
|
52
52
|
}
|
|
53
53
|
updated(changed) {
|
|
54
54
|
if (changed.has('checked') || changed.has('disabled') || changed.has('indeterminate') || changed.has('required')) {
|
|
@@ -94,6 +94,12 @@ export class Checkbox extends Base {
|
|
|
94
94
|
detail: this.checked
|
|
95
95
|
}));
|
|
96
96
|
}
|
|
97
|
+
formResetCallback() {
|
|
98
|
+
this.checked = this.hasAttribute('checked');
|
|
99
|
+
}
|
|
100
|
+
formStateRestoreCallback(state, _reason) {
|
|
101
|
+
this.checked = state === 'on';
|
|
102
|
+
}
|
|
97
103
|
}
|
|
98
104
|
_ts_decorate([
|
|
99
105
|
property({
|
package/src/base/list.js
CHANGED
|
@@ -45,11 +45,11 @@ const Base = InternalsAttached(LitElement);
|
|
|
45
45
|
},
|
|
46
46
|
wrapNavigation: ()=>false
|
|
47
47
|
});
|
|
48
|
+
this[internals].role = 'listbox';
|
|
49
|
+
if (!this.hasAttribute('tabindex')) {
|
|
50
|
+
this.setAttribute('tabindex', '0');
|
|
51
|
+
}
|
|
48
52
|
if (!isServer) {
|
|
49
|
-
this[internals].role = 'listbox';
|
|
50
|
-
if (!this.hasAttribute('tabindex')) {
|
|
51
|
-
this.setAttribute('tabindex', '0');
|
|
52
|
-
}
|
|
53
53
|
this.addEventListener('keydown', (e)=>this.#handleKeyDown(e));
|
|
54
54
|
this.addEventListener('focusin', ()=>this.#handleFocusIn());
|
|
55
55
|
this.addEventListener('focusout', ()=>this.#handleFocusOut());
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { FormAssociated } from './mixins/form-associated.js';
|
|
5
|
+
import { InternalsAttached, internals } from './mixins/internals-attached.js';
|
|
6
|
+
export class Radio extends FormAssociated(InternalsAttached(LitElement)) {
|
|
7
|
+
// Static hidden registry: Scope (Form or RootNode) -> Name -> Set<Radio>
|
|
8
|
+
static #registry = new Map();
|
|
9
|
+
#currentScope;
|
|
10
|
+
#currentName;
|
|
11
|
+
constructor(){
|
|
12
|
+
super(), this.#currentScope = null, this.#currentName = '', this.checked = false;
|
|
13
|
+
this[internals].role = 'radio';
|
|
14
|
+
}
|
|
15
|
+
connectedCallback() {
|
|
16
|
+
super.connectedCallback();
|
|
17
|
+
this.#registerToGroup();
|
|
18
|
+
this.#updateAriaState();
|
|
19
|
+
this.addEventListener('click', this.#handleClick);
|
|
20
|
+
this.addEventListener('keydown', this.#handleKeyDown);
|
|
21
|
+
}
|
|
22
|
+
disconnectedCallback() {
|
|
23
|
+
this.removeEventListener('click', this.#handleClick);
|
|
24
|
+
this.removeEventListener('keydown', this.#handleKeyDown);
|
|
25
|
+
this.#unregisterFromGroup();
|
|
26
|
+
super.disconnectedCallback();
|
|
27
|
+
}
|
|
28
|
+
updated(changedProperties) {
|
|
29
|
+
super.updated(changedProperties);
|
|
30
|
+
if (changedProperties.has('name')) {
|
|
31
|
+
this.#unregisterFromGroup();
|
|
32
|
+
this.#registerToGroup();
|
|
33
|
+
}
|
|
34
|
+
if (changedProperties.has('checked')) {
|
|
35
|
+
if (this.checked) {
|
|
36
|
+
this.#uncheckSiblings();
|
|
37
|
+
}
|
|
38
|
+
this[internals].setFormValue(this.checked ? this.value : null);
|
|
39
|
+
this.#updateGroupTabIndices();
|
|
40
|
+
this.dispatchEvent(new Event('change', {
|
|
41
|
+
bubbles: true,
|
|
42
|
+
composed: true
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
if (changedProperties.has('disabled') || changedProperties.has('checked')) this.#updateAriaState();
|
|
46
|
+
}
|
|
47
|
+
#handleClick() {
|
|
48
|
+
if (this.disabled || this.checked) return;
|
|
49
|
+
this.checked = true;
|
|
50
|
+
this.focus();
|
|
51
|
+
}
|
|
52
|
+
#handleKeyDown(event) {
|
|
53
|
+
if (this.disabled) return;
|
|
54
|
+
switch(event.key){
|
|
55
|
+
case ' ':
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
if (!this.checked) this.checked = true;
|
|
58
|
+
break;
|
|
59
|
+
case 'ArrowDown':
|
|
60
|
+
case 'ArrowRight':
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
this.#navigateGroup(1);
|
|
63
|
+
break;
|
|
64
|
+
case 'ArrowUp':
|
|
65
|
+
case 'ArrowLeft':
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
this.#navigateGroup(-1);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
#getScope() {
|
|
72
|
+
return this.form ?? this.getRootNode();
|
|
73
|
+
}
|
|
74
|
+
#registerToGroup() {
|
|
75
|
+
if (!this.name) return;
|
|
76
|
+
const scope = this.#getScope();
|
|
77
|
+
this.#currentScope = scope;
|
|
78
|
+
this.#currentName = this.name;
|
|
79
|
+
let scopeMap = Radio.#registry.get(scope);
|
|
80
|
+
if (!scopeMap) {
|
|
81
|
+
scopeMap = new Map();
|
|
82
|
+
Radio.#registry.set(scope, scopeMap);
|
|
83
|
+
}
|
|
84
|
+
let group = scopeMap.get(this.name);
|
|
85
|
+
if (!group) {
|
|
86
|
+
group = new Set();
|
|
87
|
+
scopeMap.set(this.name, group);
|
|
88
|
+
}
|
|
89
|
+
group.add(this);
|
|
90
|
+
this.#updateGroupTabIndices(group);
|
|
91
|
+
}
|
|
92
|
+
#unregisterFromGroup() {
|
|
93
|
+
if (!this.#currentScope || !this.#currentName) return;
|
|
94
|
+
const scopeMap = Radio.#registry.get(this.#currentScope);
|
|
95
|
+
if (scopeMap) {
|
|
96
|
+
const group = scopeMap.get(this.#currentName);
|
|
97
|
+
if (group) {
|
|
98
|
+
group.delete(this);
|
|
99
|
+
if (group.size === 0) {
|
|
100
|
+
scopeMap.delete(this.#currentName);
|
|
101
|
+
} else {
|
|
102
|
+
this.#updateGroupTabIndices(group);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (scopeMap.size === 0) Radio.#registry.delete(this.#currentScope);
|
|
106
|
+
}
|
|
107
|
+
this.#currentScope = null;
|
|
108
|
+
this.#currentName = '';
|
|
109
|
+
}
|
|
110
|
+
#getGroupRadios() {
|
|
111
|
+
if (!this.name) return [
|
|
112
|
+
this
|
|
113
|
+
];
|
|
114
|
+
const scope = this.#getScope();
|
|
115
|
+
const group = Radio.#registry.get(scope)?.get(this.name);
|
|
116
|
+
return group ? Array.from(group) : [
|
|
117
|
+
this
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
#uncheckSiblings() {
|
|
121
|
+
const radios = this.#getGroupRadios();
|
|
122
|
+
for (const radio of radios)if (radio !== this && radio.checked) radio.checked = false;
|
|
123
|
+
}
|
|
124
|
+
#updateGroupTabIndices(groupSet) {
|
|
125
|
+
const radios = groupSet ? Array.from(groupSet) : this.#getGroupRadios();
|
|
126
|
+
if (radios.length === 0) return;
|
|
127
|
+
const hasChecked = radios.some((r)=>r.checked && !r.disabled);
|
|
128
|
+
let enabledFound = false;
|
|
129
|
+
for (const radio of radios){
|
|
130
|
+
const active = !radio.disabled && (hasChecked ? radio.checked : !enabledFound);
|
|
131
|
+
if (active) enabledFound = true;
|
|
132
|
+
radio.tabIndex = active ? 0 : -1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
#navigateGroup(direction) {
|
|
136
|
+
const radios = this.#getGroupRadios().filter((r)=>!r.disabled);
|
|
137
|
+
if (radios.length <= 1) return;
|
|
138
|
+
const currentIndex = radios.indexOf(this);
|
|
139
|
+
const nextIndex = (currentIndex + direction + radios.length) % radios.length;
|
|
140
|
+
const targetRadio = radios[nextIndex];
|
|
141
|
+
targetRadio.checked = true;
|
|
142
|
+
targetRadio.focus();
|
|
143
|
+
}
|
|
144
|
+
#updateAriaState() {
|
|
145
|
+
this[internals].ariaChecked = String(this.checked);
|
|
146
|
+
this[internals].ariaDisabled = String(this.disabled);
|
|
147
|
+
}
|
|
148
|
+
formResetCallback() {
|
|
149
|
+
this.checked = this.hasAttribute('checked');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
_ts_decorate([
|
|
153
|
+
property({
|
|
154
|
+
type: String
|
|
155
|
+
})
|
|
156
|
+
], Radio.prototype, "value", void 0);
|
|
157
|
+
_ts_decorate([
|
|
158
|
+
property({
|
|
159
|
+
type: Boolean,
|
|
160
|
+
reflect: true
|
|
161
|
+
})
|
|
162
|
+
], Radio.prototype, "checked", void 0);
|
package/src/base/tabs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
-
import { LitElement, html
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
3
|
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
4
|
import { internals } from './mixins/internals-attached.js';
|
|
5
5
|
/**
|
|
@@ -16,10 +16,8 @@ import { internals } from './mixins/internals-attached.js';
|
|
|
16
16
|
}
|
|
17
17
|
connectedCallback() {
|
|
18
18
|
super.connectedCallback();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.addEventListener('click', this.#handleClick.bind(this));
|
|
22
|
-
}
|
|
19
|
+
this.addEventListener('keydown', this.#handleKeyDown.bind(this));
|
|
20
|
+
this.addEventListener('click', this.#handleClick.bind(this));
|
|
23
21
|
}
|
|
24
22
|
updated(changed) {
|
|
25
23
|
if (changed.has('selected') && this.selected) {
|
|
@@ -17,18 +17,18 @@ export class ToggleButton extends Base {
|
|
|
17
17
|
}
|
|
18
18
|
constructor(){
|
|
19
19
|
super(), this.checked = false, /**
|
|
20
|
-
*
|
|
20
|
+
* --> Drag-and-drop to the other side
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* `checked` is supposed to be changed once
|
|
23
23
|
*
|
|
24
24
|
* 1. `pointerdown`: _ignoreClick = false
|
|
25
25
|
* 2. `pointermove`: _ignoreClick = true
|
|
26
26
|
* 3. `pointerup`: `checked` changed
|
|
27
27
|
* 4. `click`: ignored, _ignoreClick = false
|
|
28
28
|
*
|
|
29
|
-
*
|
|
29
|
+
* --> Drag-and-drop to the other side, then click
|
|
30
30
|
*
|
|
31
|
-
*
|
|
31
|
+
* `checked` is supposed to be changed twice
|
|
32
32
|
*
|
|
33
33
|
* 1. `pointerdown`: _ignoreClick = false
|
|
34
34
|
* 2. `pointermove`: _ignoreClick = true
|
|
@@ -36,9 +36,9 @@ export class ToggleButton extends Base {
|
|
|
36
36
|
* 4. `click`: ignored, _ignoreClick = false
|
|
37
37
|
* 5. `click`: `checked` changed(2)
|
|
38
38
|
*
|
|
39
|
-
*
|
|
39
|
+
* --> Drag-and-drop to the other side, then click the label
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* `checked` is supposed to be changed twice
|
|
42
42
|
*
|
|
43
43
|
* 1. `pointerdown`: _ignoreClick = false
|
|
44
44
|
* 2. `pointermove`: _ignoreClick = true
|
|
@@ -70,6 +70,7 @@ export class ToggleButton extends Base {
|
|
|
70
70
|
this[internals].role = 'button';
|
|
71
71
|
this.checked = this.hasAttribute('checked');
|
|
72
72
|
this._updateInternals();
|
|
73
|
+
this.setAttribute('notransition', '');
|
|
73
74
|
}
|
|
74
75
|
connectedCallback() {
|
|
75
76
|
super.connectedCallback();
|
|
@@ -79,15 +80,16 @@ export class ToggleButton extends Base {
|
|
|
79
80
|
this.labels.forEach((label)=>{
|
|
80
81
|
label.addEventListener('click', this.#handleLabelClick);
|
|
81
82
|
});
|
|
83
|
+
requestAnimationFrame(()=>requestAnimationFrame(()=>this.removeAttribute('notransition')));
|
|
82
84
|
}
|
|
83
85
|
disconnectedCallback() {
|
|
84
|
-
super.disconnectedCallback();
|
|
85
86
|
this.removeEventListener('click', this.#handleClick);
|
|
86
87
|
this.removeEventListener('keydown', this.#handleKeyDown);
|
|
87
88
|
this.removeEventListener('keyup', this.#handleKeyUp);
|
|
88
89
|
this.labels.forEach((label)=>{
|
|
89
90
|
label.removeEventListener('click', this.#handleLabelClick);
|
|
90
91
|
});
|
|
92
|
+
super.disconnectedCallback();
|
|
91
93
|
}
|
|
92
94
|
updated(changed) {
|
|
93
95
|
if (changed.has('checked') || changed.has('disabled')) {
|
|
@@ -116,6 +118,12 @@ export class ToggleButton extends Base {
|
|
|
116
118
|
detail: this.checked
|
|
117
119
|
}));
|
|
118
120
|
}
|
|
121
|
+
formResetCallback() {
|
|
122
|
+
this.checked = this.hasAttribute('checked');
|
|
123
|
+
}
|
|
124
|
+
formStateRestoreCallback(state, _reason) {
|
|
125
|
+
this.checked = state === 'on';
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
_ts_decorate([
|
|
121
129
|
property({
|
package/src/core/decorators.js
CHANGED
|
@@ -2,7 +2,7 @@ import { isServer } from 'lit';
|
|
|
2
2
|
import { customElement } from 'lit/decorators.js';
|
|
3
3
|
const wrappedCustomElement = (tagName, allowSSR = true)=>{
|
|
4
4
|
if (isServer && !allowSSR) {
|
|
5
|
-
console.log(`[
|
|
5
|
+
console.log(`[seele] <${tagName}> will not be rendered server-side`);
|
|
6
6
|
return (cls)=>cls;
|
|
7
7
|
} else {
|
|
8
8
|
return customElement(tagName);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const commonButtonStyles = css`@layer common-button{:host{--_padding:16px;--md-focus-ring-shape:calc(var(--_h) / 2);background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);
|
|
2
|
+
export const commonButtonStyles = css`@layer common-button{:host{--_padding:16px;--md-focus-ring-shape:calc(var(--_h) / 2);background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);color:color-mix(in srgb, var(--_text-color) var(--_text-opacity,100%), transparent);font:var(--md-sys-typography-label-large);min-width:64px;padding-inline:var(--_padding);transition:box-shadow var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default), border-radius var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast);gap:8px}:host([size=xs]){--_padding:12px;gap:4px}:host([size=m]){--_padding:24px;font:var(--md-sys-typography-title-medium);gap:8px}:host([size=l]){--_padding:48px;font:var(--md-sys-typography-headline-small);gap:12px}:host([size=xl]){--_padding:64px;font:var(--md-sys-typography-headline-medium);gap:16px}:host([trailing-icon]){flex-direction:row-reverse}:host([variant=outlined]){padding-inline:calc(var(--_padding) - 1px)}:host([variant=text]){padding-inline:calc(var(--_padding) / 4 * 3)}::slotted([slot=icon]),::slotted([slot=icon-checked]){fill:currentColor;block-size:1em;font-size:var(--_icon-size);inline-size:1em}}@layer override{:host([variant=elevated]){--_background-color:var(--md-sys-color-surface-container-low);--_text-color:var(--_color);box-shadow:var(--md-sys-elevation-shadow-1)}@media (hover:hover) and (pointer:fine){:host([variant=filled]:hover:not(:active)),:host([variant=tonal]:hover:not(:active)){box-shadow:var(--md-sys-elevation-shadow-1)}:host([variant=elevated]:hover:not(:active)){box-shadow:var(--md-sys-elevation-shadow-2)}}}`;
|
|
@@ -22,7 +22,7 @@ export class M3ButtonToggle extends ToggleButton {
|
|
|
22
22
|
return html`<md-focus-ring></md-focus-ring><md-ripple></md-ripple><span part="target"></span><slot part="icon unchecked" name="icon" aria-hidden="true"></slot><slot part="icon checked" name="icon-checked" aria-hidden="true"></slot><slot part="label unchecked"></slot><slot part="label checked" name="checked"></slot>`;
|
|
23
23
|
}
|
|
24
24
|
constructor(...args){
|
|
25
|
-
super(...args), this.size = '
|
|
25
|
+
super(...args), this.size = 's', this.square = false, this.color = 'primary', this.variant = 'filled', /**
|
|
26
26
|
* Whether to show the icon at the end of the button.
|
|
27
27
|
*/ this.trailingIcon = false;
|
|
28
28
|
}
|
|
@@ -34,9 +34,10 @@ _ts_decorate([
|
|
|
34
34
|
], M3ButtonToggle.prototype, "size", void 0);
|
|
35
35
|
_ts_decorate([
|
|
36
36
|
property({
|
|
37
|
-
reflect: true
|
|
37
|
+
reflect: true,
|
|
38
|
+
type: Boolean
|
|
38
39
|
})
|
|
39
|
-
], M3ButtonToggle.prototype, "
|
|
40
|
+
], M3ButtonToggle.prototype, "square", void 0);
|
|
40
41
|
_ts_decorate([
|
|
41
42
|
property({
|
|
42
43
|
reflect: true
|
|
@@ -20,7 +20,7 @@ export class M3Button extends Button {
|
|
|
20
20
|
return html`<md-focus-ring></md-focus-ring><md-ripple></md-ripple><span part="target"></span><slot part="icon" name="icon" aria-hidden="true"></slot><slot part="label"></slot>`;
|
|
21
21
|
}
|
|
22
22
|
constructor(...args){
|
|
23
|
-
super(...args), this.size = '
|
|
23
|
+
super(...args), this.size = 's', this.color = 'primary', this.variant = 'filled', /**
|
|
24
24
|
* Whether to show the icon at the end of the button.
|
|
25
25
|
*/ this.trailingIcon = false;
|
|
26
26
|
}
|
|
@@ -32,9 +32,10 @@ _ts_decorate([
|
|
|
32
32
|
], M3Button.prototype, "size", void 0);
|
|
33
33
|
_ts_decorate([
|
|
34
34
|
property({
|
|
35
|
-
reflect: true
|
|
35
|
+
reflect: true,
|
|
36
|
+
type: Boolean
|
|
36
37
|
})
|
|
37
|
-
], M3Button.prototype, "
|
|
38
|
+
], M3Button.prototype, "square", void 0);
|
|
38
39
|
_ts_decorate([
|
|
39
40
|
property({
|
|
40
41
|
reflect: true
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const iconButtonStyles = css`@layer icon-button{:host{--_wn:32px;--_ww:52px;background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);
|
|
2
|
+
export const iconButtonStyles = css`@layer icon-button{:host{--_wn:32px;--_ww:52px;background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);color:color-mix(in srgb, var(--_text-color) var(--_text-opacity,100%), transparent);transition:border-radius var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast);width:var(--_h)}:host([size=xs]){--_wn:28px;--_ww:40px}:host([size=m]){--_wn:48px;--_ww:72px}:host([size=l]){--_wn:64px;--_ww:128px}:host([size=xl]){--_wn:104px;--_ww:184px}:host([width=narrow]){width:var(--_wn)}:host([width=wide]){width:var(--_ww)}::slotted(*){fill:currentColor;block-size:1em;font-size:var(--_icon-size);inline-size:1em}}@layer override{:host([size=small]){--_icon-size:24px}:host([variant=text]){--_text-color:var(--md-sys-color-on-surface-variant)}:host([variant=text]:state(checked)){--_text-color:var(--_color)}}`;
|
|
@@ -22,7 +22,7 @@ export class M3IconButtonToggle extends ToggleButton {
|
|
|
22
22
|
return html`<md-focus-ring></md-focus-ring><md-ripple enterbehavior="always"></md-ripple><span part="target"></span><slot part="icon unchecked"></slot><slot part="icon checked" name="checked"></slot>`;
|
|
23
23
|
}
|
|
24
24
|
constructor(...args){
|
|
25
|
-
super(...args), this.size = '
|
|
25
|
+
super(...args), this.size = 's', this.color = 'primary', this.variant = 'text', this.width = 'standard';
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
_ts_decorate([
|
|
@@ -20,7 +20,7 @@ export class M3IconButton extends Button {
|
|
|
20
20
|
return html`<md-focus-ring></md-focus-ring><md-ripple></md-ripple><span part="target"></span><slot part="icon"></slot>`;
|
|
21
21
|
}
|
|
22
22
|
constructor(...args){
|
|
23
|
-
super(...args), this.size = '
|
|
23
|
+
super(...args), this.size = 's', this.color = 'primary', this.variant = 'text', this.width = 'standard';
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
_ts_decorate([
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const sharedButtonStyles = css`@layer common-button,icon-button;@layer shared{:host{--_on-color:var(--md-sys-color-on-primary);--_color:var(--md-sys-color-primary);--_h:40px;--_icon-size:20px;--
|
|
2
|
+
export const sharedButtonStyles = css`@layer common-button,icon-button;@layer shared{:host{--_on-color:var(--md-sys-color-on-primary);--_color:var(--md-sys-color-primary);--_h:40px;--_icon-size:20px;--_rs:12px;--_rp:8px;--_text-color:var(--_on-color);--_background-color:var(--_color);box-sizing:border-box;cursor:pointer;height:var(--_h);-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;vertical-align:top;border-start-start-radius:var(--_inner-left-radius,calc(var(--_h) / 2));border-start-end-radius:var(--_inner-right-radius,calc(var(--_h) / 2));border-end-end-radius:var(--_inner-right-radius,calc(var(--_h) / 2));border-end-start-radius:var(--_inner-left-radius,calc(var(--_h) / 2));outline:0;flex-shrink:0;justify-content:center;align-items:center;display:inline-flex;position:relative}:host([notransition]){transition:none!important}md-ripple,md-focus-ring{position:absolute}:host([size=xs]){--_h:32px}:host([size=m]){--_h:56px;--_icon-size:24px;--_rs:16px;--_rp:12px}:host([size=l]){--_h:96px;--_icon-size:32px;--_rs:28px;--_rp:16px}:host([size=xl]){--_h:136px;--_icon-size:40px;--_rs:28px;--_rp:16px}:host([color=secondary]){--_on-color:var(--md-sys-color-on-secondary);--_color:var(--md-sys-color-secondary)}:host([color=tertiary]){--_on-color:var(--md-sys-color-on-tertiary);--_color:var(--md-sys-color-tertiary)}:host([variant=tonal]){--_background-color:var(--md-sys-color-secondary-container);--_text-color:var(--md-sys-color-on-secondary-container)}:host([variant=outlined]){--_background-color:transparent;--_text-color:var(--md-sys-color-on-surface-variant);--md-focus-ring-offset:3px;border-color:color-mix(in srgb, var(--md-sys-color-outline-variant) 100%, transparent);border-style:solid;border-width:1px}:host([variant=text]){--_text-color:var(--_color);--_background-color:transparent!important}:host([square]){--md-focus-ring-shape:var(--_rs);border-radius:var(--_rs)}}@layer shared-toggle,override;@layer active{:host(:active){--md-focus-ring-shape:var(--_rp);border-radius:var(--_rp)}}@layer disabled{:host([variant=outlined]:disabled){--_background-color:var(--md-sys-color-outline)}:host(:disabled),:host([variant=outlined]:state(checked):disabled){--_background-color:var(--md-sys-color-on-surface);--_background-opacity:10%;--_text-color:var(--md-sys-color-on-surface);--_text-opacity:38%;border-color:color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent);box-shadow:none;pointer-events:none}}`;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const sharedButtonToggleStyles = css`@layer shared-toggle{:host{transition:border-radius var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast), box-shadow var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default), background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast), color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast)}:host(:state(checked)){--md-focus-ring-shape:var(--
|
|
2
|
+
export const sharedButtonToggleStyles = css`@layer shared-toggle{:host{transition:border-radius var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast), box-shadow var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default), background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast), color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast)}:host(:state(checked)){--md-focus-ring-shape:var(--_rs);border-radius:var(--_rs)}:host(:state(checked)) [part~=unchecked],:host(:not(:state(checked))) [part~=checked]{display:none}:host([variant=filled]:not(:state(checked))){--_background-color:var(--md-sys-color-surface-container);--_text-color:var(--md-sys-color-on-surface-variant)}:host([variant=tonal]:not(:state(checked))){--_background-color:var(--md-sys-color-surface-container-highest);--_text-color:var(--md-sys-color-on-surface-variant)}:host([variant=outlined]:state(checked)){--_background-color:var(--md-sys-color-inverse-surface);--_text-color:var(--md-sys-color-inverse-on-surface);border-width:0}}`;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const connectedButtonGroupStyles = css`:host{gap:2px;display:inline-flex}::slotted(*){flex:1}::slotted(:not(:first-child)[size=xs]){--_inner-left-radius:4px}::slotted(:not(:last-child)[size=xs]){--_inner-right-radius:4px}::slotted(:not(:first-child)){--_inner-left-radius:8px}::slotted(:not(:last-child)){--_inner-right-radius:8px}::slotted(:not(:first-child)[size=l]){--_inner-left-radius:16px}::slotted(:not(:last-child)[size=l]){--_inner-right-radius:16px}::slotted(:not(:first-child)[size=xl]){--_inner-left-radius:20px}::slotted(:not(:last-child)[size=xl]){--_inner-right-radius:20px}::slotted(:first-child:state(checked):not(:active,[square])){border-start-start-radius:calc(var(--_h) / 2);border-end-start-radius:calc(var(--_h) / 2)}::slotted(:last-child:state(checked):not(:active,[square])){border-start-end-radius:calc(var(--_h) / 2);border-end-end-radius:calc(var(--_h) / 2)}`;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
import { customElement } from '../../core/decorators.js';
|
|
5
|
+
import { connectedButtonGroupStyles } from './connected-button-group-styles.css.js';
|
|
6
|
+
export class M3ConnectedButtonGroup extends LitElement {
|
|
7
|
+
static{
|
|
8
|
+
this.styles = [
|
|
9
|
+
connectedButtonGroupStyles
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
render() {
|
|
13
|
+
return html`<slot @change="${this.#handleChange}"></slot>`;
|
|
14
|
+
}
|
|
15
|
+
#handleChange;
|
|
16
|
+
constructor(...args){
|
|
17
|
+
super(...args), this.multiple = false, this.#handleChange = (e)=>{
|
|
18
|
+
console.log('Boom!', e);
|
|
19
|
+
this.$buttons.forEach((btn, _)=>{
|
|
20
|
+
if (btn !== e.target && !this.multiple) // NOTE: there will not be an event dispatched and user should listen on
|
|
21
|
+
// the button group directly.
|
|
22
|
+
btn.checked = false;
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
_ts_decorate([
|
|
28
|
+
property({
|
|
29
|
+
type: Boolean
|
|
30
|
+
})
|
|
31
|
+
], M3ConnectedButtonGroup.prototype, "multiple", void 0);
|
|
32
|
+
_ts_decorate([
|
|
33
|
+
queryAssignedElements()
|
|
34
|
+
], M3ConnectedButtonGroup.prototype, "$buttons", void 0);
|
|
35
|
+
M3ConnectedButtonGroup = _ts_decorate([
|
|
36
|
+
customElement('md-connected-button-group')
|
|
37
|
+
], M3ConnectedButtonGroup);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const standardButtonGroupStyles = css`:host{gap:8px;display:inline-flex}`;
|
|
2
|
+
export const standardButtonGroupStyles = css`:host{gap:8px;display:inline-flex}:host([size=s]){gap:12px}:host([size=ss]){gap:18px}`;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
2
|
import { LitElement, html } from 'lit';
|
|
3
|
+
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
3
4
|
import { customElement } from '../../core/decorators.js';
|
|
4
|
-
import {
|
|
5
|
+
import { getSpring } from '../system/motion.js';
|
|
5
6
|
import { standardButtonGroupStyles } from './standard-button-group-styles.css.js';
|
|
6
7
|
const EXPAND_FACTOR = 1.15;
|
|
7
8
|
const shapeAnimation = Symbol('shapeAnimation');
|
|
@@ -74,11 +75,7 @@ export class M3StandardButtonGroup extends LitElement {
|
|
|
74
75
|
else targetWidth = this.#baseWidths[i] - expansionDelta / 2;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
|
-
const
|
|
78
|
-
const rawDuration = styles.getPropertyValue('--md-sys-motion-spatial-fast-duration');
|
|
79
|
-
const rawEasing = styles.getPropertyValue('--md-sys-motion-spatial-fast');
|
|
80
|
-
const duration = parseInt(rawDuration.substring(0, rawDuration.length - 2)) || 0;
|
|
81
|
-
const easing = rawEasing || 'ease';
|
|
78
|
+
const spring = getSpring(this, 'spatial', 'fast');
|
|
82
79
|
if (btn[shapeAnimation]) btn[shapeAnimation].cancel();
|
|
83
80
|
btn[shapeAnimation] = btn.animate([
|
|
84
81
|
{
|
|
@@ -88,8 +85,7 @@ export class M3StandardButtonGroup extends LitElement {
|
|
|
88
85
|
width: `${targetWidth}px`
|
|
89
86
|
}
|
|
90
87
|
], {
|
|
91
|
-
|
|
92
|
-
easing,
|
|
88
|
+
...spring,
|
|
93
89
|
fill: 'forwards'
|
|
94
90
|
});
|
|
95
91
|
btn[shapeAnimation].onfinish = ()=>{
|
|
@@ -101,7 +97,7 @@ export class M3StandardButtonGroup extends LitElement {
|
|
|
101
97
|
});
|
|
102
98
|
}
|
|
103
99
|
constructor(...args){
|
|
104
|
-
super(...args), this.#activeIndex = -1, this.#baseWidths = [], this.#handleSlotChange = ()=>{
|
|
100
|
+
super(...args), this.size = '', this.#activeIndex = -1, this.#baseWidths = [], this.#handleSlotChange = ()=>{
|
|
105
101
|
this.#resizeObserver.disconnect();
|
|
106
102
|
this.$buttons.forEach((btn, i)=>{
|
|
107
103
|
this.#baseWidths[i] = btn.getBoundingClientRect().width;
|
|
@@ -122,9 +118,14 @@ export class M3StandardButtonGroup extends LitElement {
|
|
|
122
118
|
};
|
|
123
119
|
}
|
|
124
120
|
}
|
|
121
|
+
_ts_decorate([
|
|
122
|
+
property({
|
|
123
|
+
reflect: true
|
|
124
|
+
})
|
|
125
|
+
], M3StandardButtonGroup.prototype, "size", void 0);
|
|
125
126
|
_ts_decorate([
|
|
126
127
|
queryAssignedElements()
|
|
127
128
|
], M3StandardButtonGroup.prototype, "$buttons", void 0);
|
|
128
129
|
M3StandardButtonGroup = _ts_decorate([
|
|
129
|
-
customElement('md-button-group'
|
|
130
|
+
customElement('md-button-group')
|
|
130
131
|
], M3StandardButtonGroup);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
2
|
import { html } from 'lit';
|
|
3
|
-
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
import { customElement, property, query } from 'lit/decorators.js';
|
|
4
4
|
import { Checkbox } from '../../base/checkbox.js';
|
|
5
5
|
import '../focus-ring/focus-ring.js';
|
|
6
|
+
import '../ripple/ripple.js';
|
|
6
7
|
import { checkboxStyles } from './checkbox-styles.css.js';
|
|
7
8
|
import { targetStyles } from '../target-styles.css.js';
|
|
8
9
|
export class M3Checkbox extends Checkbox {
|
|
@@ -19,6 +20,13 @@ export class M3Checkbox extends Checkbox {
|
|
|
19
20
|
renderIcon() {
|
|
20
21
|
return html`<svg part="icon" viewBox="0 0 18 18" aria-hidden="true"><rect part="mark mark-short"></rect><rect part="mark mark-long"></rect></svg>`;
|
|
21
22
|
}
|
|
23
|
+
connectedCallback() {
|
|
24
|
+
super.connectedCallback();
|
|
25
|
+
// SSR'd <md-checkbox> components don't have their labels set up on time
|
|
26
|
+
this.updateComplete.then(()=>{
|
|
27
|
+
this.$ripple.attach(this, true);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
22
30
|
constructor(...args){
|
|
23
31
|
super(...args), this.error = false;
|
|
24
32
|
}
|
|
@@ -29,6 +37,9 @@ _ts_decorate([
|
|
|
29
37
|
reflect: true
|
|
30
38
|
})
|
|
31
39
|
], M3Checkbox.prototype, "error", void 0);
|
|
40
|
+
_ts_decorate([
|
|
41
|
+
query('md-ripple')
|
|
42
|
+
], M3Checkbox.prototype, "$ripple", void 0);
|
|
32
43
|
M3Checkbox = _ts_decorate([
|
|
33
44
|
customElement('md-checkbox')
|
|
34
45
|
], M3Checkbox);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { query, state, customElement } from 'lit/decorators.js';
|
|
4
|
+
import { Dialog } from '../../base/dialog.js';
|
|
5
|
+
import { getSpring } from '../system/motion.js';
|
|
6
|
+
import { dialogStyles } from './dialog-styles.css.js';
|
|
7
|
+
export class M3Dialog extends Dialog {
|
|
8
|
+
static{
|
|
9
|
+
this.styles = [
|
|
10
|
+
dialogStyles
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
render() {
|
|
14
|
+
return html`<dialog part="dialog" @click="${this.#handleClick}" @cancel="${this._handleCancel}"><div part="container"><div part="body"><div part="headline" class="${this.hasIcon ? 'has-icon' : ''}"><slot name="icon" @slotchange="${this.#handleIconSlotChange}"></slot><slot name="headline"></slot></div><div part="content"><slot></slot></div><div class="actions-placeholder"></div></div><div part="actions"><slot name="actions"></slot></div></div></dialog><div part="scrim"></div>`;
|
|
15
|
+
}
|
|
16
|
+
#activeAnimations;
|
|
17
|
+
#clearAnimations() {
|
|
18
|
+
this.#activeAnimations.forEach((anim)=>anim.cancel());
|
|
19
|
+
this.#activeAnimations = [];
|
|
20
|
+
this.#opening = this.#closing = false;
|
|
21
|
+
}
|
|
22
|
+
#handleClick;
|
|
23
|
+
#handleIconSlotChange;
|
|
24
|
+
get #springs() {
|
|
25
|
+
return {
|
|
26
|
+
spatialDefault: getSpring(this, 'spatial', 'default'),
|
|
27
|
+
spatialFast: getSpring(this, 'spatial', 'fast'),
|
|
28
|
+
effectsDefault: getSpring(this, 'effects', 'default'),
|
|
29
|
+
effectsFast: getSpring(this, 'effects', 'fast')
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
#opening;
|
|
33
|
+
show() {
|
|
34
|
+
if (this.#opening) return;
|
|
35
|
+
this.#clearAnimations();
|
|
36
|
+
this.#opening = true;
|
|
37
|
+
// Reset margins to allow browser centering
|
|
38
|
+
this.$dialog.style.marginTop = 'auto';
|
|
39
|
+
this.$dialog.style.marginBottom = 'auto';
|
|
40
|
+
super.show();
|
|
41
|
+
const actionsHeight = this.$actions.offsetHeight;
|
|
42
|
+
this.$actionsPlaceholder.style.height = `${actionsHeight}px`;
|
|
43
|
+
this.$container.style.height = 'auto';
|
|
44
|
+
const startHeight = 0;
|
|
45
|
+
const endHeight = this.$container.offsetHeight;
|
|
46
|
+
// Lock the position to prevent auto-centering during animation
|
|
47
|
+
const rect = this.$container.getBoundingClientRect();
|
|
48
|
+
this.$dialog.style.marginTop = `${rect.top}px`;
|
|
49
|
+
this.$dialog.style.marginBottom = 'auto';
|
|
50
|
+
this.$container.style.minHeight = '0px';
|
|
51
|
+
const container = this.$container.animate([
|
|
52
|
+
{
|
|
53
|
+
height: `${startHeight}px`,
|
|
54
|
+
transform: `translateY(-40px)`
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
height: `${endHeight}px`,
|
|
58
|
+
transform: 'translateY(0px)'
|
|
59
|
+
}
|
|
60
|
+
], this.#springs.spatialDefault);
|
|
61
|
+
const scrim = this.$scrim.animate([
|
|
62
|
+
{
|
|
63
|
+
opacity: 0
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
opacity: 0.32
|
|
67
|
+
}
|
|
68
|
+
], {
|
|
69
|
+
...this.#springs.effectsDefault,
|
|
70
|
+
fill: 'forwards'
|
|
71
|
+
});
|
|
72
|
+
const body = this.$body.animate([
|
|
73
|
+
{
|
|
74
|
+
opacity: 0.2
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
opacity: 1
|
|
78
|
+
}
|
|
79
|
+
], {
|
|
80
|
+
...this.#springs.effectsDefault,
|
|
81
|
+
fill: 'forwards'
|
|
82
|
+
});
|
|
83
|
+
const actions = this.$actions.animate([
|
|
84
|
+
{
|
|
85
|
+
opacity: 0.5
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
opacity: 1
|
|
89
|
+
}
|
|
90
|
+
], {
|
|
91
|
+
...this.#springs.effectsDefault,
|
|
92
|
+
fill: 'forwards'
|
|
93
|
+
});
|
|
94
|
+
this.#activeAnimations.push(container, scrim, body, actions);
|
|
95
|
+
container.onfinish = ()=>{
|
|
96
|
+
this.$container.style.minHeight = '';
|
|
97
|
+
this.#clearAnimations();
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
#closing;
|
|
101
|
+
close() {
|
|
102
|
+
if (this.#closing) return;
|
|
103
|
+
this.#clearAnimations();
|
|
104
|
+
this.#closing = true;
|
|
105
|
+
const startHeight = this.$container.offsetHeight;
|
|
106
|
+
const endHeight = startHeight * 0.35;
|
|
107
|
+
this.$container.style.minHeight = '0px';
|
|
108
|
+
const containerSpatial = this.$container.animate([
|
|
109
|
+
{
|
|
110
|
+
height: `${startHeight}px`,
|
|
111
|
+
transform: 'translateY(0px)'
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
height: `${endHeight}px`,
|
|
115
|
+
transform: `translateY(-40px)`
|
|
116
|
+
}
|
|
117
|
+
], this.#springs.spatialFast);
|
|
118
|
+
const containerEffects = this.$container.animate([
|
|
119
|
+
{
|
|
120
|
+
opacity: 1
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
opacity: 0
|
|
124
|
+
}
|
|
125
|
+
], {
|
|
126
|
+
...this.#springs.effectsFast,
|
|
127
|
+
fill: 'forwards'
|
|
128
|
+
});
|
|
129
|
+
const scrim = this.$scrim.animate([
|
|
130
|
+
{
|
|
131
|
+
opacity: 0.32
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
opacity: 0
|
|
135
|
+
}
|
|
136
|
+
], {
|
|
137
|
+
...this.#springs.effectsFast,
|
|
138
|
+
fill: 'forwards'
|
|
139
|
+
});
|
|
140
|
+
this.#activeAnimations.push(containerSpatial, containerEffects, scrim);
|
|
141
|
+
containerEffects.onfinish = ()=>{
|
|
142
|
+
this.$container.style.minHeight = '';
|
|
143
|
+
super.close();
|
|
144
|
+
this.#clearAnimations();
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
constructor(...args){
|
|
148
|
+
super(...args), this.hasIcon = false, this.#activeAnimations = [], this.#handleClick = (e)=>{
|
|
149
|
+
const target = e.target;
|
|
150
|
+
if (!this.$container.contains(target) && !this.contains(target)) this.close();
|
|
151
|
+
}, this.#handleIconSlotChange = (e)=>{
|
|
152
|
+
const slot = e.target;
|
|
153
|
+
this.hasIcon = slot.assignedElements({
|
|
154
|
+
flatten: true
|
|
155
|
+
}).length > 0;
|
|
156
|
+
}, this.#opening = false, this.#closing = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
_ts_decorate([
|
|
160
|
+
state()
|
|
161
|
+
], M3Dialog.prototype, "hasIcon", void 0);
|
|
162
|
+
_ts_decorate([
|
|
163
|
+
query('[part=container]')
|
|
164
|
+
], M3Dialog.prototype, "$container", void 0);
|
|
165
|
+
_ts_decorate([
|
|
166
|
+
query('[part=body]')
|
|
167
|
+
], M3Dialog.prototype, "$body", void 0);
|
|
168
|
+
_ts_decorate([
|
|
169
|
+
query('[part=actions]')
|
|
170
|
+
], M3Dialog.prototype, "$actions", void 0);
|
|
171
|
+
_ts_decorate([
|
|
172
|
+
query('.actions-placeholder')
|
|
173
|
+
], M3Dialog.prototype, "$actionsPlaceholder", void 0);
|
|
174
|
+
_ts_decorate([
|
|
175
|
+
query('[part=scrim]')
|
|
176
|
+
], M3Dialog.prototype, "$scrim", void 0);
|
|
177
|
+
M3Dialog = _ts_decorate([
|
|
178
|
+
customElement('md-dialog')
|
|
179
|
+
], M3Dialog);
|
package/src/m3/dialog/dialog.js
CHANGED
|
@@ -172,16 +172,17 @@ export class M3Dialog extends Dialog {
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
constructor(...args){
|
|
175
|
-
super(...args),
|
|
175
|
+
super(...args), // Was based on https://m3.material.io/styles/motion/easing-and-duration/applying-easing-and-duration#e5b958f0-435d-4e84-aed4-8d1ea395fa5c
|
|
176
|
+
// TODO: use curves and durations from motion physics
|
|
177
|
+
this._config = {
|
|
176
178
|
openEase: 'cubic-bezier(0, 0, 0, 1)',
|
|
177
179
|
closeEase: 'cubic-bezier(0.3, 0, 1, 1)',
|
|
178
|
-
vertSlide:
|
|
180
|
+
vertSlide: 40,
|
|
179
181
|
openDur: 500,
|
|
180
182
|
closeDur: 200,
|
|
181
183
|
bodyFadeInDur: 200,
|
|
182
184
|
bodyFadeOutDur: 100
|
|
183
|
-
}
|
|
184
|
-
making classes inheriting this class... */ , this.hasIcon = false, this.#activeAnimations = [], this.#handleClick = (e)=>{
|
|
185
|
+
}, this.hasIcon = false, this.#activeAnimations = [], this.#handleClick = (e)=>{
|
|
185
186
|
const target = e.target;
|
|
186
187
|
if (!this.$container.contains(target) && !this.contains(target)) this.close();
|
|
187
188
|
}, this.#handleIconSlotChange = (e)=>{
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
export const fabStyles = css`:host{--md-focus-ring-shape:var(--_border-radius);--_text-color:var(--md-sys-color-on-primary);--_background-color:var(--md-sys-color-primary);--_size:56px;--_border-radius:16px;--_icon-size:24px;background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);border-radius:var(--_border-radius);box-shadow:var(--md-sys-elevation-shadow-3);box-sizing:border-box;color:color-mix(in srgb, var(--_text-color) var(--_text-opacity,100%), transparent);cursor:pointer;font:var(--md-sys-typography-label-large);height:var(--_size);min-width:var(--_size);padding-inline:calc((var(--_size) - var(--_icon-size)) / 2);-webkit-tap-highlight-color:transparent;transition:box-shadow var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);-webkit-user-select:none;user-select:none;vertical-align:middle;outline:0;justify-content:center;align-items:center;gap:8px;display:inline-flex;position:relative}:host([size=
|
|
2
|
+
export const fabStyles = css`:host{--md-focus-ring-shape:var(--_border-radius);--_text-color:var(--md-sys-color-on-primary);--_background-color:var(--md-sys-color-primary);--_size:56px;--_border-radius:16px;--_icon-size:24px;background-color:color-mix(in srgb, var(--_background-color) var(--_background-opacity,100%), transparent);border-radius:var(--_border-radius);box-shadow:var(--md-sys-elevation-shadow-3);box-sizing:border-box;color:color-mix(in srgb, var(--_text-color) var(--_text-opacity,100%), transparent);cursor:pointer;font:var(--md-sys-typography-label-large);height:var(--_size);min-width:var(--_size);padding-inline:calc((var(--_size) - var(--_icon-size)) / 2);-webkit-tap-highlight-color:transparent;transition:box-shadow var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);-webkit-user-select:none;user-select:none;vertical-align:middle;outline:0;justify-content:center;align-items:center;gap:8px;display:inline-flex;position:relative}md-ripple,md-focus-ring{position:absolute}:host([size=m]){--_size:80px;--_border-radius:20px;--_icon-size:28px}:host([size=l]){--_size:96px;--_border-radius:28px;--_icon-size:36px}:host([color=primary-container]){--_text-color:var(--md-sys-color-on-primary-container);--_background-color:var(--md-sys-color-primary-container)}:host([color=secondary-container]){--_text-color:var(--md-sys-color-on-secondary-container);--_background-color:var(--md-sys-color-secondary-container)}:host([color=tertiary-container]){--_text-color:var(--md-sys-color-on-tertiary-container);--_background-color:var(--md-sys-color-tertiary-container)}:host([color=secondary]){--_text-color:var(--md-sys-color-on-secondary);--_background-color:var(--md-sys-color-secondary)}:host([color=tertiary]){--_text-color:var(--md-sys-color-on-tertiary);--_background-color:var(--md-sys-color-tertiary)}:host([color=surface]){--_text-color:var(--md-sys-color-primary);--_background-color:var(--md-sys-color-surface-container-high)}:host(:disabled){--_background-color:var(--md-sys-color-on-surface);--_background-opacity:12%;--_text-color:var(--md-sys-color-on-surface);--_text-opacity:38%;box-shadow:none;pointer-events:none}@media (hover:hover) and (pointer:fine){:host(:hover:not(:active)){box-shadow:var(--md-sys-elevation-shadow-4)}}::slotted(:not([slot=label])){fill:currentColor;block-size:1em;font-size:var(--_icon-size);inline-size:1em}::slotted([slot=label]){margin-inline:4px}`;
|
package/src/m3/fab/fab.js
CHANGED
|
@@ -18,7 +18,8 @@ export class M3Fab extends Button {
|
|
|
18
18
|
return html`<md-focus-ring></md-focus-ring><md-ripple></md-ripple><span part="target"></span><slot part="icon" aria-hidden="true"></slot><slot part="label" name="label"></slot>`;
|
|
19
19
|
}
|
|
20
20
|
constructor(...args){
|
|
21
|
-
super(...args), this.size = '
|
|
21
|
+
super(...args), this.size = 's' // s for default
|
|
22
|
+
, this.color = 'primary';
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
_ts_decorate([
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const radioStyles = css`:host{--_default:var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);--_slow:var(--md-sys-motion-effects-slow-duration) var(--md-sys-motion-effects-slow);cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;border-radius:20px;outline:0;justify-content:center;align-items:center;width:40px;height:40px;display:inline-flex;position:relative}:host:before,:host:after{content:"";box-sizing:border-box;border-radius:9999px;position:absolute}:host:before{z-index:1;background-color:var(--md-sys-color-on-surface-variant);width:20px;height:20px;transition:background var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast)}.middle{background-color:var(--md-sys-color-surface);z-index:2;width:16px;height:16px;animation:uncheck-middle-2 var(--_slow) var(--md-sys-motion-effects-default-duration);border-radius:9999px}:host:after{z-index:3;background-color:var(--md-sys-color-primary);width:10px;height:10px;animation:uncheck-inner-1 var(--_default), uncheck-inner-2 var(--_slow) var(--md-sys-motion-effects-default-duration);transform:scale(0)}@keyframes uncheck-middle-2{0%{z-index:4;transform:scale(0)}to{z-index:4;transform:scale(1)}}@keyframes uncheck-inner-1{0%{background-color:var(--md-sys-color-primary);transform:scale(1)}to{background-color:var(--md-sys-color-on-surface-variant);transform:scale(1.6)}}@keyframes uncheck-inner-2{0%{background-color:var(--md-sys-color-on-surface-variant);transform:scale(1.6)}to{background-color:var(--md-sys-color-on-surface-variant);transform:scale(1.6)}}:host([checked]):before{background-color:var(--md-sys-color-primary)}:host([checked]) .middle{animation:check-middle-1 var(--_default), check-middle-2 var(--_slow) var(--md-sys-motion-effects-default-duration);animation-fill-mode:forwards}:host([checked]):after{animation:check-inner-2 var(--_slow) var(--md-sys-motion-effects-default-duration);animation-fill-mode:forwards}@keyframes check-middle-1{0%{transform:scale(1)}to{transform:scale(0)}}@keyframes check-middle-2{0%{transform:scale(.625)}to{transform:scale(1)}}@keyframes check-inner-2{0%,to{transform:scale(1)}}:host([disabled]){pointer-events:none}:host([disabled]):before,:host([disabled]):after{background:color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent)}`;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { Radio } from '../../base/radio.js';
|
|
4
|
+
import { customElement } from '../../core/decorators.js';
|
|
5
|
+
import { radioStyles } from './radio-styles.css.js';
|
|
6
|
+
import { targetStyles } from '../target-styles.css.js';
|
|
7
|
+
import '../focus-ring/focus-ring.js';
|
|
8
|
+
import '../ripple/ripple.js';
|
|
9
|
+
export class M3Radio extends Radio {
|
|
10
|
+
static{
|
|
11
|
+
this.styles = [
|
|
12
|
+
targetStyles,
|
|
13
|
+
radioStyles
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
render() {
|
|
17
|
+
return html`<md-focus-ring></md-focus-ring><md-ripple></md-ripple><span part="target"></span> <span class="middle"></span>`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
M3Radio = _ts_decorate([
|
|
21
|
+
customElement('md-radio')
|
|
22
|
+
], M3Radio);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workaround for `CSSNumericValue.parse()` not being supported by Firefox
|
|
3
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/CSSNumericValue/parse_static
|
|
4
|
+
*/ export const parseCSSNum = (css)=>{
|
|
5
|
+
if (typeof CSSNumericValue !== 'undefined') {
|
|
6
|
+
try {
|
|
7
|
+
return CSSNumericValue.parse(css).to('ms').value;
|
|
8
|
+
} catch {
|
|
9
|
+
return 0;
|
|
10
|
+
}
|
|
11
|
+
} else {
|
|
12
|
+
css = css.trim().toLowerCase();
|
|
13
|
+
if (css.endsWith('ms')) return parseFloat(css.substring(0, css.length - 2)) | 0;
|
|
14
|
+
else if (css.endsWith('s')) return parseFloat(css.substring(0, css.length - 1)) * 1000 | 0;
|
|
15
|
+
else return 0;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* https://m3.material.io/styles/motion/overview/specs
|
|
20
|
+
*/ export const getSpring = (element, type, duration)=>{
|
|
21
|
+
const styles = getComputedStyle(element);
|
|
22
|
+
const get = (suffix)=>styles.getPropertyValue(`--md-sys-motion-${type}-${duration}${suffix}`);
|
|
23
|
+
return {
|
|
24
|
+
easing: get('') || 'ease',
|
|
25
|
+
duration: parseCSSNum(get('-duration'))
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { Radio } from '../../base/radio.js';
|
|
3
|
+
import { customElement } from '../../core/decorators.js';
|
|
4
|
+
export class Win98Radio extends Radio {
|
|
5
|
+
}
|
|
6
|
+
Win98Radio = _ts_decorate([
|
|
7
|
+
customElement('win98-radio')
|
|
8
|
+
], Win98Radio);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
2
|
+
import { Radio } from '../../base/radio.js';
|
|
3
|
+
import { customElement } from '../../core/decorators.js';
|
|
4
|
+
export class Win98Radio extends Radio {
|
|
5
|
+
}
|
|
6
|
+
Win98Radio = _ts_decorate([
|
|
7
|
+
customElement('win98-radio')
|
|
8
|
+
], Win98Radio);
|