@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HaiyueStudio Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface GECheckboxChangeDetail {
|
|
2
|
+
checked: boolean;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class GECheckbox extends HTMLElement {
|
|
6
|
+
private readonly _input;
|
|
7
|
+
private readonly _box;
|
|
8
|
+
private readonly _label;
|
|
9
|
+
private readonly _slot;
|
|
10
|
+
static get observedAttributes(): string[];
|
|
11
|
+
constructor();
|
|
12
|
+
connectedCallback(): void;
|
|
13
|
+
disconnectedCallback(): void;
|
|
14
|
+
attributeChangedCallback(): void;
|
|
15
|
+
get checked(): boolean;
|
|
16
|
+
set checked(value: boolean);
|
|
17
|
+
get disabled(): boolean;
|
|
18
|
+
set disabled(value: boolean);
|
|
19
|
+
get indeterminate(): boolean;
|
|
20
|
+
set indeterminate(value: boolean);
|
|
21
|
+
get label(): string;
|
|
22
|
+
set label(value: string);
|
|
23
|
+
get value(): string;
|
|
24
|
+
set value(value: string);
|
|
25
|
+
get name(): string;
|
|
26
|
+
set name(value: string);
|
|
27
|
+
private _sync;
|
|
28
|
+
private _onChange;
|
|
29
|
+
}
|
|
30
|
+
export declare function defineCheckboxComponents(): void;
|
package/dist/checkbox.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
export class GECheckbox extends HTMLElement {
|
|
2
|
+
_input = document.createElement('input');
|
|
3
|
+
_box = document.createElement('span');
|
|
4
|
+
_label = document.createElement('span');
|
|
5
|
+
_slot = document.createElement('slot');
|
|
6
|
+
static get observedAttributes() {
|
|
7
|
+
return ['checked', 'disabled', 'indeterminate', 'label', 'value', 'name', 'aria-label'];
|
|
8
|
+
}
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
12
|
+
const style = document.createElement('style');
|
|
13
|
+
style.textContent = `
|
|
14
|
+
:host {
|
|
15
|
+
display: inline-block;
|
|
16
|
+
width: max-content;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
color: var(--ge-checkbox-text-color, var(--ge-text-color, #d8e2f2));
|
|
19
|
+
font: 12px system-ui, sans-serif;
|
|
20
|
+
}
|
|
21
|
+
:host([disabled]) {
|
|
22
|
+
opacity: 0.55;
|
|
23
|
+
cursor: default;
|
|
24
|
+
}
|
|
25
|
+
label {
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 8px;
|
|
29
|
+
min-width: 0;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
user-select: none;
|
|
32
|
+
}
|
|
33
|
+
:host([disabled]) label {
|
|
34
|
+
cursor: default;
|
|
35
|
+
}
|
|
36
|
+
input {
|
|
37
|
+
position: absolute;
|
|
38
|
+
width: 1px;
|
|
39
|
+
height: 1px;
|
|
40
|
+
margin: -1px;
|
|
41
|
+
padding: 0;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
clip: rect(0 0 0 0);
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
border: 0;
|
|
46
|
+
}
|
|
47
|
+
.box {
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
width: 14px;
|
|
50
|
+
height: 14px;
|
|
51
|
+
flex: 0 0 14px;
|
|
52
|
+
display: inline-grid;
|
|
53
|
+
place-items: center;
|
|
54
|
+
border: 1px solid var(--ge-checkbox-border-color, var(--ge-border-color, #303746));
|
|
55
|
+
border-radius: 3px;
|
|
56
|
+
background: var(--ge-checkbox-bg-color, var(--ge-input-bg-color, #121822));
|
|
57
|
+
color: var(--ge-checkbox-mark-color, var(--ge-selected-text-color, #ffffff));
|
|
58
|
+
transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
|
|
59
|
+
}
|
|
60
|
+
input:focus-visible + .box {
|
|
61
|
+
border-color: var(--ge-focus-border-color, #3d6fa8);
|
|
62
|
+
box-shadow: 0 0 0 2px var(--ge-focus-ring-color, rgba(61, 111, 168, 0.28));
|
|
63
|
+
}
|
|
64
|
+
input:checked + .box,
|
|
65
|
+
input:indeterminate + .box {
|
|
66
|
+
border-color: var(--ge-checkbox-checked-border-color, var(--ge-accent-color, #3d6fa8));
|
|
67
|
+
background: var(--ge-checkbox-checked-bg-color, var(--ge-accent-strong-color, #255a91));
|
|
68
|
+
}
|
|
69
|
+
input:disabled + .box {
|
|
70
|
+
cursor: default;
|
|
71
|
+
}
|
|
72
|
+
.box::after {
|
|
73
|
+
content: "";
|
|
74
|
+
width: 7px;
|
|
75
|
+
height: 4px;
|
|
76
|
+
border: solid currentColor;
|
|
77
|
+
border-width: 0 0 2px 2px;
|
|
78
|
+
transform: rotate(-45deg) scale(0);
|
|
79
|
+
transform-origin: center;
|
|
80
|
+
transition: transform 0.12s ease;
|
|
81
|
+
}
|
|
82
|
+
input:checked + .box::after {
|
|
83
|
+
transform: rotate(-45deg) scale(1);
|
|
84
|
+
}
|
|
85
|
+
input:indeterminate + .box::after {
|
|
86
|
+
width: 7px;
|
|
87
|
+
height: 0;
|
|
88
|
+
border-width: 0 0 2px;
|
|
89
|
+
transform: rotate(0deg) scale(1);
|
|
90
|
+
}
|
|
91
|
+
.label {
|
|
92
|
+
min-width: 0;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const label = document.createElement('label');
|
|
99
|
+
this._input.type = 'checkbox';
|
|
100
|
+
this._box.className = 'box';
|
|
101
|
+
this._box.setAttribute('aria-hidden', 'true');
|
|
102
|
+
this._label.className = 'label';
|
|
103
|
+
this._label.append(this._slot);
|
|
104
|
+
label.append(this._input, this._box, this._label);
|
|
105
|
+
root.append(style, label);
|
|
106
|
+
}
|
|
107
|
+
connectedCallback() {
|
|
108
|
+
this._input.addEventListener('change', this._onChange);
|
|
109
|
+
this._sync();
|
|
110
|
+
}
|
|
111
|
+
disconnectedCallback() {
|
|
112
|
+
this._input.removeEventListener('change', this._onChange);
|
|
113
|
+
}
|
|
114
|
+
attributeChangedCallback() {
|
|
115
|
+
this._sync();
|
|
116
|
+
}
|
|
117
|
+
get checked() {
|
|
118
|
+
return this.hasAttribute('checked');
|
|
119
|
+
}
|
|
120
|
+
set checked(value) {
|
|
121
|
+
if (value)
|
|
122
|
+
this.setAttribute('checked', '');
|
|
123
|
+
else
|
|
124
|
+
this.removeAttribute('checked');
|
|
125
|
+
}
|
|
126
|
+
get disabled() {
|
|
127
|
+
return this.hasAttribute('disabled');
|
|
128
|
+
}
|
|
129
|
+
set disabled(value) {
|
|
130
|
+
if (value)
|
|
131
|
+
this.setAttribute('disabled', '');
|
|
132
|
+
else
|
|
133
|
+
this.removeAttribute('disabled');
|
|
134
|
+
}
|
|
135
|
+
get indeterminate() {
|
|
136
|
+
return this.hasAttribute('indeterminate');
|
|
137
|
+
}
|
|
138
|
+
set indeterminate(value) {
|
|
139
|
+
if (value)
|
|
140
|
+
this.setAttribute('indeterminate', '');
|
|
141
|
+
else
|
|
142
|
+
this.removeAttribute('indeterminate');
|
|
143
|
+
}
|
|
144
|
+
get label() {
|
|
145
|
+
return this.getAttribute('label') ?? '';
|
|
146
|
+
}
|
|
147
|
+
set label(value) {
|
|
148
|
+
if (value)
|
|
149
|
+
this.setAttribute('label', value);
|
|
150
|
+
else
|
|
151
|
+
this.removeAttribute('label');
|
|
152
|
+
}
|
|
153
|
+
get value() {
|
|
154
|
+
return this.getAttribute('value') ?? 'on';
|
|
155
|
+
}
|
|
156
|
+
set value(value) {
|
|
157
|
+
this.setAttribute('value', value);
|
|
158
|
+
}
|
|
159
|
+
get name() {
|
|
160
|
+
return this.getAttribute('name') ?? '';
|
|
161
|
+
}
|
|
162
|
+
set name(value) {
|
|
163
|
+
if (value)
|
|
164
|
+
this.setAttribute('name', value);
|
|
165
|
+
else
|
|
166
|
+
this.removeAttribute('name');
|
|
167
|
+
}
|
|
168
|
+
_sync() {
|
|
169
|
+
this._input.checked = this.checked;
|
|
170
|
+
this._input.disabled = this.disabled;
|
|
171
|
+
this._input.indeterminate = this.indeterminate;
|
|
172
|
+
this._input.value = this.value;
|
|
173
|
+
this._input.name = this.name;
|
|
174
|
+
const label = this.getAttribute('label');
|
|
175
|
+
if (label !== null)
|
|
176
|
+
this._slot.textContent = label;
|
|
177
|
+
else if (this._slot.textContent !== '')
|
|
178
|
+
this._slot.textContent = '';
|
|
179
|
+
const ariaLabel = this.getAttribute('aria-label') ?? label ?? '';
|
|
180
|
+
if (ariaLabel)
|
|
181
|
+
this._input.setAttribute('aria-label', ariaLabel);
|
|
182
|
+
else
|
|
183
|
+
this._input.removeAttribute('aria-label');
|
|
184
|
+
}
|
|
185
|
+
_onChange = () => {
|
|
186
|
+
if (this._input.checked)
|
|
187
|
+
this.setAttribute('checked', '');
|
|
188
|
+
else
|
|
189
|
+
this.removeAttribute('checked');
|
|
190
|
+
this.removeAttribute('indeterminate');
|
|
191
|
+
this.dispatchEvent(new CustomEvent('checked-change', {
|
|
192
|
+
detail: { checked: this.checked, value: this.value },
|
|
193
|
+
bubbles: true,
|
|
194
|
+
composed: true,
|
|
195
|
+
}));
|
|
196
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export function defineCheckboxComponents() {
|
|
200
|
+
if (!customElements.get('ge-checkbox')) {
|
|
201
|
+
customElements.define('ge-checkbox', GECheckbox);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=checkbox.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface GEContextMenuItem {
|
|
2
|
+
label?: string;
|
|
3
|
+
value?: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
separator?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface GEContextMenuSelectDetail {
|
|
8
|
+
value: string;
|
|
9
|
+
item: GEContextMenuItem;
|
|
10
|
+
}
|
|
11
|
+
export declare class GEContextMenu extends HTMLElement {
|
|
12
|
+
private readonly _root;
|
|
13
|
+
private readonly _style;
|
|
14
|
+
private readonly _menu;
|
|
15
|
+
private _items;
|
|
16
|
+
private _documentListenerAbort;
|
|
17
|
+
private _activeIndex;
|
|
18
|
+
static get observedAttributes(): string[];
|
|
19
|
+
constructor();
|
|
20
|
+
connectedCallback(): void;
|
|
21
|
+
disconnectedCallback(): void;
|
|
22
|
+
attributeChangedCallback(name: string): void;
|
|
23
|
+
get items(): GEContextMenuItem[];
|
|
24
|
+
set items(value: GEContextMenuItem[]);
|
|
25
|
+
openAt(x: number, y: number): void;
|
|
26
|
+
close(): void;
|
|
27
|
+
private _syncDocumentListeners;
|
|
28
|
+
private _attachDocumentListeners;
|
|
29
|
+
private _detachDocumentListeners;
|
|
30
|
+
private _readItemsAttribute;
|
|
31
|
+
private _render;
|
|
32
|
+
private _onDocumentPointerDown;
|
|
33
|
+
private _onDocumentKeyDown;
|
|
34
|
+
private _selectIndex;
|
|
35
|
+
private _setActiveIndex;
|
|
36
|
+
private _nextEnabledIndex;
|
|
37
|
+
private _firstEnabledIndex;
|
|
38
|
+
private _lastEnabledIndex;
|
|
39
|
+
private _isEnabledItem;
|
|
40
|
+
}
|
|
41
|
+
export declare function defineContextMenuComponents(): void;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { GE_MENU_ITEM_STYLES, createMenuItemButton, createMenuSeparator } from './menu-shared.js';
|
|
2
|
+
export class GEContextMenu extends HTMLElement {
|
|
3
|
+
_root;
|
|
4
|
+
_style = document.createElement('style');
|
|
5
|
+
_menu = document.createElement('div');
|
|
6
|
+
_items = [];
|
|
7
|
+
_documentListenerAbort = null;
|
|
8
|
+
_activeIndex = -1;
|
|
9
|
+
static get observedAttributes() {
|
|
10
|
+
return ['items', 'open'];
|
|
11
|
+
}
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this._root = this.attachShadow({ mode: 'open' });
|
|
15
|
+
this._style.textContent = `
|
|
16
|
+
:host {
|
|
17
|
+
position: fixed;
|
|
18
|
+
z-index: 10000;
|
|
19
|
+
display: none;
|
|
20
|
+
min-width: 132px;
|
|
21
|
+
color: var(--ge-text-color, #d8e2f2);
|
|
22
|
+
font: 12px system-ui, sans-serif;
|
|
23
|
+
}
|
|
24
|
+
:host([open]) {
|
|
25
|
+
display: block;
|
|
26
|
+
}
|
|
27
|
+
.menu {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
min-width: 132px;
|
|
30
|
+
padding: 4px;
|
|
31
|
+
border: 1px solid var(--ge-border-color, #303746);
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
background: var(--ge-menu-bg-color, var(--ge-surface-elevated-color, #1b2230));
|
|
34
|
+
box-shadow: 0 10px 26px var(--ge-menu-shadow-color, rgba(0, 0, 0, 0.36));
|
|
35
|
+
}
|
|
36
|
+
${GE_MENU_ITEM_STYLES}
|
|
37
|
+
`;
|
|
38
|
+
this._menu.className = 'menu';
|
|
39
|
+
this._menu.setAttribute('role', 'menu');
|
|
40
|
+
this._root.append(this._style, this._menu);
|
|
41
|
+
}
|
|
42
|
+
connectedCallback() {
|
|
43
|
+
this._readItemsAttribute();
|
|
44
|
+
this._render();
|
|
45
|
+
this._syncDocumentListeners();
|
|
46
|
+
}
|
|
47
|
+
disconnectedCallback() {
|
|
48
|
+
this._detachDocumentListeners();
|
|
49
|
+
}
|
|
50
|
+
attributeChangedCallback(name) {
|
|
51
|
+
if (name === 'items') {
|
|
52
|
+
this._readItemsAttribute();
|
|
53
|
+
this._render();
|
|
54
|
+
}
|
|
55
|
+
if (name === 'open')
|
|
56
|
+
this._syncDocumentListeners();
|
|
57
|
+
}
|
|
58
|
+
get items() {
|
|
59
|
+
return this._items;
|
|
60
|
+
}
|
|
61
|
+
set items(value) {
|
|
62
|
+
this._items = Array.isArray(value) ? value : [];
|
|
63
|
+
this._render();
|
|
64
|
+
}
|
|
65
|
+
openAt(x, y) {
|
|
66
|
+
this.setAttribute('open', '');
|
|
67
|
+
this.style.left = '0px';
|
|
68
|
+
this.style.top = '0px';
|
|
69
|
+
const rect = this._menu.getBoundingClientRect();
|
|
70
|
+
const viewport = getVisualViewportRect();
|
|
71
|
+
const left = Math.min(x, viewport.left + viewport.width - rect.width - 4);
|
|
72
|
+
const top = Math.min(y, viewport.top + viewport.height - rect.height - 4);
|
|
73
|
+
this.style.left = `${Math.max(viewport.left + 4, left)}px`;
|
|
74
|
+
this.style.top = `${Math.max(viewport.top + 4, top)}px`;
|
|
75
|
+
this._setActiveIndex(this._firstEnabledIndex(), false);
|
|
76
|
+
}
|
|
77
|
+
close() {
|
|
78
|
+
this.removeAttribute('open');
|
|
79
|
+
this._setActiveIndex(-1, false);
|
|
80
|
+
}
|
|
81
|
+
_syncDocumentListeners() {
|
|
82
|
+
if (this.hasAttribute('open') && this.isConnected)
|
|
83
|
+
this._attachDocumentListeners();
|
|
84
|
+
else
|
|
85
|
+
this._detachDocumentListeners();
|
|
86
|
+
}
|
|
87
|
+
_attachDocumentListeners() {
|
|
88
|
+
if (this._documentListenerAbort)
|
|
89
|
+
return;
|
|
90
|
+
this._documentListenerAbort = new AbortController();
|
|
91
|
+
const { signal } = this._documentListenerAbort;
|
|
92
|
+
document.addEventListener('pointerdown', this._onDocumentPointerDown, { capture: true, signal });
|
|
93
|
+
document.addEventListener('keydown', this._onDocumentKeyDown, { capture: true, signal });
|
|
94
|
+
}
|
|
95
|
+
_detachDocumentListeners() {
|
|
96
|
+
this._documentListenerAbort?.abort();
|
|
97
|
+
this._documentListenerAbort = null;
|
|
98
|
+
}
|
|
99
|
+
_readItemsAttribute() {
|
|
100
|
+
const raw = this.getAttribute('items');
|
|
101
|
+
if (!raw) {
|
|
102
|
+
this._items = [];
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(raw);
|
|
107
|
+
this._items = Array.isArray(parsed) ? parsed : [];
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.warn('Invalid ge-context-menu items attribute.', error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
_render() {
|
|
114
|
+
this._menu.replaceChildren();
|
|
115
|
+
this._activeIndex = -1;
|
|
116
|
+
for (const [index, item] of this._items.entries()) {
|
|
117
|
+
if (item.separator) {
|
|
118
|
+
this._menu.append(createMenuSeparator());
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const button = createMenuItemButton(item, { index, role: 'menuitem', tabIndex: -1 });
|
|
122
|
+
button.addEventListener('click', () => {
|
|
123
|
+
this._selectIndex(index);
|
|
124
|
+
});
|
|
125
|
+
this._menu.append(button);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
_onDocumentPointerDown = (event) => {
|
|
129
|
+
if (!this.hasAttribute('open'))
|
|
130
|
+
return;
|
|
131
|
+
const path = event.composedPath();
|
|
132
|
+
if (!path.includes(this))
|
|
133
|
+
this.close();
|
|
134
|
+
};
|
|
135
|
+
_onDocumentKeyDown = (event) => {
|
|
136
|
+
if (!this.hasAttribute('open'))
|
|
137
|
+
return;
|
|
138
|
+
if (event.key === 'Escape') {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
this.close();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (event.key === 'ArrowDown') {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
this._setActiveIndex(this._nextEnabledIndex(1), true);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event.key === 'ArrowUp') {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
this._setActiveIndex(this._nextEnabledIndex(-1), true);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (event.key === 'Home') {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
this._setActiveIndex(this._firstEnabledIndex(), true);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (event.key === 'End') {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
this._setActiveIndex(this._lastEnabledIndex(), true);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
this._selectIndex(this._activeIndex);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
_selectIndex(index) {
|
|
169
|
+
const item = this._items[index];
|
|
170
|
+
if (!item || item.disabled || item.separator || !item.value)
|
|
171
|
+
return;
|
|
172
|
+
this.dispatchEvent(new CustomEvent('item-select', {
|
|
173
|
+
detail: { value: item.value, item },
|
|
174
|
+
bubbles: true,
|
|
175
|
+
composed: true,
|
|
176
|
+
}));
|
|
177
|
+
this.close();
|
|
178
|
+
}
|
|
179
|
+
_setActiveIndex(index, focus) {
|
|
180
|
+
this._activeIndex = index;
|
|
181
|
+
for (const button of this._menu.querySelectorAll('button')) {
|
|
182
|
+
const active = Number(button.dataset.index) === index;
|
|
183
|
+
button.classList.toggle('active', active);
|
|
184
|
+
if (active && focus)
|
|
185
|
+
button.focus();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
_nextEnabledIndex(direction) {
|
|
189
|
+
if (!this._items.length)
|
|
190
|
+
return -1;
|
|
191
|
+
const start = this._activeIndex >= 0 ? this._activeIndex : direction > 0 ? -1 : this._items.length;
|
|
192
|
+
for (let step = 1; step <= this._items.length; step++) {
|
|
193
|
+
const index = (start + direction * step + this._items.length) % this._items.length;
|
|
194
|
+
if (this._isEnabledItem(index))
|
|
195
|
+
return index;
|
|
196
|
+
}
|
|
197
|
+
return -1;
|
|
198
|
+
}
|
|
199
|
+
_firstEnabledIndex() {
|
|
200
|
+
return this._items.findIndex((_, index) => this._isEnabledItem(index));
|
|
201
|
+
}
|
|
202
|
+
_lastEnabledIndex() {
|
|
203
|
+
for (let index = this._items.length - 1; index >= 0; index--) {
|
|
204
|
+
if (this._isEnabledItem(index))
|
|
205
|
+
return index;
|
|
206
|
+
}
|
|
207
|
+
return -1;
|
|
208
|
+
}
|
|
209
|
+
_isEnabledItem(index) {
|
|
210
|
+
const item = this._items[index];
|
|
211
|
+
return Boolean(item && !item.separator && !item.disabled && item.value);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function getVisualViewportRect() {
|
|
215
|
+
const viewport = window.visualViewport;
|
|
216
|
+
return viewport
|
|
217
|
+
? { left: viewport.offsetLeft, top: viewport.offsetTop, width: viewport.width, height: viewport.height }
|
|
218
|
+
: { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };
|
|
219
|
+
}
|
|
220
|
+
export function defineContextMenuComponents() {
|
|
221
|
+
if (!customElements.get('ge-context-menu')) {
|
|
222
|
+
customElements.define('ge-context-menu', GEContextMenu);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=context-menu.js.map
|
package/dist/dialog.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type GEDialogCloseReason = 'action' | 'backdrop' | 'escape' | 'programmatic';
|
|
2
|
+
export interface GEDialogCloseDetail {
|
|
3
|
+
readonly reason: GEDialogCloseReason | string;
|
|
4
|
+
}
|
|
5
|
+
/** Accessible modal surface with slotted body/footer content and reflected open state. */
|
|
6
|
+
export declare class GEDialog extends HTMLElement {
|
|
7
|
+
private readonly _dialog;
|
|
8
|
+
private readonly _title;
|
|
9
|
+
private readonly _closeButton;
|
|
10
|
+
private _returnFocus;
|
|
11
|
+
private _closeReason;
|
|
12
|
+
static get observedAttributes(): string[];
|
|
13
|
+
constructor();
|
|
14
|
+
connectedCallback(): void;
|
|
15
|
+
attributeChangedCallback(name: string): void;
|
|
16
|
+
get open(): boolean;
|
|
17
|
+
set open(value: boolean);
|
|
18
|
+
get dismissible(): boolean;
|
|
19
|
+
set dismissible(value: boolean);
|
|
20
|
+
get heading(): string;
|
|
21
|
+
set heading(value: string);
|
|
22
|
+
showModal(): void;
|
|
23
|
+
close(reason?: GEDialogCloseDetail['reason']): void;
|
|
24
|
+
private _syncHeading;
|
|
25
|
+
private _syncOpen;
|
|
26
|
+
private _onCancel;
|
|
27
|
+
private _onDialogClick;
|
|
28
|
+
private _onNativeClose;
|
|
29
|
+
}
|
|
30
|
+
export declare function defineDialogComponents(): void;
|