@haiyue/ui 0.1.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 +9 -0
- package/dist/checkbox.d.ts +30 -0
- package/dist/checkbox.js +204 -0
- package/dist/context-menu.d.ts +41 -0
- package/dist/context-menu.js +225 -0
- package/dist/dialog.d.ts +30 -0
- package/dist/dialog.js +183 -0
- package/dist/dropdown.d.ts +45 -0
- package/dist/dropdown.js +228 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +110 -0
- package/dist/input.d.ts +30 -0
- package/dist/input.js +131 -0
- package/dist/menu-shared.d.ts +14 -0
- package/dist/menu-shared.js +54 -0
- package/dist/radio.d.ts +30 -0
- package/dist/radio.js +205 -0
- package/dist/select.d.ts +40 -0
- package/dist/select.js +253 -0
- package/dist/split.d.ts +46 -0
- package/dist/split.js +277 -0
- package/dist/tabs.d.ts +28 -0
- package/dist/tabs.js +161 -0
- package/dist/tooltip.d.ts +37 -0
- package/dist/tooltip.js +286 -0
- package/dist/tree-model.d.ts +35 -0
- package/dist/tree-model.js +281 -0
- package/dist/tree-node.d.ts +11 -0
- package/dist/tree-node.js +63 -0
- package/dist/tree-types.d.ts +41 -0
- package/dist/tree-types.js +2 -0
- package/dist/tree.d.ts +102 -0
- package/dist/tree.js +942 -0
- package/package.json +46 -0
- package/src/checkbox.ts +215 -0
- package/src/context-menu.ts +248 -0
- package/src/dialog.ts +195 -0
- package/src/dropdown.ts +249 -0
- package/src/index.ts +192 -0
- package/src/input.ts +150 -0
- package/src/menu-shared.ts +65 -0
- package/src/radio.ts +216 -0
- package/src/select.ts +271 -0
- package/src/split.ts +306 -0
- package/src/tabs.ts +182 -0
- package/src/tooltip.ts +321 -0
- package/src/tree-model.ts +305 -0
- package/src/tree-node.ts +67 -0
- package/src/tree-types.ts +47 -0
- package/src/tree.ts +1002 -0
package/dist/select.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
export class GESelect extends HTMLElement {
|
|
2
|
+
_wrap = document.createElement('div');
|
|
3
|
+
_search = document.createElement('input');
|
|
4
|
+
_select = document.createElement('select');
|
|
5
|
+
_options = [];
|
|
6
|
+
_filterText = '';
|
|
7
|
+
static get observedAttributes() {
|
|
8
|
+
return ['options', 'value', 'disabled', 'searchable', 'aria-label', 'label'];
|
|
9
|
+
}
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
13
|
+
const style = document.createElement('style');
|
|
14
|
+
style.textContent = `
|
|
15
|
+
:host {
|
|
16
|
+
display: block;
|
|
17
|
+
width: 100%;
|
|
18
|
+
min-width: 0;
|
|
19
|
+
}
|
|
20
|
+
.wrap {
|
|
21
|
+
display: grid;
|
|
22
|
+
gap: 4px;
|
|
23
|
+
width: 100%;
|
|
24
|
+
min-width: 0;
|
|
25
|
+
}
|
|
26
|
+
input,
|
|
27
|
+
select {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 28px;
|
|
31
|
+
min-width: 0;
|
|
32
|
+
padding: 0 28px 0 8px;
|
|
33
|
+
border: 1px solid var(--ge-border-color, #303746);
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
color: var(--ge-text-color, #d8e2f2);
|
|
36
|
+
background-color: var(--ge-input-bg-color, var(--ge-surface-color, #121822));
|
|
37
|
+
font: 12px system-ui, sans-serif;
|
|
38
|
+
outline: none;
|
|
39
|
+
color-scheme: dark;
|
|
40
|
+
}
|
|
41
|
+
select {
|
|
42
|
+
appearance: none;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
background-image:
|
|
45
|
+
linear-gradient(45deg, transparent 50%, var(--ge-select-arrow-color, var(--ge-secondary-text-color, #8fa7c8)) 50%),
|
|
46
|
+
linear-gradient(135deg, var(--ge-select-arrow-color, var(--ge-secondary-text-color, #8fa7c8)) 50%, transparent 50%);
|
|
47
|
+
background-position:
|
|
48
|
+
calc(100% - 15px) 11px,
|
|
49
|
+
calc(100% - 10px) 11px;
|
|
50
|
+
background-size: 5px 5px, 5px 5px;
|
|
51
|
+
background-repeat: no-repeat;
|
|
52
|
+
}
|
|
53
|
+
select:hover:not(:disabled) {
|
|
54
|
+
border-color: var(--ge-hover-border-color, #435268);
|
|
55
|
+
background-color: var(--ge-select-hover-bg-color, var(--ge-input-bg-color, #151d2a));
|
|
56
|
+
}
|
|
57
|
+
option {
|
|
58
|
+
min-height: 28px;
|
|
59
|
+
padding: 6px 8px;
|
|
60
|
+
color: var(--ge-select-option-text-color, var(--ge-text-color, #d8e2f2));
|
|
61
|
+
background: var(--ge-select-option-bg-color, var(--ge-menu-bg-color, #1b2230));
|
|
62
|
+
font: 12px system-ui, sans-serif;
|
|
63
|
+
}
|
|
64
|
+
option:checked {
|
|
65
|
+
color: var(--ge-select-option-selected-text-color, var(--ge-selected-text-color, #ffffff));
|
|
66
|
+
background: var(--ge-select-option-selected-bg-color, var(--ge-selected-bg-color, #255a91));
|
|
67
|
+
}
|
|
68
|
+
option:disabled {
|
|
69
|
+
color: var(--ge-disabled-text-color, #65738a);
|
|
70
|
+
background: var(--ge-select-option-bg-color, var(--ge-menu-bg-color, #1b2230));
|
|
71
|
+
}
|
|
72
|
+
input {
|
|
73
|
+
display: none;
|
|
74
|
+
}
|
|
75
|
+
:host([searchable]) input {
|
|
76
|
+
display: block;
|
|
77
|
+
}
|
|
78
|
+
input:focus,
|
|
79
|
+
select:focus {
|
|
80
|
+
border-color: var(--ge-focus-border-color, #3d6fa8);
|
|
81
|
+
box-shadow: 0 0 0 2px var(--ge-focus-ring-color, rgba(61, 111, 168, 0.24));
|
|
82
|
+
}
|
|
83
|
+
input:disabled,
|
|
84
|
+
select:disabled {
|
|
85
|
+
opacity: 0.55;
|
|
86
|
+
cursor: default;
|
|
87
|
+
background-image: none;
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
this._wrap.className = 'wrap';
|
|
91
|
+
this._search.type = 'search';
|
|
92
|
+
this._search.placeholder = 'Search...';
|
|
93
|
+
this._wrap.append(this._search, this._select);
|
|
94
|
+
root.append(style, this._wrap);
|
|
95
|
+
}
|
|
96
|
+
connectedCallback() {
|
|
97
|
+
this._select.addEventListener('change', this._onChange);
|
|
98
|
+
this._search.addEventListener('input', this._onSearchInput);
|
|
99
|
+
this._syncFromAttributes();
|
|
100
|
+
this._render();
|
|
101
|
+
}
|
|
102
|
+
disconnectedCallback() {
|
|
103
|
+
this._select.removeEventListener('change', this._onChange);
|
|
104
|
+
this._search.removeEventListener('input', this._onSearchInput);
|
|
105
|
+
this._filterText = '';
|
|
106
|
+
this._search.value = '';
|
|
107
|
+
}
|
|
108
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
109
|
+
if (name === 'options')
|
|
110
|
+
this._readOptionsAttribute();
|
|
111
|
+
if (name === 'value')
|
|
112
|
+
this._select.value = newValue ?? '';
|
|
113
|
+
if (name === 'disabled' || name === 'searchable')
|
|
114
|
+
this._syncDisabled();
|
|
115
|
+
if (name === 'aria-label' || name === 'label')
|
|
116
|
+
this._syncAriaLabel();
|
|
117
|
+
if (name === 'options' || name === 'searchable')
|
|
118
|
+
this._render();
|
|
119
|
+
}
|
|
120
|
+
get options() {
|
|
121
|
+
return this._options;
|
|
122
|
+
}
|
|
123
|
+
set options(value) {
|
|
124
|
+
this._options = Array.isArray(value) ? value : [];
|
|
125
|
+
this._render();
|
|
126
|
+
}
|
|
127
|
+
get value() {
|
|
128
|
+
return this.getAttribute('value') ?? '';
|
|
129
|
+
}
|
|
130
|
+
set value(value) {
|
|
131
|
+
this.setAttribute('value', value);
|
|
132
|
+
this._select.value = value;
|
|
133
|
+
}
|
|
134
|
+
get disabled() {
|
|
135
|
+
return this.hasAttribute('disabled');
|
|
136
|
+
}
|
|
137
|
+
set disabled(value) {
|
|
138
|
+
if (value)
|
|
139
|
+
this.setAttribute('disabled', '');
|
|
140
|
+
else
|
|
141
|
+
this.removeAttribute('disabled');
|
|
142
|
+
}
|
|
143
|
+
get searchable() {
|
|
144
|
+
return this.hasAttribute('searchable');
|
|
145
|
+
}
|
|
146
|
+
set searchable(value) {
|
|
147
|
+
if (value)
|
|
148
|
+
this.setAttribute('searchable', '');
|
|
149
|
+
else
|
|
150
|
+
this.removeAttribute('searchable');
|
|
151
|
+
}
|
|
152
|
+
get label() {
|
|
153
|
+
return this.getAttribute('label') ?? '';
|
|
154
|
+
}
|
|
155
|
+
set label(value) {
|
|
156
|
+
if (value)
|
|
157
|
+
this.setAttribute('label', value);
|
|
158
|
+
else
|
|
159
|
+
this.removeAttribute('label');
|
|
160
|
+
}
|
|
161
|
+
_syncFromAttributes() {
|
|
162
|
+
// Dynamic consumers commonly assign `options` before appending the element.
|
|
163
|
+
// Do not erase that programmatic state merely because no attribute exists.
|
|
164
|
+
if (this.hasAttribute('options'))
|
|
165
|
+
this._readOptionsAttribute();
|
|
166
|
+
this._select.value = this.value;
|
|
167
|
+
this._syncDisabled();
|
|
168
|
+
this._syncAriaLabel();
|
|
169
|
+
}
|
|
170
|
+
_readOptionsAttribute() {
|
|
171
|
+
const raw = this.getAttribute('options');
|
|
172
|
+
if (!raw) {
|
|
173
|
+
this._options = [];
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const parsed = JSON.parse(raw);
|
|
178
|
+
this._options = Array.isArray(parsed)
|
|
179
|
+
? parsed.map((item) => ({
|
|
180
|
+
label: String(item.label ?? item.value ?? ''),
|
|
181
|
+
value: String(item.value ?? item.label ?? ''),
|
|
182
|
+
disabled: Boolean(item.disabled),
|
|
183
|
+
}))
|
|
184
|
+
: [];
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
console.warn('Invalid ge-select options attribute.', error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
_render() {
|
|
191
|
+
this._select.replaceChildren();
|
|
192
|
+
const visibleOptions = this.searchable && this._filterText
|
|
193
|
+
? this._options.filter(item => this._matchesFilter(item, this._filterText))
|
|
194
|
+
: this._options;
|
|
195
|
+
for (const item of visibleOptions) {
|
|
196
|
+
const option = document.createElement('option');
|
|
197
|
+
option.value = item.value;
|
|
198
|
+
option.textContent = item.label;
|
|
199
|
+
option.disabled = Boolean(item.disabled);
|
|
200
|
+
this._select.append(option);
|
|
201
|
+
}
|
|
202
|
+
this._select.value = this.value;
|
|
203
|
+
this._syncDisabled();
|
|
204
|
+
}
|
|
205
|
+
_syncDisabled() {
|
|
206
|
+
this._select.disabled = this.disabled;
|
|
207
|
+
this._search.disabled = this.disabled;
|
|
208
|
+
}
|
|
209
|
+
_syncAriaLabel() {
|
|
210
|
+
const label = this.getAttribute('aria-label') ?? this.getAttribute('label') ?? '';
|
|
211
|
+
if (label) {
|
|
212
|
+
this._select.setAttribute('aria-label', label);
|
|
213
|
+
this._search.setAttribute('aria-label', `${label} search`);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
this._select.removeAttribute('aria-label');
|
|
217
|
+
this._search.removeAttribute('aria-label');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
_matchesFilter(option, filter) {
|
|
221
|
+
const haystack = `${option.label} ${option.value}`.toLowerCase();
|
|
222
|
+
const needle = filter.trim().toLowerCase();
|
|
223
|
+
if (!needle)
|
|
224
|
+
return true;
|
|
225
|
+
let index = 0;
|
|
226
|
+
for (const char of needle) {
|
|
227
|
+
index = haystack.indexOf(char, index);
|
|
228
|
+
if (index < 0)
|
|
229
|
+
return false;
|
|
230
|
+
index++;
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
_onChange = () => {
|
|
235
|
+
this.value = this._select.value;
|
|
236
|
+
const option = this._options.find(item => item.value === this.value) ?? null;
|
|
237
|
+
this.dispatchEvent(new CustomEvent('value-change', {
|
|
238
|
+
detail: { value: this.value, option },
|
|
239
|
+
bubbles: true,
|
|
240
|
+
composed: true,
|
|
241
|
+
}));
|
|
242
|
+
};
|
|
243
|
+
_onSearchInput = () => {
|
|
244
|
+
this._filterText = this._search.value;
|
|
245
|
+
this._render();
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function defineSelectComponents() {
|
|
249
|
+
if (!customElements.get('ge-select')) {
|
|
250
|
+
customElements.define('ge-select', GESelect);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=select.js.map
|
package/dist/split.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type GESplitDirection = 'horizontal' | 'vertical';
|
|
2
|
+
export interface GESplitRatioChangeDetail {
|
|
3
|
+
ratio: number;
|
|
4
|
+
direction: GESplitDirection;
|
|
5
|
+
}
|
|
6
|
+
export declare class GESplit extends HTMLElement {
|
|
7
|
+
private readonly _style;
|
|
8
|
+
private readonly _first;
|
|
9
|
+
private readonly _second;
|
|
10
|
+
private readonly _bar;
|
|
11
|
+
private readonly _resizeObserver;
|
|
12
|
+
private _dragging;
|
|
13
|
+
private _pointerId;
|
|
14
|
+
private _layoutFrame;
|
|
15
|
+
static get observedAttributes(): string[];
|
|
16
|
+
constructor();
|
|
17
|
+
connectedCallback(): void;
|
|
18
|
+
disconnectedCallback(): void;
|
|
19
|
+
attributeChangedCallback(): void;
|
|
20
|
+
get direction(): GESplitDirection;
|
|
21
|
+
set direction(value: GESplitDirection);
|
|
22
|
+
get ratio(): number;
|
|
23
|
+
set ratio(value: number);
|
|
24
|
+
get minFirst(): number;
|
|
25
|
+
set minFirst(value: number);
|
|
26
|
+
get minSecond(): number;
|
|
27
|
+
set minSecond(value: number);
|
|
28
|
+
get barSize(): number;
|
|
29
|
+
set barSize(value: number);
|
|
30
|
+
private _createSlot;
|
|
31
|
+
private _onPointerDown;
|
|
32
|
+
private _onPointerMove;
|
|
33
|
+
private _onKeyDown;
|
|
34
|
+
private _onPointerUp;
|
|
35
|
+
private _applyLayout;
|
|
36
|
+
private _clampRatio;
|
|
37
|
+
private _scheduleLayout;
|
|
38
|
+
private _syncAria;
|
|
39
|
+
private _emitRatioChange;
|
|
40
|
+
private _getAvailablePaneSize;
|
|
41
|
+
private _getLayoutSize;
|
|
42
|
+
private _getLayoutBarSize;
|
|
43
|
+
private _snapFirstSize;
|
|
44
|
+
private _clampFirstSize;
|
|
45
|
+
}
|
|
46
|
+
export declare function defineSplitComponents(): void;
|
package/dist/split.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
const DEFAULT_BAR_SIZE = 6;
|
|
2
|
+
export class GESplit extends HTMLElement {
|
|
3
|
+
_style = document.createElement('style');
|
|
4
|
+
_first = document.createElement('div');
|
|
5
|
+
_second = document.createElement('div');
|
|
6
|
+
_bar = document.createElement('div');
|
|
7
|
+
_resizeObserver = new ResizeObserver(() => this._scheduleLayout());
|
|
8
|
+
_dragging = false;
|
|
9
|
+
_pointerId = -1;
|
|
10
|
+
_layoutFrame = 0;
|
|
11
|
+
static get observedAttributes() {
|
|
12
|
+
return ['direction', 'ratio', 'min-first', 'min-second', 'bar-size'];
|
|
13
|
+
}
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
17
|
+
this._style.textContent = `
|
|
18
|
+
:host {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
display: grid;
|
|
21
|
+
width: 100%;
|
|
22
|
+
height: 100%;
|
|
23
|
+
min-width: 0;
|
|
24
|
+
min-height: 0;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
color: var(--ge-split-text-color, var(--ge-text-color, #d8e2f2));
|
|
27
|
+
background: var(--ge-split-bg-color, var(--ge-surface-color, #121822));
|
|
28
|
+
}
|
|
29
|
+
.pane {
|
|
30
|
+
min-width: 0;
|
|
31
|
+
min-height: 0;
|
|
32
|
+
overflow: auto;
|
|
33
|
+
background: var(--ge-split-pane-bg-color, transparent);
|
|
34
|
+
}
|
|
35
|
+
.bar {
|
|
36
|
+
background: var(--ge-split-bar-color, var(--ge-border-strong-color, #273142));
|
|
37
|
+
position: relative;
|
|
38
|
+
z-index: 1;
|
|
39
|
+
transition: background var(--ge-split-bar-transition-duration, 0.12s) ease;
|
|
40
|
+
}
|
|
41
|
+
.bar:focus {
|
|
42
|
+
outline: var(--ge-split-focus-ring-width, 1px) solid var(--ge-split-focus-ring-color, var(--ge-focus-border-color, var(--ge-accent-color, #3d6fa8)));
|
|
43
|
+
outline-offset: -1px;
|
|
44
|
+
}
|
|
45
|
+
.bar:hover,
|
|
46
|
+
.bar.dragging {
|
|
47
|
+
background: var(--ge-split-bar-active-color, var(--ge-accent-color, #3d6fa8));
|
|
48
|
+
}
|
|
49
|
+
:host([direction="vertical"]) .bar {
|
|
50
|
+
cursor: row-resize;
|
|
51
|
+
}
|
|
52
|
+
:host(:not([direction="vertical"])) .bar {
|
|
53
|
+
cursor: col-resize;
|
|
54
|
+
}
|
|
55
|
+
:host([direction="vertical"]) .bar::before {
|
|
56
|
+
content: "";
|
|
57
|
+
position: absolute;
|
|
58
|
+
left: 0;
|
|
59
|
+
right: 0;
|
|
60
|
+
top: calc(var(--ge-split-bar-hit-outset, 3px) * -1);
|
|
61
|
+
bottom: calc(var(--ge-split-bar-hit-outset, 3px) * -1);
|
|
62
|
+
}
|
|
63
|
+
:host(:not([direction="vertical"])) .bar::before {
|
|
64
|
+
content: "";
|
|
65
|
+
position: absolute;
|
|
66
|
+
top: 0;
|
|
67
|
+
bottom: 0;
|
|
68
|
+
left: calc(var(--ge-split-bar-hit-outset, 3px) * -1);
|
|
69
|
+
right: calc(var(--ge-split-bar-hit-outset, 3px) * -1);
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
this._first.className = 'pane first';
|
|
73
|
+
this._second.className = 'pane second';
|
|
74
|
+
this._bar.className = 'bar';
|
|
75
|
+
this._bar.tabIndex = 0;
|
|
76
|
+
this._bar.setAttribute('role', 'separator');
|
|
77
|
+
this._bar.setAttribute('aria-valuemin', '0');
|
|
78
|
+
this._bar.setAttribute('aria-valuemax', '100');
|
|
79
|
+
this._first.append(this._createSlot('first'));
|
|
80
|
+
this._second.append(this._createSlot('second'));
|
|
81
|
+
root.append(this._style, this._first, this._bar, this._second);
|
|
82
|
+
}
|
|
83
|
+
connectedCallback() {
|
|
84
|
+
if (!this.hasAttribute('direction'))
|
|
85
|
+
this.setAttribute('direction', 'horizontal');
|
|
86
|
+
if (!this.hasAttribute('ratio'))
|
|
87
|
+
this.setAttribute('ratio', '0.5');
|
|
88
|
+
this._bar.addEventListener('pointerdown', this._onPointerDown);
|
|
89
|
+
this._bar.addEventListener('keydown', this._onKeyDown);
|
|
90
|
+
this._resizeObserver.observe(this);
|
|
91
|
+
this._syncAria();
|
|
92
|
+
this._scheduleLayout();
|
|
93
|
+
}
|
|
94
|
+
disconnectedCallback() {
|
|
95
|
+
this._resizeObserver.disconnect();
|
|
96
|
+
this._bar.removeEventListener('pointerdown', this._onPointerDown);
|
|
97
|
+
this._bar.removeEventListener('keydown', this._onKeyDown);
|
|
98
|
+
window.removeEventListener('pointermove', this._onPointerMove);
|
|
99
|
+
window.removeEventListener('pointerup', this._onPointerUp);
|
|
100
|
+
window.removeEventListener('pointercancel', this._onPointerUp);
|
|
101
|
+
if (this._layoutFrame) {
|
|
102
|
+
cancelAnimationFrame(this._layoutFrame);
|
|
103
|
+
this._layoutFrame = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
attributeChangedCallback() {
|
|
107
|
+
this._syncAria();
|
|
108
|
+
this._scheduleLayout();
|
|
109
|
+
}
|
|
110
|
+
get direction() {
|
|
111
|
+
return this.getAttribute('direction') === 'vertical' ? 'vertical' : 'horizontal';
|
|
112
|
+
}
|
|
113
|
+
set direction(value) {
|
|
114
|
+
this.setAttribute('direction', value);
|
|
115
|
+
}
|
|
116
|
+
get ratio() {
|
|
117
|
+
return this._clampRatio(Number(this.getAttribute('ratio') ?? 0.5));
|
|
118
|
+
}
|
|
119
|
+
set ratio(value) {
|
|
120
|
+
this.setAttribute('ratio', String(this._clampRatio(value)));
|
|
121
|
+
}
|
|
122
|
+
get minFirst() {
|
|
123
|
+
return Math.max(0, Number(this.getAttribute('min-first') ?? 80));
|
|
124
|
+
}
|
|
125
|
+
set minFirst(value) {
|
|
126
|
+
this.setAttribute('min-first', String(Math.max(0, value)));
|
|
127
|
+
}
|
|
128
|
+
get minSecond() {
|
|
129
|
+
return Math.max(0, Number(this.getAttribute('min-second') ?? 80));
|
|
130
|
+
}
|
|
131
|
+
set minSecond(value) {
|
|
132
|
+
this.setAttribute('min-second', String(Math.max(0, value)));
|
|
133
|
+
}
|
|
134
|
+
get barSize() {
|
|
135
|
+
const value = Number(this.getAttribute('bar-size') ?? DEFAULT_BAR_SIZE);
|
|
136
|
+
return Number.isFinite(value) ? Math.max(1, value) : DEFAULT_BAR_SIZE;
|
|
137
|
+
}
|
|
138
|
+
set barSize(value) {
|
|
139
|
+
this.setAttribute('bar-size', String(Math.max(1, value)));
|
|
140
|
+
}
|
|
141
|
+
_createSlot(name) {
|
|
142
|
+
const slot = document.createElement('slot');
|
|
143
|
+
slot.name = name;
|
|
144
|
+
return slot;
|
|
145
|
+
}
|
|
146
|
+
_onPointerDown = (event) => {
|
|
147
|
+
this._dragging = true;
|
|
148
|
+
this._pointerId = event.pointerId;
|
|
149
|
+
this._bar.classList.add('dragging');
|
|
150
|
+
this._bar.setPointerCapture(event.pointerId);
|
|
151
|
+
window.addEventListener('pointermove', this._onPointerMove);
|
|
152
|
+
window.addEventListener('pointerup', this._onPointerUp);
|
|
153
|
+
window.addEventListener('pointercancel', this._onPointerUp);
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
};
|
|
156
|
+
_onPointerMove = (event) => {
|
|
157
|
+
if (!this._dragging || event.pointerId !== this._pointerId)
|
|
158
|
+
return;
|
|
159
|
+
const rect = this.getBoundingClientRect();
|
|
160
|
+
const size = this._getLayoutSize();
|
|
161
|
+
const available = this._getAvailablePaneSize(size);
|
|
162
|
+
if (available <= 0)
|
|
163
|
+
return;
|
|
164
|
+
// Pointer coordinates and DOMRect are visual (and may be transformed or
|
|
165
|
+
// fractional), while CSS layout consumers usually observe integer
|
|
166
|
+
// clientWidth/clientHeight. Map between the two coordinate spaces before
|
|
167
|
+
// snapping the divider to the layout pixel grid.
|
|
168
|
+
const visualSize = this.direction === 'vertical' ? rect.height : rect.width;
|
|
169
|
+
const visualOffset = this.direction === 'vertical'
|
|
170
|
+
? event.clientY - rect.top
|
|
171
|
+
: event.clientX - rect.left;
|
|
172
|
+
const offset = visualSize > 0 ? visualOffset * size / visualSize : 0;
|
|
173
|
+
const firstSize = this._snapFirstSize(offset - this._getLayoutBarSize() / 2, available);
|
|
174
|
+
this.ratio = firstSize / available;
|
|
175
|
+
this._emitRatioChange();
|
|
176
|
+
};
|
|
177
|
+
_onKeyDown = (event) => {
|
|
178
|
+
const direction = this.direction;
|
|
179
|
+
const backward = direction === 'vertical' ? event.key === 'ArrowUp' : event.key === 'ArrowLeft';
|
|
180
|
+
const forward = direction === 'vertical' ? event.key === 'ArrowDown' : event.key === 'ArrowRight';
|
|
181
|
+
if (!backward && !forward && event.key !== 'Home' && event.key !== 'End')
|
|
182
|
+
return;
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
const step = event.shiftKey ? 0.1 : 0.02;
|
|
185
|
+
if (event.key === 'Home')
|
|
186
|
+
this.ratio = 0;
|
|
187
|
+
else if (event.key === 'End')
|
|
188
|
+
this.ratio = 1;
|
|
189
|
+
else
|
|
190
|
+
this.ratio = this.ratio + (forward ? step : -step);
|
|
191
|
+
this._emitRatioChange();
|
|
192
|
+
};
|
|
193
|
+
_onPointerUp = (event) => {
|
|
194
|
+
if (!this._dragging || event.pointerId !== this._pointerId)
|
|
195
|
+
return;
|
|
196
|
+
this._dragging = false;
|
|
197
|
+
this._pointerId = -1;
|
|
198
|
+
this._bar.classList.remove('dragging');
|
|
199
|
+
window.removeEventListener('pointermove', this._onPointerMove);
|
|
200
|
+
window.removeEventListener('pointerup', this._onPointerUp);
|
|
201
|
+
window.removeEventListener('pointercancel', this._onPointerUp);
|
|
202
|
+
};
|
|
203
|
+
_applyLayout() {
|
|
204
|
+
this._layoutFrame = 0;
|
|
205
|
+
const size = this._getLayoutSize();
|
|
206
|
+
const available = this._getAvailablePaneSize(size);
|
|
207
|
+
const firstSize = available > 0 ? this._snapFirstSize(this.ratio * available, available) : 0;
|
|
208
|
+
const secondSize = Math.max(0, available - firstSize);
|
|
209
|
+
const barSize = `${this._getLayoutBarSize()}px`;
|
|
210
|
+
if (this.direction === 'vertical') {
|
|
211
|
+
this.style.gridTemplateColumns = 'minmax(0, 1fr)';
|
|
212
|
+
this.style.gridTemplateRows = `${firstSize}px ${barSize} minmax(0, ${secondSize}px)`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
this.style.gridTemplateRows = 'minmax(0, 1fr)';
|
|
216
|
+
this.style.gridTemplateColumns = `${firstSize}px ${barSize} minmax(0, ${secondSize}px)`;
|
|
217
|
+
}
|
|
218
|
+
this._syncAria();
|
|
219
|
+
}
|
|
220
|
+
_clampRatio(value) {
|
|
221
|
+
if (!Number.isFinite(value))
|
|
222
|
+
return 0.5;
|
|
223
|
+
return Math.max(0, Math.min(1, value));
|
|
224
|
+
}
|
|
225
|
+
_scheduleLayout() {
|
|
226
|
+
if (!this.isConnected)
|
|
227
|
+
return;
|
|
228
|
+
if (this._layoutFrame)
|
|
229
|
+
return;
|
|
230
|
+
this._layoutFrame = requestAnimationFrame(() => this._applyLayout());
|
|
231
|
+
}
|
|
232
|
+
_syncAria() {
|
|
233
|
+
const direction = this.direction;
|
|
234
|
+
this._bar.setAttribute('aria-orientation', direction === 'vertical' ? 'horizontal' : 'vertical');
|
|
235
|
+
this._bar.setAttribute('aria-valuenow', String(Math.round(this.ratio * 100)));
|
|
236
|
+
}
|
|
237
|
+
_emitRatioChange() {
|
|
238
|
+
this._syncAria();
|
|
239
|
+
this.dispatchEvent(new CustomEvent('ratio-change', {
|
|
240
|
+
detail: { ratio: this.ratio, direction: this.direction },
|
|
241
|
+
bubbles: true,
|
|
242
|
+
composed: true,
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
_getAvailablePaneSize(size) {
|
|
246
|
+
return Math.max(0, size - this._getLayoutBarSize());
|
|
247
|
+
}
|
|
248
|
+
_getLayoutSize() {
|
|
249
|
+
return this.direction === 'vertical' ? this.clientHeight : this.clientWidth;
|
|
250
|
+
}
|
|
251
|
+
_getLayoutBarSize() {
|
|
252
|
+
return Math.max(1, Math.round(this.barSize));
|
|
253
|
+
}
|
|
254
|
+
_snapFirstSize(value, available) {
|
|
255
|
+
if (available <= 0)
|
|
256
|
+
return 0;
|
|
257
|
+
const clamped = this._clampFirstSize(value, available);
|
|
258
|
+
const minFirst = Math.ceil(Math.min(this.minFirst, available));
|
|
259
|
+
const minSecond = Math.ceil(Math.min(this.minSecond, Math.max(0, available - minFirst)));
|
|
260
|
+
const maxFirst = Math.max(minFirst, available - minSecond);
|
|
261
|
+
return Math.max(minFirst, Math.min(maxFirst, Math.round(clamped)));
|
|
262
|
+
}
|
|
263
|
+
_clampFirstSize(value, available) {
|
|
264
|
+
if (available <= 0)
|
|
265
|
+
return 0;
|
|
266
|
+
const minFirst = Math.min(this.minFirst, available);
|
|
267
|
+
const minSecond = Math.min(this.minSecond, Math.max(0, available - minFirst));
|
|
268
|
+
const maxFirst = Math.max(minFirst, available - minSecond);
|
|
269
|
+
return Math.max(minFirst, Math.min(maxFirst, value));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export function defineSplitComponents() {
|
|
273
|
+
if (!customElements.get('ge-split')) {
|
|
274
|
+
customElements.define('ge-split', GESplit);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=split.js.map
|
package/dist/tabs.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface GETabOption {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface GETabChangeDetail {
|
|
7
|
+
value: string;
|
|
8
|
+
option: GETabOption | null;
|
|
9
|
+
}
|
|
10
|
+
export declare class GETabs extends HTMLElement {
|
|
11
|
+
private readonly _style;
|
|
12
|
+
private readonly _tabs;
|
|
13
|
+
private readonly _panels;
|
|
14
|
+
private _options;
|
|
15
|
+
static get observedAttributes(): string[];
|
|
16
|
+
constructor();
|
|
17
|
+
connectedCallback(): void;
|
|
18
|
+
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
|
|
19
|
+
get options(): GETabOption[];
|
|
20
|
+
set options(value: GETabOption[]);
|
|
21
|
+
get value(): string;
|
|
22
|
+
set value(value: string);
|
|
23
|
+
private _syncFromAttributes;
|
|
24
|
+
private _readOptionsAttribute;
|
|
25
|
+
private _render;
|
|
26
|
+
private _select;
|
|
27
|
+
}
|
|
28
|
+
export declare function defineTabsComponents(): void;
|