@studious-creative/yumekit 0.1.6 → 0.1.7
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/dist/components/y-appbar.js +29 -5
- package/dist/components/y-checkbox.js +11 -1
- package/dist/components/y-dialog.js +1 -1
- package/dist/components/y-drawer.js +1 -1
- package/dist/components/y-icon.d.ts +16 -0
- package/dist/components/y-icon.js +262 -0
- package/dist/components/y-menu.js +25 -4
- package/dist/components/y-panel.js +22 -6
- package/dist/components/y-select.d.ts +2 -0
- package/dist/components/y-select.js +21 -3
- package/dist/components/y-theme.js +1 -1
- package/dist/components/y-toast.js +1 -1
- package/dist/components/y-tooltip.js +1 -1
- package/dist/icons/all.d.ts +1 -0
- package/dist/icons/all.js +119 -0
- package/dist/icons/registry.d.ts +3 -0
- package/dist/icons/registry.js +30 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.iife.d.ts +1 -0
- package/dist/index.js +360 -21
- package/dist/styles/variables.css +57 -3
- package/dist/yumekit.min.js +1 -1
- package/package.json +3 -1
|
@@ -762,9 +762,30 @@ class YumeMenu extends HTMLElement {
|
|
|
762
762
|
left = anchorRect.left - menuRect.width;
|
|
763
763
|
}
|
|
764
764
|
if (top + menuRect.height > vh) {
|
|
765
|
-
top =
|
|
765
|
+
top = anchorRect.top - menuRect.height;
|
|
766
|
+
}
|
|
767
|
+
} else if (this.direction === "up") {
|
|
768
|
+
top = anchorRect.top - menuRect.height;
|
|
769
|
+
left = anchorRect.left;
|
|
770
|
+
|
|
771
|
+
if (top < 0) {
|
|
772
|
+
top = anchorRect.bottom;
|
|
773
|
+
}
|
|
774
|
+
if (left + menuRect.width > vw) {
|
|
775
|
+
left = vw - menuRect.width - 10;
|
|
776
|
+
}
|
|
777
|
+
} else if (this.direction === "left") {
|
|
778
|
+
top = anchorRect.top;
|
|
779
|
+
left = anchorRect.left - menuRect.width;
|
|
780
|
+
|
|
781
|
+
if (left < 0) {
|
|
782
|
+
left = anchorRect.right;
|
|
783
|
+
}
|
|
784
|
+
if (top + menuRect.height > vh) {
|
|
785
|
+
top = anchorRect.top - menuRect.height;
|
|
766
786
|
}
|
|
767
787
|
} else {
|
|
788
|
+
// "down" (default)
|
|
768
789
|
top = anchorRect.bottom;
|
|
769
790
|
left = anchorRect.left;
|
|
770
791
|
|
|
@@ -776,8 +797,8 @@ class YumeMenu extends HTMLElement {
|
|
|
776
797
|
}
|
|
777
798
|
}
|
|
778
799
|
|
|
779
|
-
top = Math.max(
|
|
780
|
-
left = Math.max(
|
|
800
|
+
top = Math.max(0, Math.min(top, vh - menuRect.height));
|
|
801
|
+
left = Math.max(0, Math.min(left, vw - menuRect.width));
|
|
781
802
|
|
|
782
803
|
this.style.top = `${top}px`;
|
|
783
804
|
this.style.left = `${left}px`;
|
|
@@ -825,7 +846,7 @@ class YumeMenu extends HTMLElement {
|
|
|
825
846
|
top: 0;
|
|
826
847
|
left: 100%;
|
|
827
848
|
display: none;
|
|
828
|
-
z-index: 1001;
|
|
849
|
+
z-index: var(--component-menu-z-index, 1001);
|
|
829
850
|
}
|
|
830
851
|
|
|
831
852
|
li.menuitem:hover > ul.submenu {
|
|
@@ -1024,7 +1045,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1024
1045
|
|
|
1025
1046
|
:host([sticky]) {
|
|
1026
1047
|
position: sticky;
|
|
1027
|
-
z-index: 100;
|
|
1048
|
+
z-index: var(--component-appbar-z-index, 100);
|
|
1028
1049
|
}
|
|
1029
1050
|
:host([orientation="vertical"][sticky="start"]),
|
|
1030
1051
|
:host(:not([orientation])[sticky="start"]) {
|
|
@@ -1249,6 +1270,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1249
1270
|
|
|
1250
1271
|
const bar = document.createElement("div");
|
|
1251
1272
|
bar.className = `appbar ${isVertical ? "vertical" : "horizontal"}`;
|
|
1273
|
+
|
|
1252
1274
|
if (isCollapsed) bar.classList.add("collapsed");
|
|
1253
1275
|
bar.setAttribute("role", "navigation");
|
|
1254
1276
|
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
@@ -1269,6 +1291,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1269
1291
|
|
|
1270
1292
|
const logoWrapper = document.createElement("div");
|
|
1271
1293
|
logoWrapper.className = "logo-wrapper";
|
|
1294
|
+
|
|
1272
1295
|
const logoSlot = document.createElement("slot");
|
|
1273
1296
|
logoSlot.name = "logo";
|
|
1274
1297
|
logoWrapper.appendChild(logoSlot);
|
|
@@ -1276,6 +1299,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1276
1299
|
|
|
1277
1300
|
const titleWrapper = document.createElement("div");
|
|
1278
1301
|
titleWrapper.className = "header-title";
|
|
1302
|
+
|
|
1279
1303
|
const titleSlot = document.createElement("slot");
|
|
1280
1304
|
titleSlot.name = "title";
|
|
1281
1305
|
titleWrapper.appendChild(titleSlot);
|
|
@@ -180,6 +180,16 @@ class YumeCheckbox extends HTMLElement {
|
|
|
180
180
|
line-height: 0;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
:host([disabled]) .checkbox {
|
|
184
|
+
border-color: var(--component-checkbox-border-color);
|
|
185
|
+
background: var(--component-checkbox-disabled-background, var(--component-checkbox-background));
|
|
186
|
+
cursor: not-allowed;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
:host([disabled]) .checkbox:hover {
|
|
190
|
+
border-color: var(--component-checkbox-border-color);
|
|
191
|
+
}
|
|
192
|
+
|
|
183
193
|
.checkbox:hover {
|
|
184
194
|
border-color: var(--component-checkbox-accent);
|
|
185
195
|
}
|
|
@@ -191,7 +201,7 @@ class YumeCheckbox extends HTMLElement {
|
|
|
191
201
|
display: block;
|
|
192
202
|
}
|
|
193
203
|
|
|
194
|
-
|
|
204
|
+
[part="label"] {
|
|
195
205
|
display: inline-flex;
|
|
196
206
|
align-items: center;
|
|
197
207
|
font-size: 0.9em;
|
|
@@ -104,7 +104,7 @@ class YumeDialog extends HTMLElement {
|
|
|
104
104
|
align-items: center;
|
|
105
105
|
justify-content: center;
|
|
106
106
|
background: rgba(0,0,0,0.5);
|
|
107
|
-
z-index: 1000;
|
|
107
|
+
z-index: var(--component-dialog-z-index, 1000);
|
|
108
108
|
}
|
|
109
109
|
:host([visible]) { display: flex; }
|
|
110
110
|
.dialog {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class YumeIcon extends HTMLElement {
|
|
2
|
+
static get observedAttributes(): string[];
|
|
3
|
+
connectedCallback(): void;
|
|
4
|
+
attributeChangedCallback(name: any, oldVal: any, newVal: any): void;
|
|
5
|
+
set name(val: string);
|
|
6
|
+
get name(): string;
|
|
7
|
+
set size(val: string);
|
|
8
|
+
get size(): string;
|
|
9
|
+
set color(val: string);
|
|
10
|
+
get color(): string;
|
|
11
|
+
set label(val: string);
|
|
12
|
+
get label(): string;
|
|
13
|
+
_getColor(color: any): any;
|
|
14
|
+
_getSize(size: any): any;
|
|
15
|
+
render(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
3
|
+
*
|
|
4
|
+
* Register only the icons you need for tree-shaking:
|
|
5
|
+
*
|
|
6
|
+
* import { registerIcon } from "@studious-creative/yumekit";
|
|
7
|
+
* registerIcon("home", homeSvgString);
|
|
8
|
+
*
|
|
9
|
+
* Or register all bundled icons at once (separate import):
|
|
10
|
+
*
|
|
11
|
+
* import "@studious-creative/yumekit/icons/all.js";
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const icons = new Map();
|
|
15
|
+
|
|
16
|
+
function getIcon(name) {
|
|
17
|
+
return icons.get(name) || "";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Allowlist-based SVG sanitizer — only known-safe elements and attributes are kept.
|
|
21
|
+
const ALLOWED_ELEMENTS = new Set([
|
|
22
|
+
"svg",
|
|
23
|
+
"g",
|
|
24
|
+
"path",
|
|
25
|
+
"circle",
|
|
26
|
+
"ellipse",
|
|
27
|
+
"rect",
|
|
28
|
+
"line",
|
|
29
|
+
"polyline",
|
|
30
|
+
"polygon",
|
|
31
|
+
"text",
|
|
32
|
+
"tspan",
|
|
33
|
+
"defs",
|
|
34
|
+
"clippath",
|
|
35
|
+
"mask",
|
|
36
|
+
"lineargradient",
|
|
37
|
+
"radialgradient",
|
|
38
|
+
"stop",
|
|
39
|
+
"symbol",
|
|
40
|
+
"title",
|
|
41
|
+
"desc",
|
|
42
|
+
"metadata",
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const ALLOWED_ATTRS = new Set([
|
|
46
|
+
"viewbox",
|
|
47
|
+
"xmlns",
|
|
48
|
+
"fill",
|
|
49
|
+
"stroke",
|
|
50
|
+
"stroke-width",
|
|
51
|
+
"stroke-linecap",
|
|
52
|
+
"stroke-linejoin",
|
|
53
|
+
"stroke-dasharray",
|
|
54
|
+
"stroke-dashoffset",
|
|
55
|
+
"stroke-miterlimit",
|
|
56
|
+
"stroke-opacity",
|
|
57
|
+
"fill-opacity",
|
|
58
|
+
"fill-rule",
|
|
59
|
+
"clip-rule",
|
|
60
|
+
"opacity",
|
|
61
|
+
"d",
|
|
62
|
+
"cx",
|
|
63
|
+
"cy",
|
|
64
|
+
"r",
|
|
65
|
+
"rx",
|
|
66
|
+
"ry",
|
|
67
|
+
"x",
|
|
68
|
+
"x1",
|
|
69
|
+
"x2",
|
|
70
|
+
"y",
|
|
71
|
+
"y1",
|
|
72
|
+
"y2",
|
|
73
|
+
"width",
|
|
74
|
+
"height",
|
|
75
|
+
"points",
|
|
76
|
+
"transform",
|
|
77
|
+
"id",
|
|
78
|
+
"class",
|
|
79
|
+
"clip-path",
|
|
80
|
+
"mask",
|
|
81
|
+
"offset",
|
|
82
|
+
"stop-color",
|
|
83
|
+
"stop-opacity",
|
|
84
|
+
"gradient-units",
|
|
85
|
+
"gradienttransform",
|
|
86
|
+
"gradientunits",
|
|
87
|
+
"spreadmethod",
|
|
88
|
+
"patternunits",
|
|
89
|
+
"patterntransform",
|
|
90
|
+
"font-size",
|
|
91
|
+
"font-family",
|
|
92
|
+
"font-weight",
|
|
93
|
+
"text-anchor",
|
|
94
|
+
"dominant-baseline",
|
|
95
|
+
"alignment-baseline",
|
|
96
|
+
"dx",
|
|
97
|
+
"dy",
|
|
98
|
+
"rotate",
|
|
99
|
+
"textlength",
|
|
100
|
+
"lengthadjust",
|
|
101
|
+
"display",
|
|
102
|
+
"visibility",
|
|
103
|
+
"color",
|
|
104
|
+
"vector-effect",
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
function sanitizeSvg(raw) {
|
|
108
|
+
if (!raw) return "";
|
|
109
|
+
const doc = new DOMParser().parseFromString(raw, "image/svg+xml");
|
|
110
|
+
const svg = doc.querySelector("svg");
|
|
111
|
+
if (!svg) return "";
|
|
112
|
+
|
|
113
|
+
const walk = (el) => {
|
|
114
|
+
for (const child of [...el.children]) {
|
|
115
|
+
if (!ALLOWED_ELEMENTS.has(child.tagName.toLowerCase())) {
|
|
116
|
+
child.remove();
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (const attr of [...child.attributes]) {
|
|
120
|
+
if (!ALLOWED_ATTRS.has(attr.name.toLowerCase())) {
|
|
121
|
+
child.removeAttribute(attr.name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
walk(child);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Sanitize the <svg> element's own attributes
|
|
129
|
+
for (const attr of [...svg.attributes]) {
|
|
130
|
+
if (!ALLOWED_ATTRS.has(attr.name.toLowerCase())) {
|
|
131
|
+
svg.removeAttribute(attr.name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
walk(svg);
|
|
135
|
+
return svg.outerHTML;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Cache sanitized SVG markup per icon name to avoid repeated DOMParser + DOM-walk
|
|
139
|
+
// on every render. The cache is naturally bounded by the number of registered icons.
|
|
140
|
+
const sanitizedSvgCache = new Map();
|
|
141
|
+
|
|
142
|
+
function getCachedSvg(name) {
|
|
143
|
+
if (sanitizedSvgCache.has(name)) {
|
|
144
|
+
return sanitizedSvgCache.get(name);
|
|
145
|
+
}
|
|
146
|
+
const result = sanitizeSvg(getIcon(name));
|
|
147
|
+
sanitizedSvgCache.set(name, result);
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class YumeIcon extends HTMLElement {
|
|
152
|
+
static get observedAttributes() {
|
|
153
|
+
return ["name", "size", "color", "label"];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
constructor() {
|
|
157
|
+
super();
|
|
158
|
+
this.attachShadow({ mode: "open" });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
connectedCallback() {
|
|
162
|
+
this.render();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
166
|
+
if (oldVal === newVal) return;
|
|
167
|
+
this.render();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get name() {
|
|
171
|
+
return this.getAttribute("name") || "";
|
|
172
|
+
}
|
|
173
|
+
set name(val) {
|
|
174
|
+
this.setAttribute("name", val);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get size() {
|
|
178
|
+
return this.getAttribute("size") || "medium";
|
|
179
|
+
}
|
|
180
|
+
set size(val) {
|
|
181
|
+
this.setAttribute("size", val);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
get color() {
|
|
185
|
+
return this.getAttribute("color") || "base";
|
|
186
|
+
}
|
|
187
|
+
set color(val) {
|
|
188
|
+
this.setAttribute("color", val);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
get label() {
|
|
192
|
+
return this.getAttribute("label") || "";
|
|
193
|
+
}
|
|
194
|
+
set label(val) {
|
|
195
|
+
if (val) this.setAttribute("label", val);
|
|
196
|
+
else this.removeAttribute("label");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_getColor(color) {
|
|
200
|
+
const map = {
|
|
201
|
+
base: "var(--base-content--, #f7f7fa)",
|
|
202
|
+
primary: "var(--primary-content--, #0576ff)",
|
|
203
|
+
secondary: "var(--secondary-content--, #04b8b8)",
|
|
204
|
+
success: "var(--success-content--, #2dba73)",
|
|
205
|
+
warning: "var(--warning-content--, #d17f04)",
|
|
206
|
+
error: "var(--error-content--, #b80421)",
|
|
207
|
+
help: "var(--help-content--, #5405ff)",
|
|
208
|
+
};
|
|
209
|
+
return map[color] || map.base;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
_getSize(size) {
|
|
213
|
+
const map = {
|
|
214
|
+
small: "var(--component-icon-size-small, 16px)",
|
|
215
|
+
medium: "var(--component-icon-size-medium, 24px)",
|
|
216
|
+
large: "var(--component-icon-size-large, 32px)",
|
|
217
|
+
};
|
|
218
|
+
return map[size] || map.medium;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
render() {
|
|
222
|
+
const svg = getCachedSvg(this.name);
|
|
223
|
+
const sizeVal = this._getSize(this.size);
|
|
224
|
+
const colorVal = this._getColor(this.color);
|
|
225
|
+
const label = this.label;
|
|
226
|
+
|
|
227
|
+
if (label) {
|
|
228
|
+
this.setAttribute("role", "img");
|
|
229
|
+
this.setAttribute("aria-label", label);
|
|
230
|
+
this.removeAttribute("aria-hidden");
|
|
231
|
+
} else {
|
|
232
|
+
this.setAttribute("aria-hidden", "true");
|
|
233
|
+
this.removeAttribute("role");
|
|
234
|
+
this.removeAttribute("aria-label");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.shadowRoot.innerHTML = `
|
|
238
|
+
<style>
|
|
239
|
+
:host {
|
|
240
|
+
display: inline-flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
width: ${sizeVal};
|
|
244
|
+
height: ${sizeVal};
|
|
245
|
+
color: ${colorVal};
|
|
246
|
+
line-height: 0;
|
|
247
|
+
}
|
|
248
|
+
.icon-wrapper svg {
|
|
249
|
+
width: 100%;
|
|
250
|
+
height: 100%;
|
|
251
|
+
}
|
|
252
|
+
</style>
|
|
253
|
+
<span class="icon-wrapper" part="icon">${svg}</span>
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!customElements.get("y-icon")) {
|
|
259
|
+
customElements.define("y-icon", YumeIcon);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export { YumeIcon };
|
|
@@ -206,9 +206,30 @@ class YumeMenu extends HTMLElement {
|
|
|
206
206
|
left = anchorRect.left - menuRect.width;
|
|
207
207
|
}
|
|
208
208
|
if (top + menuRect.height > vh) {
|
|
209
|
-
top =
|
|
209
|
+
top = anchorRect.top - menuRect.height;
|
|
210
|
+
}
|
|
211
|
+
} else if (this.direction === "up") {
|
|
212
|
+
top = anchorRect.top - menuRect.height;
|
|
213
|
+
left = anchorRect.left;
|
|
214
|
+
|
|
215
|
+
if (top < 0) {
|
|
216
|
+
top = anchorRect.bottom;
|
|
217
|
+
}
|
|
218
|
+
if (left + menuRect.width > vw) {
|
|
219
|
+
left = vw - menuRect.width - 10;
|
|
220
|
+
}
|
|
221
|
+
} else if (this.direction === "left") {
|
|
222
|
+
top = anchorRect.top;
|
|
223
|
+
left = anchorRect.left - menuRect.width;
|
|
224
|
+
|
|
225
|
+
if (left < 0) {
|
|
226
|
+
left = anchorRect.right;
|
|
227
|
+
}
|
|
228
|
+
if (top + menuRect.height > vh) {
|
|
229
|
+
top = anchorRect.top - menuRect.height;
|
|
210
230
|
}
|
|
211
231
|
} else {
|
|
232
|
+
// "down" (default)
|
|
212
233
|
top = anchorRect.bottom;
|
|
213
234
|
left = anchorRect.left;
|
|
214
235
|
|
|
@@ -220,8 +241,8 @@ class YumeMenu extends HTMLElement {
|
|
|
220
241
|
}
|
|
221
242
|
}
|
|
222
243
|
|
|
223
|
-
top = Math.max(
|
|
224
|
-
left = Math.max(
|
|
244
|
+
top = Math.max(0, Math.min(top, vh - menuRect.height));
|
|
245
|
+
left = Math.max(0, Math.min(left, vw - menuRect.width));
|
|
225
246
|
|
|
226
247
|
this.style.top = `${top}px`;
|
|
227
248
|
this.style.left = `${left}px`;
|
|
@@ -269,7 +290,7 @@ class YumeMenu extends HTMLElement {
|
|
|
269
290
|
top: 0;
|
|
270
291
|
left: 100%;
|
|
271
292
|
display: none;
|
|
272
|
-
z-index: 1001;
|
|
293
|
+
z-index: var(--component-menu-z-index, 1001);
|
|
273
294
|
}
|
|
274
295
|
|
|
275
296
|
li.menuitem:hover > ul.submenu {
|
|
@@ -81,7 +81,12 @@ class YumePanel extends HTMLElement {
|
|
|
81
81
|
if (!this._expanded) {
|
|
82
82
|
const parentBar = this.closest("y-panelbar");
|
|
83
83
|
if (parentBar && parentBar.hasAttribute("exclusive")) {
|
|
84
|
-
const
|
|
84
|
+
const parent = this.parentElement;
|
|
85
|
+
const siblingPanels = parent
|
|
86
|
+
? Array.from(parent.children).filter(
|
|
87
|
+
(el) => el.tagName === "Y-PANEL",
|
|
88
|
+
)
|
|
89
|
+
: [];
|
|
85
90
|
siblingPanels.forEach((panel) => {
|
|
86
91
|
if (panel !== this && panel.expanded) {
|
|
87
92
|
panel.collapse();
|
|
@@ -133,9 +138,19 @@ class YumePanel extends HTMLElement {
|
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
updateChildState() {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
let depth = 0;
|
|
142
|
+
let el = this.parentElement;
|
|
143
|
+
while (el) {
|
|
144
|
+
const parent = el.closest("y-panel");
|
|
145
|
+
if (parent && parent !== this) {
|
|
146
|
+
depth++;
|
|
147
|
+
el = parent.parentElement;
|
|
148
|
+
} else {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
this.setAttribute("data-is-child", depth > 0 ? "true" : "false");
|
|
153
|
+
this.style.setProperty("--panel-depth", depth);
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
checkRouteMatch() {
|
|
@@ -151,7 +166,8 @@ class YumePanel extends HTMLElement {
|
|
|
151
166
|
const header = this.shadowRoot.querySelector(".header");
|
|
152
167
|
if (!header) return;
|
|
153
168
|
|
|
154
|
-
header.addEventListener("click", () => {
|
|
169
|
+
header.addEventListener("click", (e) => {
|
|
170
|
+
e.stopPropagation();
|
|
155
171
|
if (this.hasAttribute("href")) {
|
|
156
172
|
const href = this.getAttribute("href");
|
|
157
173
|
if (this.getAttribute("history") !== "false") {
|
|
@@ -261,7 +277,7 @@ class YumePanel extends HTMLElement {
|
|
|
261
277
|
}
|
|
262
278
|
|
|
263
279
|
:host([data-is-child="true"]) .header {
|
|
264
|
-
padding-left: calc(var(--component-panelbar-padding, 4px) *
|
|
280
|
+
padding-left: calc(var(--component-panelbar-padding, 4px) + (var(--panel-depth, 1) * var(--component-panelbar-indent, 16px)));
|
|
265
281
|
}
|
|
266
282
|
|
|
267
283
|
.header {
|
|
@@ -3,7 +3,9 @@ export class YumeSelect extends HTMLElement {
|
|
|
3
3
|
static get observedAttributes(): string[];
|
|
4
4
|
_internals: ElementInternals;
|
|
5
5
|
selectedValues: Set<any>;
|
|
6
|
+
_onDocumentClick(e: any): void;
|
|
6
7
|
connectedCallback(): void;
|
|
8
|
+
disconnectedCallback(): void;
|
|
7
9
|
attributeChangedCallback(name: any, oldValue: any, newValue: any): void;
|
|
8
10
|
set value(val: any);
|
|
9
11
|
get value(): any;
|
|
@@ -24,6 +24,7 @@ class YumeSelect extends HTMLElement {
|
|
|
24
24
|
"placeholder",
|
|
25
25
|
"options",
|
|
26
26
|
"display-mode",
|
|
27
|
+
"close-on-click-outside",
|
|
27
28
|
];
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -32,6 +33,7 @@ class YumeSelect extends HTMLElement {
|
|
|
32
33
|
this._internals = this.attachInternals();
|
|
33
34
|
this.attachShadow({ mode: "open" });
|
|
34
35
|
this.selectedValues = new Set();
|
|
36
|
+
this._onDocumentClick = this._onDocumentClick.bind(this);
|
|
35
37
|
this.render();
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -43,6 +45,18 @@ class YumeSelect extends HTMLElement {
|
|
|
43
45
|
this._internals.setFormValue(this.value);
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
disconnectedCallback() {
|
|
49
|
+
this.closeDropdown();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_onDocumentClick(e) {
|
|
53
|
+
if (this.getAttribute("close-on-click-outside") === "false") return;
|
|
54
|
+
const path = e.composedPath();
|
|
55
|
+
if (!path.includes(this) && this.dropdown?.classList.contains("open")) {
|
|
56
|
+
this.closeDropdown();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
47
61
|
if (oldValue === newValue) return;
|
|
48
62
|
|
|
@@ -141,15 +155,18 @@ class YumeSelect extends HTMLElement {
|
|
|
141
155
|
this._onScrollOrResize = this._positionDropdown.bind(this);
|
|
142
156
|
window.addEventListener("scroll", this._onScrollOrResize, true);
|
|
143
157
|
window.addEventListener("resize", this._onScrollOrResize);
|
|
158
|
+
document.addEventListener("click", this._onDocumentClick, true);
|
|
144
159
|
}
|
|
145
160
|
}
|
|
146
161
|
|
|
147
162
|
closeDropdown() {
|
|
148
|
-
this.dropdown
|
|
149
|
-
this.selectContainer
|
|
163
|
+
this.dropdown?.classList.remove("open");
|
|
164
|
+
this.selectContainer?.classList.remove("open");
|
|
165
|
+
document.removeEventListener("click", this._onDocumentClick, true);
|
|
150
166
|
if (this._onScrollOrResize) {
|
|
151
167
|
window.removeEventListener("scroll", this._onScrollOrResize, true);
|
|
152
168
|
window.removeEventListener("resize", this._onScrollOrResize);
|
|
169
|
+
this._onScrollOrResize = null;
|
|
153
170
|
}
|
|
154
171
|
}
|
|
155
172
|
|
|
@@ -309,6 +326,7 @@ class YumeSelect extends HTMLElement {
|
|
|
309
326
|
}
|
|
310
327
|
|
|
311
328
|
render() {
|
|
329
|
+
this.closeDropdown();
|
|
312
330
|
this.applyStyles();
|
|
313
331
|
this.shadowRoot.innerHTML = this.generateTemplate();
|
|
314
332
|
this.queryRefs();
|
|
@@ -382,7 +400,7 @@ class YumeSelect extends HTMLElement {
|
|
|
382
400
|
|
|
383
401
|
.dropdown {
|
|
384
402
|
position: fixed;
|
|
385
|
-
z-index:
|
|
403
|
+
z-index: var(--component-select-z-index, 6000);
|
|
386
404
|
background: var(--component-select-background);
|
|
387
405
|
border: var(--component-inputs-border-width) solid var(--component-select-border-color);
|
|
388
406
|
border-radius: var(--component-inputs-border-radius-outer);
|