@jungherz-de/glasskit-elements 0.8.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.
Files changed (32) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +263 -0
  3. package/dist/glasskit-elements.esm.js +1789 -0
  4. package/dist/glasskit-elements.js +1819 -0
  5. package/dist/glasskit-elements.min.js +1 -0
  6. package/package.json +43 -0
  7. package/src/base.js +181 -0
  8. package/src/components/buttons/glk-button.js +80 -0
  9. package/src/components/containers/glk-accordion-item.js +61 -0
  10. package/src/components/containers/glk-accordion.js +12 -0
  11. package/src/components/content/glk-avatar.js +58 -0
  12. package/src/components/content/glk-badge.js +37 -0
  13. package/src/components/content/glk-card.js +33 -0
  14. package/src/components/content/glk-divider.js +11 -0
  15. package/src/components/content/glk-status.js +35 -0
  16. package/src/components/content/glk-title.js +16 -0
  17. package/src/components/feedback/glk-modal.js +98 -0
  18. package/src/components/feedback/glk-progress.js +77 -0
  19. package/src/components/feedback/glk-toast.js +104 -0
  20. package/src/components/forms/glk-checkbox.js +103 -0
  21. package/src/components/forms/glk-input.js +138 -0
  22. package/src/components/forms/glk-radio.js +109 -0
  23. package/src/components/forms/glk-range.js +112 -0
  24. package/src/components/forms/glk-search.js +87 -0
  25. package/src/components/forms/glk-select.js +96 -0
  26. package/src/components/forms/glk-textarea.js +95 -0
  27. package/src/components/forms/glk-toggle.js +121 -0
  28. package/src/components/navigation/glk-nav.js +12 -0
  29. package/src/components/navigation/glk-pill.js +48 -0
  30. package/src/components/navigation/glk-tab-bar.js +31 -0
  31. package/src/components/navigation/glk-tab-item.js +74 -0
  32. package/src/index.js +38 -0
