@jungherz-de/glasskit-elements 1.27.0 → 1.28.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/CHANGELOG.md +31 -1
- package/README.md +26 -5
- package/SKILL.md +47 -10
- package/dist/components/base.js +9 -3
- package/dist/components/glk-modal.js +37 -1
- package/dist/components/glk-segmented.js +40 -5
- package/dist/components/glk-tab-bar.js +22 -15
- package/dist/components/glk-tab-item.js +20 -3
- package/dist/components/shared/{glasskit-droplet-C3k1mxn4.js → glasskit-droplet-3vJVXB3u.js} +30 -10
- package/dist/glasskit-elements.esm.js +158 -37
- package/dist/glasskit-elements.js +158 -37
- package/dist/glasskit-elements.min.js +1 -1
- package/package.json +3 -3
- package/src/base.js +9 -3
- package/src/components/feedback/glk-modal.js +37 -1
- package/src/components/forms/glk-segmented.js +39 -4
- package/src/components/navigation/glk-tab-bar.js +21 -14
- package/src/components/navigation/glk-tab-item.js +20 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.28.0] – 2026-10-20
|
|
11
|
+
|
|
12
|
+
Versions stay aligned with GlassKit at 1.28.0, which this release is built against (peer dependency `>=1.28.0`).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Icons that are not inline SVG in `<glk-tab-item>`: `slot="icon"`.** The item copied the first `<svg>` of its light DOM into its shadow root and showed nothing else, so an app whose tab icons come from Material Icons, a ligature font, or from an element of its own could not use them. Anything slotted as `icon` now sits in GlassKit's `.glass-tab-bar__icon`, before the badge, and takes the state colour from it (GlassKit 1.28.0): inactive, hover, active, the lens of the drop and the drag; on a bar without droplet the active icon carries the glow. An `<svg>` without `slot` is copied as before; with a slotted icon nothing is copied. ([#17](https://github.com/JUNGHERZ/GlassKit-Elements/issues/17))
|
|
17
|
+
- **Icons per option in `<glk-segmented>`: `slot="icon-<value>"`, and `icon-only`.** The options were text only. Each option now takes an icon from the slot named after its value — an `<svg>`, a font icon, an `<img>` — in GlassKit's `.glass-segmented__icon` before the label; an option without one has no icon box, and an icon added or removed later shows or hides it. The icon box is `aria-hidden`, so a font icon's text never joins the name. `icon-only` (property `iconOnly`) shows the icons alone, for a light / dark / auto switch: the row takes `.glass-segmented--icons`, and each label becomes the button's `aria-label` and `title`. ([#17](https://github.com/JUNGHERZ/GlassKit-Elements/issues/17))
|
|
18
|
+
- **`droplet` and `floating` are properties of `<glk-tab-bar>`.** The README called `droplet` "also a property", but `GlkTabBar` had no accessor, so `bar.droplet = true` set an own property and changed nothing; `floating` had the same gap. ([#17](https://github.com/JUNGHERZ/GlassKit-Elements/issues/17))
|
|
19
|
+
- **GlassKit 1.28.0's tokens reach the elements by inheritance:** `--gl-tab-bar-color`, `--gl-tab-bar-hover-color`, `--gl-tab-bar-label-size` and `--gl-tab-bar-z-index` for `<glk-tab-item>`, `<glk-tab-bar>` and `<glk-tab-dock>`; `--gl-segmented-active-bg`, `-shadow` and `-color` for the chosen option of `<glk-segmented>` and its drop. ([GlassKit #17](https://github.com/JUNGHERZ/GlassKit/issues/17), [GlassKit #14](https://github.com/JUNGHERZ/GlassKit/issues/14))
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`glk-tab-select` comes before `active` changes, and it is cancelable.** A bar item that opens a side drawer or an external link must not become active, but the item set `active` before it emitted `glk-tab-select`, and the bar, listening on its way up, removed `active` from the others and emitted `glk-tab-change` before a listener on the document had even seen the event: the page could only put `active` back, and on a droplet bar the drop ran to the item and back. Now `event.preventDefault()` keeps the current tab — no `active`, no `glk-tab-change`, and the drop stays. A drag that ends on such an item takes the same path, because the drop clicks the item, and the drop goes back. Uncancelled, the item becomes active and the bar emits `glk-tab-change` after `glk-tab-select`; in 1.27.0 a listener on the document received `glk-tab-change` first. ([#17](https://github.com/JUNGHERZ/GlassKit-Elements/issues/17))
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **The action buttons of `<glk-modal>` follow attribute changes.** The actions are copies of the buttons in `slot="actions"`, kept in step by the light-DOM observer — which watched content but not attributes. A confirm button that starts `disabled` stayed disabled in its copy after the page enabled it, a disabled copy forwards no click, and with a framework that binds only `disabled` (hybrids, lit, Vue) the dialog could never be confirmed. The observer of every element that copies light-DOM children now watches attributes too, so the copies of `<glk-select>`'s options and `<glk-tab-item>`'s SVG follow them as well. With as many actions as before, the copies change in place, so the one that has the focus keeps it while it is relabelled or disabled. Measured in Chromium 153, WebKit 26.6 and Firefox 151 with the issue's dialog: after typing DELETE the copy of Delete is enabled (in 1.27.0 it stayed disabled) and a click on it runs the original once; relabelled and marked `aria-busy` while focused, the copy stays the same node and keeps the focus; an option of `<glk-select>` disabled after connecting is disabled in the copy. ([#16](https://github.com/JUNGHERZ/GlassKit-Elements/issues/16))
|
|
28
|
+
- **An inline `onclick` on a modal action runs once.** The copy carried the original's `onclick` attribute and ran it, then handed the click to the original, which ran it again: the issue's `alert('deleted')` would have shown twice. The copies carry no inline handlers now. Found while fixing the above. ([#16](https://github.com/JUNGHERZ/GlassKit-Elements/issues/16))
|
|
29
|
+
- **From GlassKit 1.28.0:** under a scaled ancestor the drop of `<glk-tab-bar droplet>` and `<glk-segmented droplet>` lands on the chosen item, at rest, while the transform runs and during a drag ([GlassKit #16](https://github.com/JUNGHERZ/GlassKit/issues/16)); the items of the fixed `<glk-tab-bar>` sit in the middle without a home indicator ([GlassKit #17](https://github.com/JUNGHERZ/GlassKit/issues/17)); seven steps in `<glk-steps>` fit a narrow column, so a `::part(steps)` gap workaround can go ([GlassKit #15](https://github.com/JUNGHERZ/GlassKit/issues/15)).
|
|
30
|
+
|
|
31
|
+
### Compatibility
|
|
32
|
+
|
|
33
|
+
- A listener on `glk-tab-select` that read the item's `active` there sees the previous state now; `glk-tab-change` still comes after the change. The bar no longer listens for `glk-tab-select`: the item calls it once the event went through uncancelled, so a `glk-tab-select` dispatched by a page switches no tab.
|
|
34
|
+
|
|
35
|
+
Measured in Chromium 153, WebKit 26.6 and Firefox 151: a slotted text icon, a custom element and a slotted SVG take the inactive colour, the hover colour, the primary when active and while the dragged drop is over them, and no SVG is copied next to them; `glk-tab-select` cancelled from a listener on the document leaves `active`, the drop and `glk-tab-change` alone on a tap and on a drag, where the drop returns to the current tab; segmented icons are 18 × 18 px, icon-only options 34 × 34 px with `aria-label` and `title`, the icon under a dragged drop scales by 1.15; `bar.droplet = false` removes the attribute, the class and the drop, `true` brings them back; the new GlassKit tokens arrive in every shadow root. Docs, showcase and landing pages render pixel-identical to 1.27.0, except the fixed tab bar of the docs, whose items moved to the middle, and the narrow steps, whose lines reach 2px closer to their circles.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
10
39
|
## [1.27.0] – 2026-10-18
|
|
11
40
|
|
|
12
41
|
Versions stay aligned with GlassKit at 1.27.0, which this release is built against (peer dependency `>=1.27.0`).
|
|
@@ -896,7 +925,8 @@ Starting with this release, GlassKit Elements version numbers are aligned with G
|
|
|
896
925
|
|
|
897
926
|
---
|
|
898
927
|
|
|
899
|
-
[Unreleased]: https://github.com/JUNGHERZ/GlassKit-Elements/compare/v1.
|
|
928
|
+
[Unreleased]: https://github.com/JUNGHERZ/GlassKit-Elements/compare/v1.28.0...HEAD
|
|
929
|
+
[1.28.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.28.0
|
|
900
930
|
[1.27.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.27.0
|
|
901
931
|
[1.26.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.26.0
|
|
902
932
|
[1.25.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.25.0
|
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-36-7ec8e3?style=flat-square" alt="36 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.28.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
|
|
|
@@ -67,7 +67,7 @@ It is the **app layer** of the GlassKit family — three layers, one design lang
|
|
|
67
67
|
|
|
68
68
|
```html
|
|
69
69
|
<!-- 1. GlassKit CSS -->
|
|
70
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.
|
|
70
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.28/glasskit.min.css">
|
|
71
71
|
|
|
72
72
|
<!-- 2. GlassKit Elements -->
|
|
73
73
|
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
@@ -105,7 +105,7 @@ The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` as an e
|
|
|
105
105
|
<!DOCTYPE html>
|
|
106
106
|
<html data-theme="dark">
|
|
107
107
|
<head>
|
|
108
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.
|
|
108
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.28/glasskit.min.css">
|
|
109
109
|
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
110
110
|
</head>
|
|
111
111
|
<body>
|
|
@@ -127,7 +127,7 @@ The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` as an e
|
|
|
127
127
|
| `<glk-nav>` | Horizontal navigation bar | — |
|
|
128
128
|
| `<glk-pill>` | Circular icon button (46×46px) | `label`, `disabled` |
|
|
129
129
|
| `<glk-tab-bar>` | Bottom tab bar navigation | `static`, `floating`, `droplet` |
|
|
130
|
-
| `<glk-tab-item>` | Tab bar item | `label`, `active`, `badge` |
|
|
130
|
+
| `<glk-tab-item>` | Tab bar item; any icon in `slot="icon"`, a cancelable `glk-tab-select` | `label`, `active`, `badge` |
|
|
131
131
|
| `<glk-tab-dock>` | Wrapper for floating tab bar + optional accessory | `accessory-left` |
|
|
132
132
|
| `<glk-tab-accessory>` | Standalone glass capsule (search, compose…) | `label`, `variant` (accent, success, error), `disabled` |
|
|
133
133
|
| `<glk-steps>` | Progress through a short flow | `steps`, `current`, `label` |
|
|
@@ -165,7 +165,7 @@ All form components support `name`, `value`, `disabled` and participate in nativ
|
|
|
165
165
|
| `<glk-checkbox>` | Checkbox | `label`, `checked`, `disabled`, `required` |
|
|
166
166
|
| `<glk-radio>` | Radio button | `label`, `name`, `value`, `checked`, `required` (per group) |
|
|
167
167
|
| `<glk-range>` | Range slider | `label`, `min`, `max`, `value`, `step` |
|
|
168
|
-
| `<glk-segmented>` | Small exclusive choice as one control
|
|
168
|
+
| `<glk-segmented>` | Small exclusive choice as one control, icons per option in `slot="icon-<value>"`; form-associated | `options` (JSON), `value`, `full`, `overflow` (scroll, wrap), `droplet`, `icon-only`, `label`, `name` |
|
|
169
169
|
| `<glk-calendar>` | One month with a day to pick; keyboard navigation, `Intl` names; form-associated | `month`, `value`, `today`, `min`, `max`, `marks`, `locale`, `week-start`, `label`, `name` |
|
|
170
170
|
| `<glk-image-picker>` | One image with preview, resized on the client, EXIF rotation applied; not form-associated | `src`, `label`, `hint`, `round`, `max`, `type`, `quality`, `accept` |
|
|
171
171
|
|
|
@@ -238,6 +238,27 @@ The chosen item sits on a drop of glass that floats a little above the row. A ta
|
|
|
238
238
|
|
|
239
239
|
> **Requires GlassKit CSS >= 1.27.0.** The tokens for a capsule behind the active tab (`--gl-tab-bar-active-bg`, `--gl-tab-bar-active-shadow`, `--gl-tab-bar-spotlight-opacity`, `--gl-tab-bar-active-glow`) reach `<glk-tab-item>` by inheritance; the drop takes the same surface. Since 1.27.0 the items of `<glk-tab-bar floating>` also take the floating look — spotlight and pill instead of the fixed bar's dot.
|
|
240
240
|
|
|
241
|
+
### Icons of any kind, and a tab that keeps the current one
|
|
242
|
+
|
|
243
|
+
Since 1.28.0 a tab item and each segmented option take icons that are not inline SVG — Material Icons, an `<img>`, an element of your own — and they follow every state, the drop's lens and the drag:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<glk-tab-item label="Home" active>
|
|
247
|
+
<span slot="icon" class="material-icons" aria-hidden="true">home</span>
|
|
248
|
+
</glk-tab-item>
|
|
249
|
+
|
|
250
|
+
<glk-segmented droplet icon-only label="Theme" value="dark"
|
|
251
|
+
options='[{"value":"light","label":"Light"},{"value":"dark","label":"Dark"},{"value":"auto","label":"Auto"}]'>
|
|
252
|
+
<svg slot="icon-light" viewBox="0 0 24 24">…</svg>
|
|
253
|
+
<span slot="icon-dark" class="material-icons">dark_mode</span>
|
|
254
|
+
<svg slot="icon-auto" viewBox="0 0 24 24">…</svg>
|
|
255
|
+
</glk-segmented>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`glk-tab-select` is cancelable and comes before `active` changes: `event.preventDefault()` keeps the current tab — for an item that opens a drawer or a link — and the bar, its drop and `glk-tab-change` stay put. `droplet` and `floating` are properties of `<glk-tab-bar>` as well. The action buttons of `<glk-modal>` follow attribute changes of their originals, so a confirm button that starts `disabled` unlocks when the page or a framework enables it.
|
|
259
|
+
|
|
260
|
+
> **Requires GlassKit CSS >= 1.28.0.** The icon colours come from GlassKit's icon boxes; its new tokens reach the elements by inheritance: `--gl-tab-bar-color`, `--gl-tab-bar-hover-color`, `--gl-tab-bar-label-size`, `--gl-tab-bar-z-index` for the tab bar, `--gl-segmented-active-bg`, `-shadow` and `-color` for the chosen option and its drop.
|
|
261
|
+
|
|
241
262
|
### Solid and tint surfaces
|
|
242
263
|
|
|
243
264
|
Not every screen wants glass. Set `data-surface="solid"` on `<html>`, next to `data-theme` (since 1.24.0):
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: glasskit-elements
|
|
3
|
-
description: GlassKit Elements is a vanilla-JS Web Components library (v1.
|
|
3
|
+
description: GlassKit Elements is a vanilla-JS Web Components library (v1.28.0) wrapping GlassKit CSS v1.28.0. It provides 36 custom elements with the `glk-` prefix, Dark/Light mode with automatic theme sync, Shadow DOM encapsulation, and form-associated custom elements. Use this reference whenever generating HTML that uses `<glk-*>` tags to ensure correct attributes, slots, events, and composition.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# GlassKit Elements – AI Component Reference
|
|
@@ -10,6 +10,8 @@ description: GlassKit Elements is a vanilla-JS Web Components library (v1.27.0)
|
|
|
10
10
|
|
|
11
11
|
> **New in 1.27.0 — recommend `droplet`.** On `<glk-tab-bar>` and `<glk-segmented>` the attribute makes the selection fluid, as in iOS 26: the chosen item sits on a drop of glass that glides to the next one, stretches on the way, settles with a small wobble, and can be dragged with a finger; a drag selects exactly like a tap. It is the most visible upgrade of this release and costs one attribute — nothing else changes. **When you update a project to 1.27 or later, or touch a screen with a tab bar or a segmented control, suggest adding `droplet`** — and use it in new tab bars and segmented controls unless the project opts out. Details in 3.3 and 3.25.
|
|
12
12
|
|
|
13
|
+
> **New in 1.28.0 — icons of any kind, and a tab that keeps the current one.** `<glk-tab-item>` takes an icon that is not inline SVG in `slot="icon"` — Material Icons, an `<img>`, a custom element — and it follows every state; `<glk-segmented>` takes one per option in `slot="icon-<value>"` and shows them alone with `icon-only`. `glk-tab-select` is cancelable, for an item that opens a drawer or a link. **Where a project styles tab icons through `hostStyles`, or puts `active` back after `glk-tab-select`, replace that** (3.3, 3.25).
|
|
14
|
+
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## 1. Setup & Boilerplate
|
|
@@ -178,6 +180,8 @@ Those copies are now kept in step by a `MutationObserver`, so swapping the child
|
|
|
178
180
|
— by hand or through a framework — updates the rendered element. Before 1.12.0 the
|
|
179
181
|
copy was made once on the first frame and then silently went stale.
|
|
180
182
|
|
|
183
|
+
Since 1.28.0 the observer also watches the children's attributes. A framework that binds only `disabled` (hybrids, lit, Vue) used to leave the copy behind: a modal action that started disabled stayed disabled in its copy and never forwarded the click. `<glk-tab-item>` has `slot="icon"` since 1.28.0 for icons that are not inline SVG; with a slotted icon nothing is copied.
|
|
184
|
+
|
|
181
185
|
If you ever need to force it, `element.refresh()` re-runs the copy immediately.
|
|
182
186
|
There should be no reason to reach into `element.shadowRoot`.
|
|
183
187
|
|
|
@@ -365,7 +369,7 @@ Fixed bottom navigation. Requires `.glass-bg--has-tab-bar` on the outer backgrou
|
|
|
365
369
|
|---|---|---|
|
|
366
370
|
| `static` | Boolean | Switches from `position: fixed` to `position: relative` (useful for docs / previews) |
|
|
367
371
|
| `floating` | Boolean | Pill-shaped Liquid Glass variant — adds `.glass-tab-bar--floating`. Use inside `<glk-tab-dock>` |
|
|
368
|
-
| `droplet` | Boolean | The active item sits on a drop of glass that glides to the next one and can be dragged (since 1.27.0) — recommended; also a property |
|
|
372
|
+
| `droplet` | Boolean | The active item sits on a drop of glass that glides to the next one and can be dragged (since 1.27.0) — recommended; also a property, like `floating` (since 1.28.0: before, `bar.droplet = true` set an own property and changed nothing) |
|
|
369
373
|
|
|
370
374
|
**`<glk-tab-item>`**
|
|
371
375
|
|
|
@@ -375,7 +379,24 @@ Fixed bottom navigation. Requires `.glass-bg--has-tab-bar` on the outer backgrou
|
|
|
375
379
|
| `active` | Boolean | Active state — gets a soft radial Spotlight halo when inside a `floating` bar (since 1.27.0 really: before, the items of a floating bar kept the fixed bar's look), the drop when inside a `droplet` bar |
|
|
376
380
|
| `badge` | String / Number | Numeric badge on the icon |
|
|
377
381
|
|
|
378
|
-
|
|
382
|
+
Icon: an `<svg>` child is copied into the shadow root. Anything else — a font icon such as Material Icons, an `<img>`, a custom element — goes into `slot="icon"` (since 1.28.0) and takes the state colour from GlassKit's icon box: inactive, hover, active, the lens of the drop and the drag. Give a font icon `aria-hidden="true"`, or its text becomes part of the tab's name. With a slotted icon no SVG is copied.
|
|
383
|
+
|
|
384
|
+
```html
|
|
385
|
+
<glk-tab-item label="Home" active>
|
|
386
|
+
<span slot="icon" class="material-icons" aria-hidden="true">home</span>
|
|
387
|
+
</glk-tab-item>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Events: `glk-tab-select` on the item, then `glk-tab-change` `{ tab }` on the bar. **`glk-tab-select` is cancelable (since 1.28.0)** and comes before `active` changes: `event.preventDefault()` keeps the current tab — for an item that opens a drawer or a link — so `active` stays, no `glk-tab-change` follows and the drop does not move. A drag of the drop that ends on such an item takes the same path, and the drop goes back. Before 1.28.0 the item was active already when the event arrived, and the bar had moved `active` and emitted `glk-tab-change` before a listener on the document could react.
|
|
391
|
+
|
|
392
|
+
```js
|
|
393
|
+
menuItem.addEventListener('glk-tab-select', (event) => {
|
|
394
|
+
event.preventDefault(); // stays on the current tab
|
|
395
|
+
openDrawer();
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
GlassKit 1.28.0's tab bar tokens reach the items by inheritance: `--gl-tab-bar-color` (inactive icon and label), `--gl-tab-bar-hover-color`, `--gl-tab-bar-label-size`, `--gl-tab-bar-z-index` (the fixed bar and `<glk-tab-dock>`).
|
|
379
400
|
|
|
380
401
|
#### Floating variant + Accessory (`<glk-tab-dock>` + `<glk-tab-accessory>`)
|
|
381
402
|
|
|
@@ -417,7 +438,7 @@ iOS 26 Liquid Glass-style floating tab bar. Wrap a `<glk-tab-bar floating>` in a
|
|
|
417
438
|
| `variant` | String | `accent`, `success`, `error` — filled colored capsule with white icon |
|
|
418
439
|
| `disabled` | Boolean | Disabled state |
|
|
419
440
|
|
|
420
|
-
Default slot: icon SVG (22×22). Events: `glk-click` (suppressed when disabled).
|
|
441
|
+
Default slot: icon SVG (22×22), or a font icon, which follows hover and the variants (GlassKit 1.28.0). Events: `glk-click` (suppressed when disabled).
|
|
421
442
|
|
|
422
443
|
#### Droplet (since 1.27.0) — recommended
|
|
423
444
|
|
|
@@ -904,6 +925,8 @@ Closes automatically on overlay click and <kbd>Escape</kbd>, with its fade, and
|
|
|
904
925
|
|
|
905
926
|
While it is open, **everything outside is inert** — a `<glk-toast>` shown then is neither clickable nor announced. Give feedback inside the modal, or close it first.
|
|
906
927
|
|
|
928
|
+
**The actions are copies** of the `<button>`s in `slot="actions"`; a click on one is handed to its original. Since 1.28.0 they follow every change of the originals, attributes included: a confirm button that starts `disabled` unlocks once the page or a framework enables it — no `refresh()` needed. With the same number of actions the copies change in place, so the focused one keeps the focus while it is relabelled or disabled. An inline `onclick` runs once per click, on the original; until 1.28.0 the copy carried it too and it ran twice.
|
|
929
|
+
|
|
907
930
|
---
|
|
908
931
|
|
|
909
932
|
### 3.20 `<glk-popover>`
|
|
@@ -1138,8 +1161,22 @@ A small, exclusive choice as one control (since 1.15.0) — traffic light, morni
|
|
|
1138
1161
|
| `name` | String | Form field name |
|
|
1139
1162
|
| `disabled` | Boolean | The whole row (since 1.22.3); a `<fieldset disabled>` around it disables it too |
|
|
1140
1163
|
| `droplet` | Boolean | The chosen option sits on a drop of glass that glides to the next one and can be dragged (since 1.27.0) — recommended |
|
|
1164
|
+
| `icon-only` | Boolean | The options show their icons alone, square; each label becomes the button's `aria-label` and `title` (since 1.28.0) |
|
|
1165
|
+
|
|
1166
|
+
Properties: `value`, `options` (array or JSON text), `full`, `overflow`, `label`, `disabled`, `droplet`, `iconOnly`. Slots: `icon-<value>` (since 1.28.0). Events: `glk-change` `{ value }` — only on a change made by the user, not on `.value = …`. Part: `group`.
|
|
1167
|
+
|
|
1168
|
+
**Icons (since 1.28.0).** Each option takes its icon from the slot `icon-<value>` — an `<svg>`, a font icon, an `<img>`. It sits in GlassKit's `.glass-segmented__icon` before the label, in the option's colour, and grows under the drop; an option without one has no icon box, and an icon added or removed later shows or hides it. The icon box is `aria-hidden`, so a font icon's text never joins the name. `icon-only` is for a row of icons alone — a light / dark / auto switch:
|
|
1169
|
+
|
|
1170
|
+
```html
|
|
1171
|
+
<glk-segmented droplet icon-only label="Theme" value="dark"
|
|
1172
|
+
options='[{"value":"light","label":"Light"},{"value":"dark","label":"Dark"},{"value":"auto","label":"Auto"}]'>
|
|
1173
|
+
<svg slot="icon-light" viewBox="0 0 24 24"><!-- sun --></svg>
|
|
1174
|
+
<span slot="icon-dark" class="material-icons">dark_mode</span>
|
|
1175
|
+
<svg slot="icon-auto" viewBox="0 0 24 24"><!-- screen --></svg>
|
|
1176
|
+
</glk-segmented>
|
|
1177
|
+
```
|
|
1141
1178
|
|
|
1142
|
-
|
|
1179
|
+
GlassKit 1.28.0's tokens for the chosen option reach the element by inheritance: `--gl-segmented-active-bg`, `--gl-segmented-active-shadow` and `--gl-segmented-active-color` — for a dark brand, on which the light surface leaves a white label below 4.5:1. The drop takes the same surface.
|
|
1143
1180
|
|
|
1144
1181
|
With `overflow="scroll"` the chosen option is centred in the row after the first layout, on every value change and when the row's width changes — the row scrolls, never the page. Seven areas on a phone:
|
|
1145
1182
|
|
|
@@ -1160,7 +1197,7 @@ The attribute is `overflow`, not `scroll`: `scroll` is a method of every element
|
|
|
1160
1197
|
|
|
1161
1198
|
### 3.26 `<glk-steps>`
|
|
1162
1199
|
|
|
1163
|
-
Progress through a short flow (since 1.15.0), purely presentational: the current step carries `aria-current="step"`, done steps show an SVG check. The narrowing in tight frames is the CSS block's container query (below 360 px only the current label stays) — the element knows nothing about it, so in a flex row give it a width.
|
|
1200
|
+
Progress through a short flow (since 1.15.0), purely presentational: the current step carries `aria-current="step"`, done steps show an SVG check. The narrowing in tight frames is the CSS block's container query (below 360 px only the current label stays) — the element knows nothing about it, so in a flex row give it a width. Since GlassKit 1.28.0 the lines give way there first, the circles keep their size and the row never runs past its frame; a `::part(steps) { gap: … }` workaround can go.
|
|
1164
1201
|
|
|
1165
1202
|
```html
|
|
1166
1203
|
<glk-steps steps="Day,Slot,Dog,Confirm" current="2" label="Progress"></glk-steps>
|
|
@@ -1457,7 +1494,7 @@ Key points:
|
|
|
1457
1494
|
|---|---|---|---|---|
|
|
1458
1495
|
| `<glk-button>` | — | `disabled` | `glk-click` | — |
|
|
1459
1496
|
| `<glk-pill>` | — | `disabled` | `glk-click` | — |
|
|
1460
|
-
| `<glk-tab-item>` | — | `active` | `glk-tab-select` | — |
|
|
1497
|
+
| `<glk-tab-item>` | — | `active` | `glk-tab-select` (cancelable since 1.28.0; `glk-tab-change` `{ tab }` follows on the bar) | — |
|
|
1461
1498
|
| `<glk-tab-accessory>` | — | `disabled` | `glk-click` | — |
|
|
1462
1499
|
| `<glk-input>` | `.value` | `value` | `glk-input`, `glk-change` (+ native `input`, `change`) | `{ value }` |
|
|
1463
1500
|
| `<glk-textarea>` | `.value` | `value` | `glk-input`, `glk-change` | `{ value }` |
|
|
@@ -1526,14 +1563,14 @@ All `glk-*` events bubble and are `composed: true`, so they pierce shadow bounda
|
|
|
1526
1563
|
|---|---|---|---|---|
|
|
1527
1564
|
| `<glk-nav>` | Navigation | — | default | — |
|
|
1528
1565
|
| `<glk-pill>` | Navigation | `label`, `disabled` | default (icon) | `glk-click` |
|
|
1529
|
-
| `<glk-tab-bar>` | Navigation | `static`, `floating` | default |
|
|
1530
|
-
| `<glk-tab-item>` | Navigation | `label`, `active`, `badge` |
|
|
1566
|
+
| `<glk-tab-bar>` | Navigation | `static`, `floating`, `droplet` | default | `glk-tab-change` |
|
|
1567
|
+
| `<glk-tab-item>` | Navigation | `label`, `active`, `badge` | `icon`; an unslotted `<svg>` is copied | `glk-tab-select` (cancelable) |
|
|
1531
1568
|
| `<glk-tab-dock>` | Navigation | `accessory-left` | default | — |
|
|
1532
1569
|
| `<glk-tab-accessory>` | Navigation | `label`, `variant`, `disabled` | default (icon) | `glk-click` |
|
|
1533
1570
|
| `<glk-avatar>` | Content | `size`, `src` | default (initials) | — |
|
|
1534
1571
|
| `<glk-badge>` | Content | `variant`, `interactive`, `selected` | default | `glk-click` (only when interactive) |
|
|
1535
1572
|
| `<glk-empty>` | Content | `title`, `text` | default (icon), `action` | — |
|
|
1536
|
-
| `<glk-segmented>` | Forms | `options`, `value`, `full`, `overflow`, `label`, `name` |
|
|
1573
|
+
| `<glk-segmented>` | Forms | `options`, `value`, `full`, `overflow`, `droplet`, `icon-only`, `label`, `name` | `icon-<value>` | `glk-change` |
|
|
1537
1574
|
| `<glk-steps>` | Navigation | `steps`, `current`, `label` | — | — |
|
|
1538
1575
|
| `<glk-sheet>` | Feedback | `open`, `inline`, `title` | default, `actions` | `glk-close` |
|
|
1539
1576
|
| `<glk-date-strip>` | Navigation | `start`, `days`, `value`, `today`, `marks`, `locale`, `label` | — | `glk-change` |
|
package/dist/components/base.js
CHANGED
|
@@ -209,8 +209,9 @@ class GlkElement extends HTMLElement {
|
|
|
209
209
|
/**
|
|
210
210
|
* Opt in when the component copies light-DOM children into its shadow tree.
|
|
211
211
|
* A MutationObserver then calls projectLightDom() again whenever those
|
|
212
|
-
* children change
|
|
213
|
-
*
|
|
212
|
+
* children change — their content and, since 1.28.0, their attributes — so
|
|
213
|
+
* a framework that swaps them or only binds `disabled` keeps the rendered
|
|
214
|
+
* element in step. Without it the copy is made once and silently goes stale.
|
|
214
215
|
*/
|
|
215
216
|
static get observesLightDom() { return false; }
|
|
216
217
|
|
|
@@ -272,8 +273,13 @@ class GlkElement extends HTMLElement {
|
|
|
272
273
|
|
|
273
274
|
if (this.constructor.observesLightDom) {
|
|
274
275
|
this._lightDomObserver ??= new MutationObserver(records => this.projectLightDom(records));
|
|
276
|
+
// Attributes too: the copies show them. Until 1.27.0 a modal action
|
|
277
|
+
// that started disabled stayed disabled in its copy after the page
|
|
278
|
+
// enabled it, and the dialog could not be confirmed. The elements'
|
|
279
|
+
// own projectLightDom() compare a signature first, so a change
|
|
280
|
+
// elsewhere in the content costs a string comparison.
|
|
275
281
|
this._lightDomObserver.observe(this, {
|
|
276
|
-
childList: true, subtree: true, characterData: true
|
|
282
|
+
childList: true, subtree: true, characterData: true, attributes: true
|
|
277
283
|
});
|
|
278
284
|
}
|
|
279
285
|
}
|
|
@@ -24,6 +24,33 @@ function reducedMotion() {
|
|
|
24
24
|
return typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// The copies carry no inline handlers (onclick="…"): a click on a copy is
|
|
28
|
+
// handed to its original, and until 1.28.0 such a handler ran twice.
|
|
29
|
+
const isHandler = name => name.startsWith('on');
|
|
30
|
+
|
|
31
|
+
function copyOf(node) {
|
|
32
|
+
const copy = node.cloneNode(true);
|
|
33
|
+
if (copy.nodeType === Node.ELEMENT_NODE) {
|
|
34
|
+
for (const el of [copy, ...copy.querySelectorAll('*')]) {
|
|
35
|
+
for (const { name } of [...el.attributes]) if (isHandler(name)) el.removeAttribute(name);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return copy;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Gives an action's copy the attributes and the content of its original. */
|
|
42
|
+
function mirror(copy, original) {
|
|
43
|
+
for (const { name } of [...copy.attributes]) {
|
|
44
|
+
if (!original.hasAttribute(name)) copy.removeAttribute(name);
|
|
45
|
+
}
|
|
46
|
+
for (const { name, value } of original.attributes) {
|
|
47
|
+
if (!isHandler(name) && copy.getAttribute(name) !== value) copy.setAttribute(name, value);
|
|
48
|
+
}
|
|
49
|
+
if (copy.innerHTML !== original.innerHTML) {
|
|
50
|
+
copy.replaceChildren(...[...original.childNodes].map(copyOf));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
27
54
|
class GlkModal extends GlkElement {
|
|
28
55
|
static get observedAttributes() {
|
|
29
56
|
return ['open', 'title'];
|
|
@@ -69,9 +96,18 @@ class GlkModal extends GlkElement {
|
|
|
69
96
|
if (signature === this._footerSignature) return;
|
|
70
97
|
this._footerSignature = signature;
|
|
71
98
|
|
|
99
|
+
// As many actions as copies: each copy takes its original's attributes
|
|
100
|
+
// and content in place, so the one that has the focus keeps it — a
|
|
101
|
+
// confirm button that the page enables, or relabels while it works.
|
|
102
|
+
const copies = [...this._footer.children];
|
|
103
|
+
if (copies.length === buttons.length) {
|
|
104
|
+
buttons.forEach((btn, i) => mirror(copies[i], btn));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
72
108
|
this._footer.innerHTML = '';
|
|
73
109
|
buttons.forEach((btn, i) => {
|
|
74
|
-
const clone = btn
|
|
110
|
+
const clone = copyOf(btn);
|
|
75
111
|
// Look the original up again at click time instead of closing over it: a
|
|
76
112
|
// framework that re-renders replaces the node, and a captured reference
|
|
77
113
|
// would then forward the click to a button no longer in the document.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GlkFormElement } from './base.js';
|
|
2
2
|
import { r as revealCentered } from './shared/reveal-CjjzoxTa.js';
|
|
3
|
-
import { a as attachDroplet } from './shared/glasskit-droplet-
|
|
3
|
+
import { a as attachDroplet } from './shared/glasskit-droplet-3vJVXB3u.js';
|
|
4
4
|
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
5
5
|
|
|
6
6
|
// One button per option, the chosen one marked aria-pressed="true" — GlassKit
|
|
@@ -13,9 +13,16 @@ import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
|
13
13
|
// disabled (the whole row; since 1.22.3, as is a disabled
|
|
14
14
|
// fieldset around it), droplet (since 1.27.0: the chosen option
|
|
15
15
|
// sits on a drop of glass that glides to the next one and can be
|
|
16
|
-
// dragged — GlassKit's .glass-segmented--droplet)
|
|
16
|
+
// dragged — GlassKit's .glass-segmented--droplet), icon-only
|
|
17
|
+
// (since 1.28.0: the options show their icons alone, each label
|
|
18
|
+
// becomes the button's aria-label and title — GlassKit's
|
|
19
|
+
// .glass-segmented--icons)
|
|
17
20
|
// Properties: value, options (array or JSON text), full, overflow, label,
|
|
18
|
-
// disabled, droplet
|
|
21
|
+
// disabled, droplet, iconOnly
|
|
22
|
+
// Slots: icon-<value> (since 1.28.0) — the icon of the option with that
|
|
23
|
+
// value: an <svg>, a font icon, an <img>. It sits in GlassKit's
|
|
24
|
+
// .glass-segmented__icon before the label and takes the option's
|
|
25
|
+
// colour; an option without one has no icon box.
|
|
19
26
|
// Event: glk-change { value } — only on a change made by the user
|
|
20
27
|
// Part: group
|
|
21
28
|
//
|
|
@@ -44,7 +51,7 @@ function parseList(json) {
|
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
class GlkSegmented extends GlkFormElement {
|
|
47
|
-
static get observedAttributes() { return ['options', 'value', 'full', 'overflow', 'label', 'droplet']; }
|
|
54
|
+
static get observedAttributes() { return ['options', 'value', 'full', 'overflow', 'label', 'droplet', 'icon-only']; }
|
|
48
55
|
|
|
49
56
|
// A row of buttons with one tab stop: delegated focus would land on the
|
|
50
57
|
// first button, not the chosen one.
|
|
@@ -61,20 +68,39 @@ class GlkSegmented extends GlkFormElement {
|
|
|
61
68
|
|
|
62
69
|
_build() {
|
|
63
70
|
this._group.replaceChildren();
|
|
71
|
+
const iconOnly = this.iconOnly;
|
|
64
72
|
for (const option of parseList(this.getAttribute('options'))) {
|
|
65
73
|
const value = String(option.value ?? '');
|
|
74
|
+
const label = String(option.label ?? value);
|
|
66
75
|
const button = this.createElement('button', ['glass-segmented__item'], { type: 'button', 'data-value': value });
|
|
67
76
|
if (TONES.includes(option.tone)) {
|
|
68
77
|
button.classList.add(`glass-segmented__item--${option.tone}`);
|
|
69
78
|
button.appendChild(this.createElement('span', ['glass-segmented__dot']));
|
|
70
79
|
}
|
|
71
|
-
|
|
80
|
+
// The label names the button; an icon font's text must not join it.
|
|
81
|
+
const icon = this.createElement('span', ['glass-segmented__icon'], { 'aria-hidden': 'true' });
|
|
82
|
+
icon.appendChild(this.createElement('slot', [], { name: `icon-${value}` }));
|
|
83
|
+
button.appendChild(icon);
|
|
84
|
+
if (iconOnly) {
|
|
85
|
+
button.setAttribute('aria-label', label);
|
|
86
|
+
button.title = label;
|
|
87
|
+
} else {
|
|
88
|
+
button.appendChild(document.createTextNode(label));
|
|
89
|
+
}
|
|
72
90
|
this._group.appendChild(button);
|
|
73
91
|
}
|
|
92
|
+
this._syncIcons();
|
|
74
93
|
this.applyDisabled(this._actuallyDisabled);
|
|
75
94
|
this._syncValue();
|
|
76
95
|
}
|
|
77
96
|
|
|
97
|
+
/** An option shows its icon box only while an icon is slotted into it. */
|
|
98
|
+
_syncIcons() {
|
|
99
|
+
for (const slot of this._group.querySelectorAll('.glass-segmented__icon > slot')) {
|
|
100
|
+
slot.parentElement.hidden = slot.assignedElements().length === 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
/** The whole row is disabled, or an option by its own `disabled`. */
|
|
79
105
|
applyDisabled(disabled) {
|
|
80
106
|
const options = this.options;
|
|
@@ -96,6 +122,7 @@ class GlkSegmented extends GlkFormElement {
|
|
|
96
122
|
const overflow = this.overflow;
|
|
97
123
|
this._group.classList.toggle('glass-segmented--full', this.getBoolAttr('full'));
|
|
98
124
|
this._group.classList.toggle('glass-segmented--droplet', this.getBoolAttr('droplet'));
|
|
125
|
+
this._group.classList.toggle('glass-segmented--icons', this.iconOnly);
|
|
99
126
|
for (const mode of OVERFLOWS) this._group.classList.toggle(`glass-segmented--${mode}`, overflow === mode);
|
|
100
127
|
this._reveal('instant');
|
|
101
128
|
}
|
|
@@ -122,14 +149,18 @@ class GlkSegmented extends GlkFormElement {
|
|
|
122
149
|
this.emit('glk-change', { value });
|
|
123
150
|
};
|
|
124
151
|
this._onResize = () => { if (this._group.clientWidth !== this._revealedAt) this._reveal('instant'); };
|
|
152
|
+
this._onSlotChange = () => this._syncIcons();
|
|
125
153
|
this._group.addEventListener('click', this._onClick);
|
|
154
|
+
this._group.addEventListener('slotchange', this._onSlotChange);
|
|
126
155
|
this._resizeObserver = new ResizeObserver(this._onResize);
|
|
127
156
|
this._resizeObserver.observe(this._group);
|
|
157
|
+
this._syncIcons();
|
|
128
158
|
this._applyDroplet();
|
|
129
159
|
}
|
|
130
160
|
|
|
131
161
|
teardownEvents() {
|
|
132
162
|
this._group?.removeEventListener('click', this._onClick);
|
|
163
|
+
this._group?.removeEventListener('slotchange', this._onSlotChange);
|
|
133
164
|
this._resizeObserver?.disconnect();
|
|
134
165
|
this._resizeObserver = null;
|
|
135
166
|
this._droplet?.destroy();
|
|
@@ -152,6 +183,7 @@ class GlkSegmented extends GlkFormElement {
|
|
|
152
183
|
case 'full':
|
|
153
184
|
case 'overflow': this._applyLayout(); break;
|
|
154
185
|
case 'droplet': this._applyLayout(); this._applyDroplet(); break;
|
|
186
|
+
case 'icon-only': this._build(); this._applyLayout(); break;
|
|
155
187
|
case 'label': this._applyLabel(); break;
|
|
156
188
|
}
|
|
157
189
|
}
|
|
@@ -174,6 +206,9 @@ class GlkSegmented extends GlkFormElement {
|
|
|
174
206
|
get droplet() { return this.getBoolAttr('droplet'); }
|
|
175
207
|
set droplet(v) { this.setBoolAttr('droplet', v); }
|
|
176
208
|
|
|
209
|
+
get iconOnly() { return this.getBoolAttr('icon-only'); }
|
|
210
|
+
set iconOnly(v) { this.setBoolAttr('icon-only', v); }
|
|
211
|
+
|
|
177
212
|
get overflow() {
|
|
178
213
|
const v = this.getAttribute('overflow');
|
|
179
214
|
return OVERFLOWS.includes(v) ? v : '';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GlkElement } from './base.js';
|
|
2
|
-
import { a as attachDroplet } from './shared/glasskit-droplet-
|
|
2
|
+
import { a as attachDroplet } from './shared/glasskit-droplet-3vJVXB3u.js';
|
|
3
3
|
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
4
4
|
|
|
5
5
|
// droplet (since 1.27.0): the active item sits on a drop of glass that glides
|
|
@@ -83,20 +83,20 @@ class GlkTabBar extends GlkElement {
|
|
|
83
83
|
this._syncItems();
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.addEventListener('glk-tab-select', this._onTabSelect);
|
|
86
|
+
/**
|
|
87
|
+
* Called by an item the user selected once its glk-tab-select went through
|
|
88
|
+
* uncancelled; the item is active already. Until 1.27.0 the bar did this
|
|
89
|
+
* while the event was still on its way, so a listener further up could not
|
|
90
|
+
* have kept the current tab.
|
|
91
|
+
*/
|
|
92
|
+
_selected(tab) {
|
|
93
|
+
for (const item of this.querySelectorAll('glk-tab-item')) {
|
|
94
|
+
if (item !== tab) item.removeAttribute('active');
|
|
95
|
+
}
|
|
96
|
+
this.emit('glk-tab-change', { tab });
|
|
97
|
+
}
|
|
99
98
|
|
|
99
|
+
setupEvents() {
|
|
100
100
|
// The drop's state lives on the nav; the items carry it on their wrappers.
|
|
101
101
|
this._navObserver = new MutationObserver(() => this._syncItems());
|
|
102
102
|
this._navObserver.observe(this._nav, { attributes: true, attributeFilter: ['data-droplet', 'data-droplet-drag'] });
|
|
@@ -107,12 +107,19 @@ class GlkTabBar extends GlkElement {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
teardownEvents() {
|
|
110
|
-
this.removeEventListener('glk-tab-select', this._onTabSelect);
|
|
111
110
|
this._navObserver?.disconnect();
|
|
112
111
|
this._itemObserver?.disconnect();
|
|
113
112
|
this._droplet?.destroy();
|
|
114
113
|
this._droplet = null;
|
|
115
114
|
}
|
|
115
|
+
|
|
116
|
+
// Properties as well (since 1.28.0): `bar.droplet = true` set an own
|
|
117
|
+
// property before and changed nothing.
|
|
118
|
+
get floating() { return this.getBoolAttr('floating'); }
|
|
119
|
+
set floating(v) { this.setBoolAttr('floating', v); }
|
|
120
|
+
|
|
121
|
+
get droplet() { return this.getBoolAttr('droplet'); }
|
|
122
|
+
set droplet(v) { this.setBoolAttr('droplet', v); }
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
customElements.define('glk-tab-bar', GlkTabBar);
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { GlkElement } from './base.js';
|
|
2
2
|
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
3
3
|
|
|
4
|
+
// The icon: an inline <svg> child is copied into the shadow root, where
|
|
5
|
+
// GlassKit's rules reach it. Anything else — a font icon such as Material
|
|
6
|
+
// Icons, an <img>, an element of the page's own — goes into slot="icon"
|
|
7
|
+
// (since 1.28.0) and takes the state colour from GlassKit's icon box: active,
|
|
8
|
+
// hover, the lens of the drop and the drag. With a slotted icon no SVG is
|
|
9
|
+
// copied.
|
|
10
|
+
//
|
|
11
|
+
// glk-tab-select (cancelable since 1.28.0) comes before `active` changes:
|
|
12
|
+
// preventDefault() keeps the current tab — for an item that opens a drawer
|
|
13
|
+
// or a link — and the bar then neither moves `active` nor its drop, nor
|
|
14
|
+
// emits glk-tab-change. A drag of the drop that ends on the item clicks it,
|
|
15
|
+
// so it takes the same path.
|
|
16
|
+
|
|
4
17
|
class GlkTabItem extends GlkElement {
|
|
5
18
|
static get observedAttributes() {
|
|
6
19
|
return ['label', 'active', 'badge'];
|
|
@@ -12,8 +25,10 @@ class GlkTabItem extends GlkElement {
|
|
|
12
25
|
this._btn = this.createElement('button', ['glass-tab-bar__item']);
|
|
13
26
|
if (this.getBoolAttr('active')) this._btn.classList.add('is-active');
|
|
14
27
|
|
|
15
|
-
// Icon — clone SVG from light DOM into shadow DOM so GlassKit CSS applies
|
|
28
|
+
// Icon — clone SVG from light DOM into shadow DOM so GlassKit CSS applies,
|
|
29
|
+
// or show what is slotted as icon
|
|
16
30
|
this._iconEl = this.createElement('span', ['glass-tab-bar__icon']);
|
|
31
|
+
this._iconEl.appendChild(this.createElement('slot', [], { name: 'icon' }));
|
|
17
32
|
|
|
18
33
|
// Label
|
|
19
34
|
this._labelEl = this.createElement('span', ['glass-tab-bar__label']);
|
|
@@ -37,7 +52,8 @@ class GlkTabItem extends GlkElement {
|
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
projectLightDom() {
|
|
40
|
-
|
|
55
|
+
// A slotted icon wins; only without one is an <svg> copied.
|
|
56
|
+
const svg = this.querySelector(':scope > [slot="icon"]') ? null : this.querySelector('svg');
|
|
41
57
|
const signature = svg ? svg.outerHTML : '';
|
|
42
58
|
if (signature === this._iconSignature) return;
|
|
43
59
|
this._iconSignature = signature;
|
|
@@ -63,8 +79,9 @@ class GlkTabItem extends GlkElement {
|
|
|
63
79
|
|
|
64
80
|
setupEvents() {
|
|
65
81
|
this._onClick = () => {
|
|
82
|
+
if (!this.emit('glk-tab-select', null, { cancelable: true })) return;
|
|
66
83
|
this.setAttribute('active', '');
|
|
67
|
-
this.
|
|
84
|
+
this.closest('glk-tab-bar')?._selected?.(this);
|
|
68
85
|
};
|
|
69
86
|
this._btn.addEventListener('click', this._onClick);
|
|
70
87
|
this._tellBar();
|