@vollowx/seele 0.13.4 → 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/package.json +1 -1
- package/src/all.js +1 -0
- package/src/base/autocomplete.js +7 -0
- package/src/base/button.js +6 -4
- package/src/base/radio.js +162 -0
- package/src/base/toggle-button.js +2 -2
- package/src/core/decorators.js +1 -1
- package/src/m3/button-group/connected-button-group.js +6 -6
- package/src/m3/button-group/standard-button-group.js +1 -1
- package/src/m3/checkbox/checkbox.js +1 -0
- package/src/m3/radio/radio-styles.css.js +2 -0
- package/src/m3/radio/radio.js +22 -0
- package/src/win98/button/radio.js +8 -0
- package/src/win98/radio/radio.js +8 -0
package/package.json
CHANGED
package/src/all.js
CHANGED
|
@@ -17,6 +17,7 @@ import './m3/list/list-item.js';
|
|
|
17
17
|
import './m3/loading-indicator/loading-indicator.js';
|
|
18
18
|
import './m3/menu/menu.js';
|
|
19
19
|
import './m3/menu/menu-item.js';
|
|
20
|
+
import './m3/radio/radio.js';
|
|
20
21
|
import './m3/ripple/ripple.js';
|
|
21
22
|
import './m3/select/filled-select.js';
|
|
22
23
|
import './m3/select/outlined-select.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,16 +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);
|
|
37
|
-
|
|
38
|
-
// `border-radius` on `:host`. Also in `ToggleButton`.
|
|
39
|
-
this.setAttribute('notransition', '');
|
|
40
|
-
requestAnimationFrame(()=>this.removeAttribute('notransition'));
|
|
42
|
+
requestAnimationFrame(()=>requestAnimationFrame(()=>this.removeAttribute('notransition')));
|
|
41
43
|
}
|
|
42
44
|
disconnectedCallback() {
|
|
43
45
|
super.disconnectedCallback();
|
|
@@ -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);
|
|
@@ -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,8 +80,7 @@ export class ToggleButton extends Base {
|
|
|
79
80
|
this.labels.forEach((label)=>{
|
|
80
81
|
label.addEventListener('click', this.#handleLabelClick);
|
|
81
82
|
});
|
|
82
|
-
this.
|
|
83
|
-
requestAnimationFrame(()=>this.removeAttribute('notransition'));
|
|
83
|
+
requestAnimationFrame(()=>requestAnimationFrame(()=>this.removeAttribute('notransition')));
|
|
84
84
|
}
|
|
85
85
|
disconnectedCallback() {
|
|
86
86
|
this.removeEventListener('click', this.#handleClick);
|
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(`[seele]
|
|
5
|
+
console.log(`[seele] <${tagName}> will not be rendered server-side`);
|
|
6
6
|
return (cls)=>cls;
|
|
7
7
|
} else {
|
|
8
8
|
return customElement(tagName);
|
|
@@ -3,7 +3,7 @@ import { LitElement, html } from 'lit';
|
|
|
3
3
|
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
4
|
import { customElement } from '../../core/decorators.js';
|
|
5
5
|
import { connectedButtonGroupStyles } from './connected-button-group-styles.css.js';
|
|
6
|
-
export class
|
|
6
|
+
export class M3ConnectedButtonGroup extends LitElement {
|
|
7
7
|
static{
|
|
8
8
|
this.styles = [
|
|
9
9
|
connectedButtonGroupStyles
|
|
@@ -28,10 +28,10 @@ _ts_decorate([
|
|
|
28
28
|
property({
|
|
29
29
|
type: Boolean
|
|
30
30
|
})
|
|
31
|
-
],
|
|
31
|
+
], M3ConnectedButtonGroup.prototype, "multiple", void 0);
|
|
32
32
|
_ts_decorate([
|
|
33
33
|
queryAssignedElements()
|
|
34
|
-
],
|
|
35
|
-
|
|
36
|
-
customElement('md-connected-button-group'
|
|
37
|
-
],
|
|
34
|
+
], M3ConnectedButtonGroup.prototype, "$buttons", void 0);
|
|
35
|
+
M3ConnectedButtonGroup = _ts_decorate([
|
|
36
|
+
customElement('md-connected-button-group')
|
|
37
|
+
], M3ConnectedButtonGroup);
|
|
@@ -3,6 +3,7 @@ import { html } from 'lit';
|
|
|
3
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 {
|
|
@@ -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,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);
|