@@ -0,0 +1,109 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
3
+ class GlkRadio extends GlkFormElement {
4
+ static get observedAttributes() {
5
+ return ['checked', 'disabled', 'label', 'name', 'value'];
6
+ }
7
+
8
+ render() {
9
+ const label = this.createElement('label', ['glass-radio']);
10
+
11
+ this._input = this.createElement('input', ['glass-radio__input'], {
12
+ type: 'radio'
13
+ });
14
+
15
+ const name = this.getAttribute('name');
16
+ if (name) this._input.setAttribute('name', name);
17
+
18
+ const val = this.getAttribute('value');
19
+ if (val) this._input.setAttribute('value', val);
20
+
21
+ const circle = this.createElement('span', ['glass-radio__circle']);
22
+ const dot = this.createElement('span', ['glass-radio__dot']);
23
+ circle.appendChild(dot);
24
+
25
+ this._labelEl = this.createElement('span', ['glass-radio__label']);
26
+ this._labelEl.textContent = this.getAttribute('label') || '';
27
+
28
+ label.appendChild(this._input);
29
+ label.appendChild(circle);
30
+ label.appendChild(this._labelEl);
31
+
32
+ if (this.getBoolAttr('checked')) this._input.checked = true;
33
+ if (this.getBoolAttr('disabled')) this._input.disabled = true;
34
+
35
+ this._defaultChecked = this.getBoolAttr('checked');
36
+ this._wrapper.appendChild(label);
37
+ this._syncFormValue();
38
+ }
39
+
40
+ setupEvents() {
41
+ this._onChange = () => {
42
+ this._syncing = true;
43
+ this.setBoolAttr('checked', this._input.checked);
44
+ this._syncing = false;
45
+ this._syncFormValue();
46
+ this.emit('glk-change', { checked: this._input.checked, value: this._input.value });
47
+ this.dispatchEvent(new Event('change', { bubbles: true }));
48
+ };
49
+ this._input.addEventListener('change', this._onChange);
50
+ }
51
+
52
+ teardownEvents() {
53
+ this._input?.removeEventListener('change', this._onChange);
54
+ }
55
+
56
+ onAttributeChanged(name) {
57
+ if (this._syncing) return;
58
+ if (!this._input) return;
59
+ switch (name) {
60
+ case 'checked':
61
+ this._input.checked = this.getBoolAttr('checked');
62
+ this._syncFormValue();
63
+ break;
64
+ case 'disabled':
65
+ this._input.disabled = this.getBoolAttr('disabled');
66
+ break;
67
+ case 'label':
68
+ this._labelEl.textContent = this.getAttribute('label') || '';
69
+ break;
70
+ case 'name':
71
+ this._input.setAttribute('name', this.getAttribute('name') || '');
72
+ break;
73
+ case 'value':
74
+ this._input.setAttribute('value', this.getAttribute('value') || '');
75
+ this._syncFormValue();
76
+ break;
77
+ }
78
+ }
79
+
80
+ _syncFormValue() {
81
+ const val = this.getAttribute('value') || '';
82
+ this.setFormValue(this._input.checked ? val : null);
83
+ }
84
+
85
+ resetValue() {
86
+ this._input.checked = this._defaultChecked;
87
+ this.setBoolAttr('checked', this._defaultChecked);
88
+ this._syncFormValue();
89
+ }
90
+
91
+ get checked() { return this._input?.checked ?? false; }
92
+ set checked(v) {
93
+ if (this._input) this._input.checked = v;
94
+ this.setBoolAttr('checked', v);
95
+ this._syncFormValue();
96
+ }
97
+
98
+ get disabled() { return this.getBoolAttr('disabled'); }
99
+ set disabled(v) { this.setBoolAttr('disabled', v); }
100
+
101
+ get name() { return this.getAttribute('name'); }
102
+ set name(v) { this.setAttribute('name', v); }
103
+
104
+ get value() { return this.getAttribute('value'); }
105
+ set value(v) { this.setAttribute('value', v); }
106
+ }
107
+
108
+ customElements.define('glk-radio', GlkRadio);
109
+ export { GlkRadio };
@@ -0,0 +1,112 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
3
+ class GlkRange extends GlkFormElement {
4
+ static get observedAttributes() {
5
+ return ['label', 'min', 'max', 'value', 'step', 'name', 'disabled'];
6
+ }
7
+
8
+ render() {
9
+ const group = this.createElement('div', ['glass-range-group']);
10
+
11
+ const header = this.createElement('div', ['glass-range-header']);
12
+
13
+ this._labelEl = this.createElement('label', []);
14
+ this._labelEl.textContent = this.getAttribute('label') || '';
15
+
16
+ this._valueEl = this.createElement('span', ['glass-range-value']);
17
+
18
+ header.appendChild(this._labelEl);
19
+ header.appendChild(this._valueEl);
20
+
21
+ this._input = this.createElement('input', ['glass-range'], {
22
+ type: 'range',
23
+ min: this.getAttribute('min') || '0',
24
+ max: this.getAttribute('max') || '100',
25
+ value: this.getAttribute('value') || '50'
26
+ });
27
+
28
+ const step = this.getAttribute('step');
29
+ if (step) this._input.setAttribute('step', step);
30
+
31
+ const name = this.getAttribute('name');
32
+ if (name) this._input.setAttribute('name', name);
33
+
34
+ if (this.getBoolAttr('disabled')) this._input.disabled = true;
35
+
36
+ this._updateValueDisplay();
37
+
38
+ group.appendChild(header);
39
+ group.appendChild(this._input);
40
+
41
+ this._wrapper.appendChild(group);
42
+ this._defaultValue = this._input.value;
43
+ this._syncFormValue();
44
+ }
45
+
46
+ setupEvents() {
47
+ this._onInput = () => {
48
+ this._updateValueDisplay();
49
+ this._syncFormValue();
50
+ this.emit('glk-input', { value: this._input.value });
51
+ this.dispatchEvent(new Event('input', { bubbles: true }));
52
+ };
53
+ this._input.addEventListener('input', this._onInput);
54
+ }
55
+
56
+ teardownEvents() {
57
+ this._input?.removeEventListener('input', this._onInput);
58
+ }
59
+
60
+ onAttributeChanged(name) {
61
+ if (!this._input) return;
62
+ switch (name) {
63
+ case 'label':
64
+ this._labelEl.textContent = this.getAttribute('label') || '';
65
+ break;
66
+ case 'min':
67
+ this._input.min = this.getAttribute('min') || '0';
68
+ break;
69
+ case 'max':
70
+ this._input.max = this.getAttribute('max') || '100';
71
+ break;
72
+ case 'value':
73
+ this._input.value = this.getAttribute('value') || '50';
74
+ this._updateValueDisplay();
75
+ this._syncFormValue();
76
+ break;
77
+ case 'step':
78
+ this._input.step = this.getAttribute('step') || '';
79
+ break;
80
+ case 'name':
81
+ this._input.setAttribute('name', this.getAttribute('name') || '');
82
+ break;
83
+ case 'disabled':
84
+ this._input.disabled = this.getBoolAttr('disabled');
85
+ break;
86
+ }
87
+ }
88
+
89
+ _updateValueDisplay() {
90
+ this._valueEl.textContent = `${this._input.value}%`;
91
+ }
92
+
93
+ _syncFormValue() {
94
+ this.setFormValue(this._input.value);
95
+ }
96
+
97
+ resetValue() {
98
+ this._input.value = this._defaultValue;
99
+ this._updateValueDisplay();
100
+ this._syncFormValue();
101
+ }
102
+
103
+ get value() { return this._input?.value ?? ''; }
104
+ set value(v) {
105
+ if (this._input) this._input.value = v;
106
+ this._updateValueDisplay();
107
+ this._syncFormValue();
108
+ }
109
+ }
110
+
111
+ customElements.define('glk-range', GlkRange);
112
+ export { GlkRange };
@@ -0,0 +1,87 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
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
+
5
+ class GlkSearch extends GlkFormElement {
6
+ static get observedAttributes() {
7
+ return ['placeholder', 'name', 'value', 'disabled'];
8
+ }
9
+
10
+ render() {
11
+ const container = this.createElement('div', ['glass-search']);
12
+
13
+ const icon = this.createElement('span', ['glass-search__icon']);
14
+ icon.innerHTML = SEARCH_SVG;
15
+
16
+ this._input = this.createElement('input', ['glass-input'], {
17
+ type: 'search'
18
+ });
19
+
20
+ const placeholder = this.getAttribute('placeholder');
21
+ if (placeholder) this._input.setAttribute('placeholder', placeholder);
22
+
23
+ const name = this.getAttribute('name');
24
+ if (name) this._input.setAttribute('name', name);
25
+
26
+ const value = this.getAttribute('value');
27
+ if (value) this._input.value = value;
28
+
29
+ if (this.getBoolAttr('disabled')) this._input.disabled = true;
30
+
31
+ container.appendChild(icon);
32
+ container.appendChild(this._input);
33
+
34
+ this._wrapper.appendChild(container);
35
+ this._syncFormValue();
36
+ }
37
+
38
+ setupEvents() {
39
+ this._onInput = () => {
40
+ this._syncFormValue();
41
+ this.emit('glk-input', { value: this._input.value });
42
+ this.dispatchEvent(new Event('input', { bubbles: true }));
43
+ };
44
+ this._input.addEventListener('input', this._onInput);
45
+ }
46
+
47
+ teardownEvents() {
48
+ this._input?.removeEventListener('input', this._onInput);
49
+ }
50
+
51
+ onAttributeChanged(name) {
52
+ if (!this._input) return;
53
+ switch (name) {
54
+ case 'placeholder':
55
+ this._input.setAttribute('placeholder', this.getAttribute('placeholder') || '');
56
+ break;
57
+ case 'name':
58
+ this._input.setAttribute('name', this.getAttribute('name') || '');
59
+ break;
60
+ case 'value':
61
+ this._input.value = this.getAttribute('value') || '';
62
+ this._syncFormValue();
63
+ break;
64
+ case 'disabled':
65
+ this._input.disabled = this.getBoolAttr('disabled');
66
+ break;
67
+ }
68
+ }
69
+
70
+ _syncFormValue() {
71
+ this.setFormValue(this._input.value);
72
+ }
73
+
74
+ resetValue() {
75
+ this._input.value = this.getAttribute('value') || '';
76
+ this._syncFormValue();
77
+ }
78
+
79
+ get value() { return this._input?.value ?? ''; }
80
+ set value(v) {
81
+ if (this._input) this._input.value = v;
82
+ this._syncFormValue();
83
+ }
84
+ }
85
+
86
+ customElements.define('glk-search', GlkSearch);
87
+ export { GlkSearch };
@@ -0,0 +1,96 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
3
+ class GlkSelect extends GlkFormElement {
4
+ static get observedAttributes() {
5
+ return ['label', 'disabled', 'name', 'value', 'required'];
6
+ }
7
+
8
+ render() {
9
+ const group = this.createElement('div', ['glass-input-group']);
10
+
11
+ this._labelEl = this.createElement('label', ['glass-label']);
12
+ this._labelEl.textContent = this.getAttribute('label') || '';
13
+
14
+ this._select = this.createElement('select', ['glass-select']);
15
+
16
+ const name = this.getAttribute('name');
17
+ if (name) this._select.setAttribute('name', name);
18
+
19
+ if (this.getBoolAttr('disabled')) this._select.disabled = true;
20
+ if (this.getBoolAttr('required')) this._select.required = true;
21
+
22
+ // Move slotted <option> elements into the shadow select
23
+ this._moveOptions();
24
+
25
+ group.appendChild(this._labelEl);
26
+ group.appendChild(this._select);
27
+
28
+ this._wrapper.appendChild(group);
29
+
30
+ const value = this.getAttribute('value');
31
+ if (value) this._select.value = value;
32
+
33
+ this._syncFormValue();
34
+ }
35
+
36
+ _moveOptions() {
37
+ // Copy <option> children from the light DOM into the shadow <select>
38
+ const options = this.querySelectorAll('option');
39
+ options.forEach(opt => {
40
+ this._select.appendChild(opt.cloneNode(true));
41
+ });
42
+ }
43
+
44
+ setupEvents() {
45
+ this._onChange = () => {
46
+ this._syncFormValue();
47
+ this.emit('glk-change', { value: this._select.value });
48
+ this.dispatchEvent(new Event('change', { bubbles: true }));
49
+ };
50
+ this._select.addEventListener('change', this._onChange);
51
+ }
52
+
53
+ teardownEvents() {
54
+ this._select?.removeEventListener('change', this._onChange);
55
+ }
56
+
57
+ onAttributeChanged(name) {
58
+ if (!this._select) return;
59
+ switch (name) {
60
+ case 'label':
61
+ this._labelEl.textContent = this.getAttribute('label') || '';
62
+ break;
63
+ case 'disabled':
64
+ this._select.disabled = this.getBoolAttr('disabled');
65
+ break;
66
+ case 'name':
67
+ this._select.setAttribute('name', this.getAttribute('name') || '');
68
+ break;
69
+ case 'value':
70
+ this._select.value = this.getAttribute('value') || '';
71
+ this._syncFormValue();
72
+ break;
73
+ }
74
+ }
75
+
76
+ _syncFormValue() {
77
+ this.setFormValue(this._select.value);
78
+ }
79
+
80
+ resetValue() {
81
+ this._select.selectedIndex = 0;
82
+ this._syncFormValue();
83
+ }
84
+
85
+ get value() { return this._select?.value ?? ''; }
86
+ set value(v) {
87
+ if (this._select) this._select.value = v;
88
+ this._syncFormValue();
89
+ }
90
+
91
+ get disabled() { return this.getBoolAttr('disabled'); }
92
+ set disabled(v) { this.setBoolAttr('disabled', v); }
93
+ }
94
+
95
+ customElements.define('glk-select', GlkSelect);
96
+ export { GlkSelect };
@@ -0,0 +1,95 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
3
+ class GlkTextarea extends GlkFormElement {
4
+ static get observedAttributes() {
5
+ return ['label', 'placeholder', 'rows', 'disabled', 'name', 'value', 'required'];
6
+ }
7
+
8
+ render() {
9
+ const group = this.createElement('div', ['glass-input-group']);
10
+
11
+ this._labelEl = this.createElement('label', ['glass-label']);
12
+ this._labelEl.textContent = this.getAttribute('label') || '';
13
+
14
+ this._textarea = this.createElement('textarea', ['glass-textarea']);
15
+ const placeholder = this.getAttribute('placeholder');
16
+ if (placeholder) this._textarea.setAttribute('placeholder', placeholder);
17
+
18
+ const rows = this.getAttribute('rows');
19
+ if (rows) this._textarea.setAttribute('rows', rows);
20
+
21
+ const name = this.getAttribute('name');
22
+ if (name) this._textarea.setAttribute('name', name);
23
+
24
+ const value = this.getAttribute('value');
25
+ if (value) this._textarea.value = value;
26
+
27
+ if (this.getBoolAttr('disabled')) this._textarea.disabled = true;
28
+ if (this.getBoolAttr('required')) this._textarea.required = true;
29
+
30
+ group.appendChild(this._labelEl);
31
+ group.appendChild(this._textarea);
32
+
33
+ this._wrapper.appendChild(group);
34
+ this._syncFormValue();
35
+ }
36
+
37
+ setupEvents() {
38
+ this._onInput = () => {
39
+ this._syncFormValue();
40
+ this.emit('glk-input', { value: this._textarea.value });
41
+ this.dispatchEvent(new Event('input', { bubbles: true }));
42
+ };
43
+ this._textarea.addEventListener('input', this._onInput);
44
+ }
45
+
46
+ teardownEvents() {
47
+ this._textarea?.removeEventListener('input', this._onInput);
48
+ }
49
+
50
+ onAttributeChanged(name) {
51
+ if (!this._textarea) return;
52
+ switch (name) {
53
+ case 'label':
54
+ this._labelEl.textContent = this.getAttribute('label') || '';
55
+ break;
56
+ case 'placeholder':
57
+ this._textarea.setAttribute('placeholder', this.getAttribute('placeholder') || '');
58
+ break;
59
+ case 'rows':
60
+ this._textarea.setAttribute('rows', this.getAttribute('rows') || '');
61
+ break;
62
+ case 'disabled':
63
+ this._textarea.disabled = this.getBoolAttr('disabled');
64
+ break;
65
+ case 'name':
66
+ this._textarea.setAttribute('name', this.getAttribute('name') || '');
67
+ break;
68
+ case 'value':
69
+ this._textarea.value = this.getAttribute('value') || '';
70
+ this._syncFormValue();
71
+ break;
72
+ }
73
+ }
74
+
75
+ _syncFormValue() {
76
+ this.setFormValue(this._textarea.value);
77
+ }
78
+
79
+ resetValue() {
80
+ this._textarea.value = this.getAttribute('value') || '';
81
+ this._syncFormValue();
82
+ }
83
+
84
+ get value() { return this._textarea?.value ?? ''; }
85
+ set value(v) {
86
+ if (this._textarea) this._textarea.value = v;
87
+ this._syncFormValue();
88
+ }
89
+
90
+ get disabled() { return this.getBoolAttr('disabled'); }
91
+ set disabled(v) { this.setBoolAttr('disabled', v); }
92
+ }
93
+
94
+ customElements.define('glk-textarea', GlkTextarea);
95
+ export { GlkTextarea };
@@ -0,0 +1,121 @@
1
+ import { GlkFormElement } from '../../base.js';
2
+
3
+ class GlkToggle extends GlkFormElement {
4
+ static get observedAttributes() {
5
+ return ['checked', 'disabled', 'label', 'name', 'value'];
6
+ }
7
+
8
+ render() {
9
+ const label = this.createElement('label', ['glass-toggle']);
10
+
11
+ this._input = this.createElement('input', ['glass-toggle__input'], {
12
+ type: 'checkbox'
13
+ });
14
+
15
+ const name = this.getAttribute('name');
16
+ if (name) this._input.setAttribute('name', name);
17
+
18
+ const track = this.createElement('span', ['glass-toggle__track']);
19
+ const thumb = this.createElement('span', ['glass-toggle__thumb']);
20
+ track.appendChild(thumb);
21
+
22
+ this._labelEl = this.createElement('span', ['glass-toggle__label']);
23
+ this._labelEl.textContent = this.getAttribute('label') || '';
24
+
25
+ label.appendChild(this._input);
26
+ label.appendChild(track);
27
+ label.appendChild(this._labelEl);
28
+
29
+ if (this.getBoolAttr('checked')) this._input.checked = true;
30
+ if (this.getBoolAttr('disabled')) this._input.disabled = true;
31
+
32
+ this._defaultChecked = this.getBoolAttr('checked');
33
+ this._wrapper.appendChild(label);
34
+
35
+ this._syncFormValue();
36
+ this.setAttribute('role', 'switch');
37
+ this.setAttribute('aria-checked', String(this._input.checked));
38
+ if (this.getBoolAttr('disabled')) this.setAttribute('aria-disabled', 'true');
39
+ }
40
+
41
+ setupEvents() {
42
+ this._onChange = () => {
43
+ this._syncing = true;
44
+ this.setBoolAttr('checked', this._input.checked);
45
+ this._syncing = false;
46
+ this.setAttribute('aria-checked', String(this._input.checked));
47
+ this._syncFormValue();
48
+ this.emit('glk-change', { checked: this._input.checked });
49
+ this.dispatchEvent(new Event('change', { bubbles: true }));
50
+ };
51
+ this._input.addEventListener('change', this._onChange);
52
+ }
53
+
54
+ teardownEvents() {
55
+ this._input?.removeEventListener('change', this._onChange);
56
+ }
57
+
58
+ onAttributeChanged(name) {
59
+ if (this._syncing) return;
60
+ if (!this._input) return;
61
+ switch (name) {
62
+ case 'checked':
63
+ this._input.checked = this.getBoolAttr('checked');
64
+ this.setAttribute('aria-checked', String(this._input.checked));
65
+ this._syncFormValue();
66
+ break;
67
+ case 'disabled':
68
+ this._input.disabled = this.getBoolAttr('disabled');
69
+ this.setAttribute('aria-disabled', String(this.getBoolAttr('disabled')));
70
+ break;
71
+ case 'label':
72
+ this._labelEl.textContent = this.getAttribute('label') || '';
73
+ break;
74
+ case 'name':
75
+ this._input.setAttribute('name', this.getAttribute('name') || '');
76
+ break;
77
+ }
78
+ }
79
+
80
+ _syncFormValue() {
81
+ const val = this.getAttribute('value') || 'on';
82
+ this.setFormValue(this._input.checked ? val : null);
83
+ }
84
+
85
+ resetValue() {
86
+ this._input.checked = this._defaultChecked;
87
+ this.setBoolAttr('checked', this._defaultChecked);
88
+ this.setAttribute('aria-checked', String(this._defaultChecked));
89
+ this._syncFormValue();
90
+ }
91
+
92
+ restoreValue(state) {
93
+ if (state) {
94
+ this._input.checked = true;
95
+ this.setBoolAttr('checked', true);
96
+ }
97
+ }
98
+
99
+ get checked() { return this._input?.checked ?? false; }
100
+ set checked(v) {
101
+ if (this._input) this._input.checked = v;
102
+ this.setBoolAttr('checked', v);
103
+ this.setAttribute('aria-checked', String(v));
104
+ this._syncFormValue();
105
+ }
106
+
107
+ get disabled() { return this.getBoolAttr('disabled'); }
108
+ set disabled(v) { this.setBoolAttr('disabled', v); }
109
+
110
+ get name() { return this.getAttribute('name'); }
111
+ set name(v) { this.setAttribute('name', v); }
112
+
113
+ get value() { return this.getAttribute('value') || 'on'; }
114
+ set value(v) { this.setAttribute('value', v); }
115
+
116
+ get label() { return this.getAttribute('label'); }
117
+ set label(v) { this.setAttribute('label', v); }
118
+ }
119
+
120
+ customElements.define('glk-toggle', GlkToggle);
121
+ export { GlkToggle };
@@ -0,0 +1,12 @@
1
+ import { GlkElement } from '../../base.js';
2
+
3
+ class GlkNav extends GlkElement {
4
+ render() {
5
+ const nav = this.createElement('nav', ['glass-nav']);
6
+ nav.appendChild(document.createElement('slot'));
7
+ this._wrapper.appendChild(nav);
8
+ }
9
+ }
10
+
11
+ customElements.define('glk-nav', GlkNav);
12
+ export { GlkNav };
@@ -0,0 +1,48 @@
1
+ import { GlkElement } from '../../base.js';
2
+
3
+ class GlkPill extends GlkElement {
4
+ static get observedAttributes() {
5
+ return ['label', 'disabled'];
6
+ }
7
+
8
+ render() {
9
+ this._btn = this.createElement('button', ['glass-pill']);
10
+
11
+ if (this.getBoolAttr('disabled')) this._btn.disabled = true;
12
+
13
+ const ariaLabel = this.getAttribute('label');
14
+ if (ariaLabel) this._btn.setAttribute('aria-label', ariaLabel);
15
+
16
+ // Slotted content (SVG icons, text)
17
+ this._btn.appendChild(document.createElement('slot'));
18
+ this._wrapper.appendChild(this._btn);
19
+ }
20
+
21
+ setupEvents() {
22
+ this._onClick = () => {
23
+ if (!this.getBoolAttr('disabled')) {
24
+ this.emit('glk-click');
25
+ }
26
+ };
27
+ this._btn.addEventListener('click', this._onClick);
28
+ }
29
+
30
+ teardownEvents() {
31
+ this._btn?.removeEventListener('click', this._onClick);
32
+ }
33
+
34
+ onAttributeChanged(name) {
35
+ if (!this._btn) return;
36
+ switch (name) {
37
+ case 'label':
38
+ this._btn.setAttribute('aria-label', this.getAttribute('label') || '');
39
+ break;
40
+ case 'disabled':
41
+ this._btn.disabled = this.getBoolAttr('disabled');
42
+ break;
43
+ }
44
+ }
45
+ }
46
+
47
+ customElements.define('glk-pill', GlkPill);
48
+ export { GlkPill };