@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.
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/glasskit-elements.esm.js +1789 -0
- package/dist/glasskit-elements.js +1819 -0
- package/dist/glasskit-elements.min.js +1 -0
- package/package.json +43 -0
- package/src/base.js +181 -0
- package/src/components/buttons/glk-button.js +80 -0
- package/src/components/containers/glk-accordion-item.js +61 -0
- package/src/components/containers/glk-accordion.js +12 -0
- package/src/components/content/glk-avatar.js +58 -0
- package/src/components/content/glk-badge.js +37 -0
- package/src/components/content/glk-card.js +33 -0
- package/src/components/content/glk-divider.js +11 -0
- package/src/components/content/glk-status.js +35 -0
- package/src/components/content/glk-title.js +16 -0
- package/src/components/feedback/glk-modal.js +98 -0
- package/src/components/feedback/glk-progress.js +77 -0
- package/src/components/feedback/glk-toast.js +104 -0
- package/src/components/forms/glk-checkbox.js +103 -0
- package/src/components/forms/glk-input.js +138 -0
- package/src/components/forms/glk-radio.js +109 -0
- package/src/components/forms/glk-range.js +112 -0
- package/src/components/forms/glk-search.js +87 -0
- package/src/components/forms/glk-select.js +96 -0
- package/src/components/forms/glk-textarea.js +95 -0
- package/src/components/forms/glk-toggle.js +121 -0
- package/src/components/navigation/glk-nav.js +12 -0
- package/src/components/navigation/glk-pill.js +48 -0
- package/src/components/navigation/glk-tab-bar.js +31 -0
- package/src/components/navigation/glk-tab-item.js +74 -0
- 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 };
|