@jungherz-de/glasskit-elements 1.9.0 → 1.11.0
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 +18 -3
- package/dist/components/glk-accordion-item.js +1 -1
- package/dist/components/glk-accordion.js +1 -1
- package/dist/components/glk-avatar.js +1 -1
- package/dist/components/glk-badge.js +1 -1
- package/dist/components/glk-button.js +1 -1
- package/dist/components/glk-card.js +1 -1
- package/dist/components/glk-checkbox.js +1 -1
- package/dist/components/glk-divider.js +1 -1
- package/dist/components/glk-input.js +1 -1
- package/dist/components/glk-list-item.js +1 -1
- package/dist/components/glk-list.js +1 -1
- package/dist/components/glk-modal.js +1 -1
- package/dist/components/glk-nav.js +1 -1
- package/dist/components/glk-pill.js +1 -1
- package/dist/components/glk-popover.js +1 -1
- package/dist/components/glk-progress.js +1 -1
- package/dist/components/glk-radio.js +89 -8
- package/dist/components/glk-range.js +1 -1
- package/dist/components/glk-search.js +1 -1
- package/dist/components/glk-select.js +1 -1
- package/dist/components/glk-status.js +1 -1
- package/dist/components/glk-tab-accessory.js +1 -1
- package/dist/components/glk-tab-bar.js +1 -1
- package/dist/components/glk-tab-dock.js +1 -1
- package/dist/components/glk-tab-item.js +1 -1
- package/dist/components/glk-textarea.js +1 -1
- package/dist/components/glk-title.js +1 -1
- package/dist/components/glk-toast.js +1 -1
- package/dist/components/glk-toggle.js +1 -1
- package/dist/components/shared/base-C0B1hgOt.js +240 -0
- package/dist/glasskit-elements.esm.js +91 -10
- package/dist/glasskit-elements.js +91 -10
- package/dist/glasskit-elements.min.js +1 -1
- package/package.json +3 -3
- package/src/components/forms/glk-radio.js +88 -7
- package/dist/components/shared/base-C4ZYKLPu.js +0 -240
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<a href="#"><img src="https://img.shields.io/badge/vanilla_JS-no_dependencies-44cc11?style=flat-square" alt="Vanilla JS"></a>
|
|
6
6
|
<a href="#"><img src="https://img.shields.io/badge/components-29-7ec8e3?style=flat-square" alt="29 Components"></a>
|
|
7
7
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT License"></a>
|
|
8
|
-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.
|
|
8
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.11.0-lightgrey?style=flat-square" alt="Changelog"></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@jungherz-de/glasskit-elements"><img src="https://img.shields.io/badge/npm-%40jungherz--de%2Fglasskit--elements-cb3837?style=flat-square&logo=npm" alt="npm"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
@@ -66,7 +66,7 @@ It is the **app layer** of the GlassKit family — three layers, one design lang
|
|
|
66
66
|
|
|
67
67
|
```html
|
|
68
68
|
<!-- 1. GlassKit CSS -->
|
|
69
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.
|
|
69
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.11/glasskit.min.css">
|
|
70
70
|
|
|
71
71
|
<!-- 2. GlassKit Elements -->
|
|
72
72
|
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
@@ -99,7 +99,7 @@ import '@jungherz-de/glasskit-elements/components/glk-toggle.js';
|
|
|
99
99
|
<!DOCTYPE html>
|
|
100
100
|
<html data-theme="dark">
|
|
101
101
|
<head>
|
|
102
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.
|
|
102
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.11/glasskit.min.css">
|
|
103
103
|
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
104
104
|
</head>
|
|
105
105
|
<body>
|
|
@@ -192,6 +192,21 @@ const current = html.getAttribute('data-theme');
|
|
|
192
192
|
html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
+
### Icons
|
|
196
|
+
|
|
197
|
+
Pass an icon as a **direct child**, not wrapped:
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<glk-button><svg viewBox="0 0 24 24">…</svg>Save</glk-button>
|
|
201
|
+
<glk-list-item title="Download"><svg slot="leading" viewBox="0 0 24 24">…</svg></glk-list-item>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> **Requires GlassKit CSS >= 1.10.0.** The icon rules are descendant selectors, and a
|
|
205
|
+
> slotted icon stays in the light DOM — before 1.10.0 they never matched it, so button
|
|
206
|
+
> icons filled their container and list icons collapsed to 0×0. Wrapping the icon in a
|
|
207
|
+
> `<span slot="…">` still does not work: `::slotted()` cannot reach inside an assigned
|
|
208
|
+
> node. Size it yourself if you must wrap it.
|
|
209
|
+
|
|
195
210
|
### Branding
|
|
196
211
|
|
|
197
212
|
Override GlassKit's `--gl-*` tokens anywhere in the document. Every `<glk-*>` element
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GlkElement } from './shared/base-
|
|
1
|
+
import { G as GlkElement } from './shared/base-C0B1hgOt.js';
|
|
2
2
|
|
|
3
3
|
const CHEVRON_SVG = `<span class="glass-accordion__trigger-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="6 9 12 15 18 9"/></svg></span>`;
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GlkElement } from './shared/base-
|
|
1
|
+
import { G as GlkElement } from './shared/base-C0B1hgOt.js';
|
|
2
2
|
|
|
3
3
|
// <glk-card fill> stretches the visible card to the height of its grid or flex
|
|
4
4
|
// cell. The host must become a grid for this: it is stretched by the outer
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as GlkFormElement } from './shared/base-
|
|
1
|
+
import { a as GlkFormElement } from './shared/base-C0B1hgOt.js';
|
|
2
2
|
|
|
3
3
|
const CHECKMARK_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
|
4
4
|
|
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
import { a as GlkFormElement } from './shared/base-
|
|
1
|
+
import { a as GlkFormElement } from './shared/base-C0B1hgOt.js';
|
|
2
|
+
|
|
3
|
+
// ── Grouping ──
|
|
4
|
+
// Every <glk-radio> keeps its <input type="radio"> in its own shadow root, and
|
|
5
|
+
// native radio grouping works per tree — it does not reach across shadow
|
|
6
|
+
// boundaries. Two <glk-radio name="x"> would therefore both stay checked. So
|
|
7
|
+
// the group is kept here instead, following the native definition as closely
|
|
8
|
+
// as we can: same `name`, same containing tree, same form owner.
|
|
9
|
+
|
|
10
|
+
const ARROW_KEYS = {
|
|
11
|
+
ArrowDown: 1, ArrowRight: 1,
|
|
12
|
+
ArrowUp: -1, ArrowLeft: -1
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** Form owner, also for a peer that has not been upgraded yet. */
|
|
16
|
+
function ownerForm(el) {
|
|
17
|
+
return (el.form !== undefined ? el.form : el.closest('form')) ?? null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Only the selected radio is a tab stop; arrow keys move within the group. */
|
|
21
|
+
function syncGroupTabIndex(group) {
|
|
22
|
+
const enabled = group.filter(el => !el.disabled);
|
|
23
|
+
if (!enabled.length) return;
|
|
24
|
+
const focusable = enabled.find(el => el.checked) || enabled[0];
|
|
25
|
+
for (const el of group) {
|
|
26
|
+
if (el._input) el._input.tabIndex = el === focusable ? 0 : -1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
2
29
|
|
|
3
30
|
class GlkRadio extends GlkFormElement {
|
|
4
31
|
static get observedAttributes() {
|
|
@@ -35,22 +62,67 @@ class GlkRadio extends GlkFormElement {
|
|
|
35
62
|
this._defaultChecked = this.getBoolAttr('checked');
|
|
36
63
|
this._wrapper.appendChild(label);
|
|
37
64
|
this._syncFormValue();
|
|
65
|
+
|
|
66
|
+
// Elements upgrade in document order, so the last `checked` one in the
|
|
67
|
+
// markup wins the group — the same outcome native radios produce.
|
|
68
|
+
if (this._input.checked) this._uncheckPeers();
|
|
69
|
+
syncGroupTabIndex(this._group());
|
|
38
70
|
}
|
|
39
71
|
|
|
40
72
|
setupEvents() {
|
|
41
|
-
this._onChange = () =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
73
|
+
this._onChange = () => this._applyChange();
|
|
74
|
+
|
|
75
|
+
this._onKeyDown = (e) => {
|
|
76
|
+
const dir = ARROW_KEYS[e.key];
|
|
77
|
+
if (!dir || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
78
|
+
const group = this._group().filter(el => !el.disabled);
|
|
79
|
+
if (group.length < 2) return;
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
const next = group[(group.indexOf(this) + dir + group.length) % group.length];
|
|
82
|
+
next._input.focus();
|
|
83
|
+
next._input.checked = true;
|
|
84
|
+
next._applyChange();
|
|
48
85
|
};
|
|
86
|
+
|
|
49
87
|
this._input.addEventListener('change', this._onChange);
|
|
88
|
+
this._input.addEventListener('keydown', this._onKeyDown);
|
|
50
89
|
}
|
|
51
90
|
|
|
52
91
|
teardownEvents() {
|
|
53
92
|
this._input?.removeEventListener('change', this._onChange);
|
|
93
|
+
this._input?.removeEventListener('keydown', this._onKeyDown);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Shared by user change and arrow-key selection, so both look identical. */
|
|
97
|
+
_applyChange() {
|
|
98
|
+
if (this._input.checked) this._uncheckPeers();
|
|
99
|
+
this._syncing = true;
|
|
100
|
+
this.setBoolAttr('checked', this._input.checked);
|
|
101
|
+
this._syncing = false;
|
|
102
|
+
this._syncFormValue();
|
|
103
|
+
syncGroupTabIndex(this._group());
|
|
104
|
+
this.emit('glk-change', { checked: this._input.checked, value: this._input.value });
|
|
105
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The radio group per the HTML definition: same name, same tree, same form
|
|
110
|
+
* owner — in document order, this element included.
|
|
111
|
+
*/
|
|
112
|
+
_group() {
|
|
113
|
+
const name = this.getAttribute('name');
|
|
114
|
+
const root = this.getRootNode();
|
|
115
|
+
if (!name || typeof root?.querySelectorAll !== 'function') return [this];
|
|
116
|
+
const form = ownerForm(this);
|
|
117
|
+
return [...root.querySelectorAll('glk-radio')].filter(
|
|
118
|
+
el => el.getAttribute('name') === name && ownerForm(el) === form
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_uncheckPeers() {
|
|
123
|
+
for (const el of this._group()) {
|
|
124
|
+
if (el !== this && el.checked) el.checked = false;
|
|
125
|
+
}
|
|
54
126
|
}
|
|
55
127
|
|
|
56
128
|
onAttributeChanged(name) {
|
|
@@ -59,16 +131,20 @@ class GlkRadio extends GlkFormElement {
|
|
|
59
131
|
switch (name) {
|
|
60
132
|
case 'checked':
|
|
61
133
|
this._input.checked = this.getBoolAttr('checked');
|
|
134
|
+
if (this._input.checked) this._uncheckPeers();
|
|
62
135
|
this._syncFormValue();
|
|
136
|
+
syncGroupTabIndex(this._group());
|
|
63
137
|
break;
|
|
64
138
|
case 'disabled':
|
|
65
139
|
this._input.disabled = this.getBoolAttr('disabled');
|
|
140
|
+
syncGroupTabIndex(this._group());
|
|
66
141
|
break;
|
|
67
142
|
case 'label':
|
|
68
143
|
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
69
144
|
break;
|
|
70
145
|
case 'name':
|
|
71
146
|
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
147
|
+
syncGroupTabIndex(this._group());
|
|
72
148
|
break;
|
|
73
149
|
case 'value':
|
|
74
150
|
this._input.setAttribute('value', this.getAttribute('value') || '');
|
|
@@ -86,13 +162,18 @@ class GlkRadio extends GlkFormElement {
|
|
|
86
162
|
this._input.checked = this._defaultChecked;
|
|
87
163
|
this.setBoolAttr('checked', this._defaultChecked);
|
|
88
164
|
this._syncFormValue();
|
|
165
|
+
syncGroupTabIndex(this._group());
|
|
89
166
|
}
|
|
90
167
|
|
|
91
168
|
get checked() { return this._input?.checked ?? false; }
|
|
92
169
|
set checked(v) {
|
|
93
170
|
if (this._input) this._input.checked = v;
|
|
171
|
+
// Also covers the case where the attribute is already present, so
|
|
172
|
+
// setBoolAttr stays silent and onAttributeChanged never runs.
|
|
173
|
+
if (v) this._uncheckPeers();
|
|
94
174
|
this.setBoolAttr('checked', v);
|
|
95
175
|
this._syncFormValue();
|
|
176
|
+
syncGroupTabIndex(this._group());
|
|
96
177
|
}
|
|
97
178
|
|
|
98
179
|
get disabled() { return this.getBoolAttr('disabled'); }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as GlkFormElement } from './shared/base-
|
|
1
|
+
import { a as GlkFormElement } from './shared/base-C0B1hgOt.js';
|
|
2
2
|
|
|
3
3
|
const SEARCH_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`;
|
|
4
4
|
|