@jungherz-de/glasskit-elements 1.13.0 → 1.15.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 +43 -6
- package/dist/components/base.js +253 -0
- package/dist/components/glk-accordion-item.js +2 -1
- package/dist/components/glk-accordion.js +2 -1
- package/dist/components/glk-avatar.js +2 -1
- package/dist/components/glk-badge.js +2 -1
- package/dist/components/glk-button.js +2 -1
- package/dist/components/glk-card.js +2 -1
- package/dist/components/glk-checkbox.js +2 -1
- package/dist/components/glk-divider.js +2 -1
- package/dist/components/glk-empty.js +53 -0
- package/dist/components/glk-input.js +2 -1
- package/dist/components/glk-list-item.js +2 -1
- package/dist/components/glk-list.js +2 -1
- package/dist/components/glk-modal.js +2 -1
- package/dist/components/glk-nav.js +2 -1
- package/dist/components/glk-pill.js +2 -1
- package/dist/components/glk-popover.js +2 -1
- package/dist/components/glk-progress.js +2 -1
- package/dist/components/glk-radio.js +2 -1
- package/dist/components/glk-range.js +2 -1
- package/dist/components/glk-search.js +2 -1
- package/dist/components/glk-segmented.js +124 -0
- package/dist/components/glk-select.js +2 -1
- package/dist/components/glk-sheet.js +141 -0
- package/dist/components/glk-status.js +2 -1
- package/dist/components/glk-steps.js +75 -0
- package/dist/components/glk-tab-accessory.js +2 -1
- package/dist/components/glk-tab-bar.js +2 -1
- package/dist/components/glk-tab-dock.js +2 -1
- package/dist/components/glk-tab-item.js +2 -1
- package/dist/components/glk-textarea.js +2 -1
- package/dist/components/glk-title.js +2 -1
- package/dist/components/glk-toast.js +2 -1
- package/dist/components/glk-toggle.js +2 -1
- package/dist/glasskit-elements.esm.js +380 -3
- package/dist/glasskit-elements.js +385 -2
- package/dist/glasskit-elements.min.js +1 -1
- package/package.json +4 -3
- package/src/components/content/glk-empty.js +51 -0
- package/src/components/feedback/glk-sheet.js +139 -0
- package/src/components/forms/glk-segmented.js +122 -0
- package/src/components/navigation/glk-steps.js +73 -0
- package/src/index.js +10 -0
- package/dist/components/shared/base-ClsXgLON.js +0 -273
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://www.npmjs.com/package/@jungherz-de/glasskit-elements"><img src="https://img.shields.io/npm/v/@jungherz-de/glasskit-elements?style=flat-square&color=f5a623&label=version" alt="Version"></a>
|
|
5
5
|
<a href="#"><img src="https://img.shields.io/badge/vanilla_JS-no_dependencies-44cc11?style=flat-square" alt="Vanilla JS"></a>
|
|
6
|
-
<a href="#"><img src="https://img.shields.io/badge/components-
|
|
6
|
+
<a href="#"><img src="https://img.shields.io/badge/components-33-7ec8e3?style=flat-square" alt="33 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.15.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
|
|
|
@@ -51,7 +51,7 @@ It is the **app layer** of the GlassKit family — three layers, one design lang
|
|
|
51
51
|
| Feature | Details |
|
|
52
52
|
|---|---|
|
|
53
53
|
| 🔌 **Shadow DOM** | Style encapsulation via `adoptedStyleSheets` — no CSS leaking |
|
|
54
|
-
| 🧩 **
|
|
54
|
+
| 🧩 **33 Components** | Buttons, cards, toggles, modals, accordions, lists, popovers, tab bars (incl. floating + accessory), and more |
|
|
55
55
|
| 🪶 **Lightweight** | 117 KB raw / 92 KB minified / 15 KB gzipped (IIFE bundle), no external dependencies |
|
|
56
56
|
| 📦 **Three bundle formats** | IIFE for `<script>`, minified IIFE for production, ESM for bundlers & tree-shaking |
|
|
57
57
|
| 🎛️ **Form Participation** | Input, toggle, checkbox, radio, select — all work natively with `<form>` via `ElementInternals` |
|
|
@@ -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.15/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>
|
|
@@ -89,8 +89,13 @@ import '@jungherz-de/glasskit-elements';
|
|
|
89
89
|
// Only import what you need
|
|
90
90
|
import '@jungherz-de/glasskit-elements/components/glk-button.js';
|
|
91
91
|
import '@jungherz-de/glasskit-elements/components/glk-toggle.js';
|
|
92
|
+
|
|
93
|
+
// The base classes, for elements of your own (same module the components use)
|
|
94
|
+
import { GlkElement, GlkFormElement } from '@jungherz-de/glasskit-elements/base.js';
|
|
92
95
|
```
|
|
93
96
|
|
|
97
|
+
The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` as an external import, so the GlassKit stylesheet exists once — a bundler resolves it from `node_modules`; a build-free project adds it to its import map next to the elements entries. Do not mix the `<script>` bundle with `base.js`: the bundle carries its own copy of the classes, so `instanceof` would fail across the two. With the bundle, take the class from it — `GlassKitElements.GlkElement`, or `import { GlkElement } from '@jungherz-de/glasskit-elements'` for the ESM bundle.
|
|
98
|
+
|
|
94
99
|
---
|
|
95
100
|
|
|
96
101
|
## 🚀 Quick Start
|
|
@@ -99,7 +104,7 @@ import '@jungherz-de/glasskit-elements/components/glk-toggle.js';
|
|
|
99
104
|
<!DOCTYPE html>
|
|
100
105
|
<html data-theme="dark">
|
|
101
106
|
<head>
|
|
102
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.
|
|
107
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.15/glasskit.min.css">
|
|
103
108
|
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
104
109
|
</head>
|
|
105
110
|
<body>
|
|
@@ -124,6 +129,7 @@ import '@jungherz-de/glasskit-elements/components/glk-toggle.js';
|
|
|
124
129
|
| `<glk-tab-item>` | Tab bar item | `label`, `active`, `badge` |
|
|
125
130
|
| `<glk-tab-dock>` | Wrapper for floating tab bar + optional accessory | `accessory-left` |
|
|
126
131
|
| `<glk-tab-accessory>` | Standalone glass capsule (search, compose…) | `label`, `variant` (accent, success, error), `disabled` |
|
|
132
|
+
| `<glk-steps>` | Progress through a short flow | `steps`, `current`, `label` |
|
|
127
133
|
|
|
128
134
|
### Content
|
|
129
135
|
|
|
@@ -135,6 +141,7 @@ import '@jungherz-de/glasskit-elements/components/glk-toggle.js';
|
|
|
135
141
|
| `<glk-title>` | Styled heading | — |
|
|
136
142
|
| `<glk-divider>` | Horizontal divider | — |
|
|
137
143
|
| `<glk-status>` | Status notice | `message` |
|
|
144
|
+
| `<glk-empty>` | Empty state: icon, title, text, one action | `title`, `text` |
|
|
138
145
|
|
|
139
146
|
### Buttons
|
|
140
147
|
|
|
@@ -156,6 +163,7 @@ All form components support `name`, `value`, `disabled` and participate in nativ
|
|
|
156
163
|
| `<glk-checkbox>` | Checkbox | `label`, `checked`, `disabled` |
|
|
157
164
|
| `<glk-radio>` | Radio button | `label`, `name`, `value`, `checked` |
|
|
158
165
|
| `<glk-range>` | Range slider | `label`, `min`, `max`, `value`, `step` |
|
|
166
|
+
| `<glk-segmented>` | Small exclusive choice as one control; form-associated | `options` (JSON), `value`, `full`, `label`, `name` |
|
|
159
167
|
|
|
160
168
|
### Feedback & Notifications
|
|
161
169
|
|
|
@@ -165,6 +173,7 @@ All form components support `name`, `value`, `disabled` and participate in nativ
|
|
|
165
173
|
| `<glk-modal>` | Modal dialog | `open`, `title` |
|
|
166
174
|
| `<glk-toast>` | Auto-dismissing notification | `message`, `variant` (success, error, warning), `duration` |
|
|
167
175
|
| `<glk-popover>` | Anchored dropdown / menu | `open`, `placement` (top, bottom, start, end) |
|
|
176
|
+
| `<glk-sheet>` | Bottom sheet, the mobile sibling of the modal | `open`, `inline`, `title` |
|
|
168
177
|
|
|
169
178
|
### Containers
|
|
170
179
|
|
|
@@ -237,6 +246,33 @@ pages that never load `glasskit.css` still work and your brand file always wins.
|
|
|
237
246
|
- **`GlkElement`** base class — handles Shadow DOM setup, theme sync, attribute reflection
|
|
238
247
|
- **`GlkFormElement`** extends `GlkElement` — adds `ElementInternals` for native form participation
|
|
239
248
|
|
|
249
|
+
### Build your own element
|
|
250
|
+
|
|
251
|
+
Since 1.14.0 both base classes are exported, so a project element inherits the whole setup — open shadow root with GlassKit's stylesheet adopted (`.glass-*` classes work inside), theme wrapper following `data-theme`, listeners re-armed when the element moves, `emit()` for bubbling, composed events:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
import { GlkElement } from '@jungherz-de/glasskit-elements'; // or '…/base.js', or GlassKitElements.GlkElement from the CDN bundle
|
|
255
|
+
|
|
256
|
+
class DemoCounter extends GlkElement {
|
|
257
|
+
static get observedAttributes() { return ['count']; }
|
|
258
|
+
render() {
|
|
259
|
+
this._btn = this.createElement('button', ['glass-btn', 'glass-btn--secondary', 'glass-btn--sm', 'glass-btn--auto']);
|
|
260
|
+
this._wrapper.appendChild(this._btn);
|
|
261
|
+
}
|
|
262
|
+
setupEvents() {
|
|
263
|
+
this._onClick = () => { this.count += 1; this.emit('demo-count', { count: this.count }); };
|
|
264
|
+
this._btn.addEventListener('click', this._onClick);
|
|
265
|
+
}
|
|
266
|
+
teardownEvents() { this._btn?.removeEventListener('click', this._onClick); }
|
|
267
|
+
onAttributeChanged() { this._btn.textContent = `Clicked ${this.count}×`; }
|
|
268
|
+
get count() { return Number(this.getAttribute('count')) || 0; }
|
|
269
|
+
set count(v) { this.setAttribute('count', v); }
|
|
270
|
+
}
|
|
271
|
+
customElements.define('demo-counter', DemoCounter);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Hooks: `render()` builds into `this._wrapper`, `setupEvents()` / `teardownEvents()` run on every connect / disconnect, `onAttributeChanged(name, old, value)` after the first render. `static get displayInline()` → `true` for inline elements; `static get observesLightDom()` → `true` re-runs `projectLightDom()` whenever light-DOM children change. `GlkFormElement` adds `setFormValue()`, `setValidity()`, `resetValue()` / `restoreValue()`.
|
|
275
|
+
|
|
240
276
|
---
|
|
241
277
|
|
|
242
278
|
## 📁 Project Structure
|
|
@@ -257,6 +293,7 @@ glasskit-elements/
|
|
|
257
293
|
glasskit-elements.js # IIFE bundle (117 KB raw / 18 KB gzipped)
|
|
258
294
|
glasskit-elements.min.js # IIFE minified (92 KB raw / 15 KB gzipped)
|
|
259
295
|
glasskit-elements.esm.js # ES module bundle (112 KB raw / 17 KB gzipped)
|
|
296
|
+
components/ # one ESM file per element + base.js (GlkElement, GlkFormElement)
|
|
260
297
|
index.html # Landing page
|
|
261
298
|
docs.html # Documentation
|
|
262
299
|
showcase.html # Interactive showcase
|
|
@@ -280,7 +317,7 @@ Requires `adoptedStyleSheets`, `ElementInternals`, and `customElements` v1.
|
|
|
280
317
|
|
|
281
318
|
## 🤖 AI / LLM Reference
|
|
282
319
|
|
|
283
|
-
[`SKILL.md`](SKILL.md) is a tag-based, machine-readable reference for LLMs and AI copilots. It contains copy-paste-ready HTML for all
|
|
320
|
+
[`SKILL.md`](SKILL.md) is a tag-based, machine-readable reference for LLMs and AI copilots. It contains copy-paste-ready HTML for all 33 elements, attribute / slot / event tables, composition patterns, and a common-mistakes section. It is the companion to the class-based `SKILL.md` in [GlassKit CSS](https://github.com/JUNGHERZ/GlassKit) — use both together for complete coverage of the glass stack.
|
|
284
321
|
|
|
285
322
|
---
|
|
286
323
|
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { tokensCss, componentsSheet } from '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
|
+
|
|
3
|
+
// ── Design Tokens ──
|
|
4
|
+
// The shadow roots deliberately adopt the *components* sheet only. Adopting
|
|
5
|
+
// the full GlassKit sheet would bring its [data-theme] blocks along, and those
|
|
6
|
+
// match the .glk-wrapper below — the tokens would then be re-declared inside
|
|
7
|
+
// every shadow root, where a matching rule always beats an inherited value.
|
|
8
|
+
// A project's own `:root { --gl-color-primary: … }` would never arrive.
|
|
9
|
+
//
|
|
10
|
+
// So the token defaults go on the document once, and every shadow root
|
|
11
|
+
// inherits them like any other custom property. They are wrapped in a cascade
|
|
12
|
+
// layer so an ordinary (unlayered) brand stylesheet wins over them, no matter
|
|
13
|
+
// whether it loads before or after this module.
|
|
14
|
+
|
|
15
|
+
const TOKENS_INJECTED = '__glkDefaultTokensInjected';
|
|
16
|
+
|
|
17
|
+
function injectDefaultTokens() {
|
|
18
|
+
if (typeof document === 'undefined') return; // SSR / non-DOM
|
|
19
|
+
if (globalThis[TOKENS_INJECTED]) return; // another bundle copy did it
|
|
20
|
+
globalThis[TOKENS_INJECTED] = true;
|
|
21
|
+
|
|
22
|
+
const sheet = new CSSStyleSheet();
|
|
23
|
+
sheet.replaceSync(`@layer glasskit-defaults { ${tokensCss} }`);
|
|
24
|
+
// Append — never assign — so an app's own adopted sheets survive.
|
|
25
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
injectDefaultTokens();
|
|
29
|
+
|
|
30
|
+
// ── Global Theme Sync ──
|
|
31
|
+
// Single MutationObserver that watches data-theme on <html>
|
|
32
|
+
// and notifies all GlkElement instances.
|
|
33
|
+
|
|
34
|
+
const instances = new Set();
|
|
35
|
+
|
|
36
|
+
function getCurrentTheme() {
|
|
37
|
+
return document.documentElement.getAttribute('data-theme') || 'dark';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function syncAllThemes() {
|
|
41
|
+
const theme = getCurrentTheme();
|
|
42
|
+
for (const instance of instances) {
|
|
43
|
+
instance._syncTheme(theme);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof window !== 'undefined' && typeof MutationObserver !== 'undefined') {
|
|
48
|
+
const observer = new MutationObserver(syncAllThemes);
|
|
49
|
+
observer.observe(document.documentElement, {
|
|
50
|
+
attributes: true,
|
|
51
|
+
attributeFilter: ['data-theme']
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Host Stylesheet ──
|
|
56
|
+
// Sets display:block on all custom elements by default.
|
|
57
|
+
// Inline components (badge, avatar) override this.
|
|
58
|
+
|
|
59
|
+
const hostSheet = new CSSStyleSheet();
|
|
60
|
+
hostSheet.replaceSync(`
|
|
61
|
+
:host { display: block; }
|
|
62
|
+
:host([hidden]) { display: none; }
|
|
63
|
+
.glk-wrapper { display: contents; }
|
|
64
|
+
`);
|
|
65
|
+
|
|
66
|
+
const inlineHostSheet = new CSSStyleSheet();
|
|
67
|
+
inlineHostSheet.replaceSync(`
|
|
68
|
+
:host { display: inline-block; }
|
|
69
|
+
:host([hidden]) { display: none; }
|
|
70
|
+
.glk-wrapper { display: contents; }
|
|
71
|
+
`);
|
|
72
|
+
|
|
73
|
+
// ── Base Class ──
|
|
74
|
+
|
|
75
|
+
class GlkElement extends HTMLElement {
|
|
76
|
+
|
|
77
|
+
/** Override in subclass to use inline-block display */
|
|
78
|
+
static get displayInline() { return false; }
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Optional CSSStyleSheet with component-specific host rules, adopted after
|
|
82
|
+
* the shared sheets. Keeps per-component selectors out of the shared sheet
|
|
83
|
+
* so an attribute like [fill] only means something where it is documented.
|
|
84
|
+
*/
|
|
85
|
+
static get hostStyles() { return null; }
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Opt in when the component copies light-DOM children into its shadow tree.
|
|
89
|
+
* A MutationObserver then calls projectLightDom() again whenever those
|
|
90
|
+
* children change, so a framework that swaps them keeps the rendered element
|
|
91
|
+
* in step. Without it the copy is made once and silently goes stale.
|
|
92
|
+
*/
|
|
93
|
+
static get observesLightDom() { return false; }
|
|
94
|
+
|
|
95
|
+
static get observedAttributes() {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
constructor() {
|
|
100
|
+
super();
|
|
101
|
+
this._initialized = false;
|
|
102
|
+
this._shadow = this.attachShadow({ mode: 'open' });
|
|
103
|
+
const displaySheet = this.constructor.displayInline ? inlineHostSheet : hostSheet;
|
|
104
|
+
const sheets = [componentsSheet, displaySheet];
|
|
105
|
+
const extra = this.constructor.hostStyles;
|
|
106
|
+
if (extra) sheets.push(extra);
|
|
107
|
+
this._shadow.adoptedStyleSheets = sheets;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
connectedCallback() {
|
|
111
|
+
if (!this._initialized) {
|
|
112
|
+
this._initialized = true;
|
|
113
|
+
|
|
114
|
+
// Create theme wrapper (display:contents makes it layout-transparent)
|
|
115
|
+
this._wrapper = document.createElement('div');
|
|
116
|
+
this._wrapper.className = 'glk-wrapper';
|
|
117
|
+
this._wrapper.setAttribute('data-theme', getCurrentTheme());
|
|
118
|
+
this._shadow.appendChild(this._wrapper);
|
|
119
|
+
|
|
120
|
+
this.render();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Everything below runs on every connect, not just the first. Moving an
|
|
124
|
+
// element in the DOM disconnects and reconnects it, and disconnectedCallback
|
|
125
|
+
// tears all of this down — without re-arming it here a moved element would
|
|
126
|
+
// keep its markup but silently stop reacting.
|
|
127
|
+
this.setupEvents();
|
|
128
|
+
instances.add(this);
|
|
129
|
+
|
|
130
|
+
if (this.constructor.observesLightDom) {
|
|
131
|
+
this._lightDomObserver ??= new MutationObserver(records => this.projectLightDom(records));
|
|
132
|
+
this._lightDomObserver.observe(this, {
|
|
133
|
+
childList: true, subtree: true, characterData: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
disconnectedCallback() {
|
|
139
|
+
instances.delete(this);
|
|
140
|
+
this._lightDomObserver?.disconnect();
|
|
141
|
+
this.teardownEvents();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
145
|
+
if (!this._initialized) return;
|
|
146
|
+
if (oldValue === newValue) return;
|
|
147
|
+
this.onAttributeChanged(name, oldValue, newValue);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_syncTheme(theme) {
|
|
151
|
+
if (this._wrapper) {
|
|
152
|
+
this._wrapper.setAttribute('data-theme', theme);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Subclasses override to build inner DOM inside this._wrapper. */
|
|
157
|
+
render() {}
|
|
158
|
+
|
|
159
|
+
/** Subclasses override to attach event listeners. */
|
|
160
|
+
setupEvents() {}
|
|
161
|
+
|
|
162
|
+
/** Subclasses override to remove event listeners. */
|
|
163
|
+
teardownEvents() {}
|
|
164
|
+
|
|
165
|
+
/** Subclasses override to react to attribute changes. */
|
|
166
|
+
onAttributeChanged(name, oldValue, newValue) {}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Subclasses that set observesLightDom override this to (re-)copy their
|
|
170
|
+
* light-DOM children into the shadow tree. Runs on every change to those
|
|
171
|
+
* children, so it has to be safe to call repeatedly.
|
|
172
|
+
*/
|
|
173
|
+
projectLightDom() {}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Escape hatch: re-copy the light-DOM children now. The observer covers the
|
|
177
|
+
* ordinary cases; this is for the ones it cannot see, so nobody has to reach
|
|
178
|
+
* into element.shadowRoot.
|
|
179
|
+
*/
|
|
180
|
+
refresh() { this.projectLightDom(); }
|
|
181
|
+
|
|
182
|
+
// ── Utility Methods ──
|
|
183
|
+
|
|
184
|
+
getBoolAttr(name) {
|
|
185
|
+
return this.hasAttribute(name);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
setBoolAttr(name, value) {
|
|
189
|
+
if (value) {
|
|
190
|
+
this.setAttribute(name, '');
|
|
191
|
+
} else {
|
|
192
|
+
this.removeAttribute(name);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
createElement(tag, classes = [], attrs = {}) {
|
|
197
|
+
const el = document.createElement(tag);
|
|
198
|
+
if (classes.length) el.classList.add(...classes);
|
|
199
|
+
for (const [key, val] of Object.entries(attrs)) {
|
|
200
|
+
el.setAttribute(key, val);
|
|
201
|
+
}
|
|
202
|
+
return el;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
emit(eventName, detail = null) {
|
|
206
|
+
this.dispatchEvent(new CustomEvent(eventName, {
|
|
207
|
+
bubbles: true,
|
|
208
|
+
composed: true,
|
|
209
|
+
detail
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── Form-Associated Base Class ──
|
|
215
|
+
|
|
216
|
+
class GlkFormElement extends GlkElement {
|
|
217
|
+
|
|
218
|
+
static formAssociated = true;
|
|
219
|
+
|
|
220
|
+
constructor() {
|
|
221
|
+
super();
|
|
222
|
+
this._internals = this.attachInternals();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
get form() { return this._internals.form; }
|
|
226
|
+
get validationMessage() { return this._internals.validationMessage; }
|
|
227
|
+
get validity() { return this._internals.validity; }
|
|
228
|
+
|
|
229
|
+
checkValidity() { return this._internals.checkValidity(); }
|
|
230
|
+
reportValidity() { return this._internals.reportValidity(); }
|
|
231
|
+
|
|
232
|
+
formResetCallback() {
|
|
233
|
+
this.resetValue();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
formStateRestoreCallback(state, mode) {
|
|
237
|
+
this.restoreValue(state);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Subclasses override. */
|
|
241
|
+
resetValue() {}
|
|
242
|
+
restoreValue(state) {}
|
|
243
|
+
|
|
244
|
+
setFormValue(value) {
|
|
245
|
+
this._internals.setFormValue(value);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
setValidity(flags, message, anchor) {
|
|
249
|
+
this._internals.setValidity(flags, message, anchor);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { GlkElement, GlkFormElement };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlkElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
3
|
|
|
3
4
|
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
5
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlkElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
3
|
|
|
3
4
|
// <glk-card fill> stretches the visible card to the height of its grid or flex
|
|
4
5
|
// cell. The host must become a grid for this: it is stretched by the outer
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlkFormElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
3
|
|
|
3
4
|
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
5
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { GlkElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
3
|
+
|
|
4
|
+
// Empty state for lists and result pages. Came back from EhrenPfoten in 1.15.0.
|
|
5
|
+
//
|
|
6
|
+
// Attributes: title, text — an empty one hides its element instead of
|
|
7
|
+
// leaving an empty paragraph
|
|
8
|
+
// Slots: default (icon, with a plain circle as slot fallback), action
|
|
9
|
+
// Parts: empty, icon, title, text, action
|
|
10
|
+
|
|
11
|
+
const FALLBACK_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M8 12h8"/></svg>';
|
|
12
|
+
|
|
13
|
+
class GlkEmpty extends GlkElement {
|
|
14
|
+
static get observedAttributes() { return ['title', 'text']; }
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
const root = this.createElement('div', ['glass-empty'], { part: 'empty' });
|
|
18
|
+
const icon = this.createElement('span', ['glass-empty__icon'], { part: 'icon' });
|
|
19
|
+
const iconSlot = document.createElement('slot');
|
|
20
|
+
iconSlot.innerHTML = FALLBACK_ICON;
|
|
21
|
+
icon.appendChild(iconSlot);
|
|
22
|
+
this._titleEl = this.createElement('p', ['glass-empty__title'], { part: 'title' });
|
|
23
|
+
this._textEl = this.createElement('p', ['glass-empty__text'], { part: 'text' });
|
|
24
|
+
const action = this.createElement('div', ['glass-empty__action'], { part: 'action' });
|
|
25
|
+
action.appendChild(this.createElement('slot', [], { name: 'action' }));
|
|
26
|
+
root.append(icon, this._titleEl, this._textEl, action);
|
|
27
|
+
this._wrapper.appendChild(root);
|
|
28
|
+
this._update();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_update() {
|
|
32
|
+
const title = this.getAttribute('title') || '';
|
|
33
|
+
const text = this.getAttribute('text') || '';
|
|
34
|
+
this._titleEl.textContent = title;
|
|
35
|
+
this._titleEl.hidden = !title;
|
|
36
|
+
this._textEl.textContent = text;
|
|
37
|
+
this._textEl.hidden = !text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
onAttributeChanged() {
|
|
41
|
+
if (this._titleEl) this._update();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get text() { return this.getAttribute('text') || ''; }
|
|
45
|
+
set text(v) {
|
|
46
|
+
if (v) this.setAttribute('text', v);
|
|
47
|
+
else this.removeAttribute('text');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
customElements.define('glk-empty', GlkEmpty);
|
|
52
|
+
|
|
53
|
+
export { GlkEmpty };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlkElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
3
|
|
|
3
4
|
// Module-level override sheet — shared by all instances.
|
|
4
5
|
// Suppresses the auto-divider when glk-list marks this item as the
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlkFormElement } from './base.js';
|
|
2
|
+
import '@jungherz-de/glasskit/glasskit-styles.js';
|
|
2
3
|
|
|
3
4
|
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
5
|
|