@studious-creative/yumekit 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.
@@ -0,0 +1,1378 @@
1
+ class YumeButton extends HTMLElement {
2
+ static get observedAttributes() {
3
+ return [
4
+ "left-icon",
5
+ "right-icon",
6
+ "color",
7
+ "size",
8
+ "style-type",
9
+ "type",
10
+ "disabled",
11
+ "name",
12
+ "value",
13
+ "autofocus",
14
+ "form",
15
+ "formaction",
16
+ "formenctype",
17
+ "formmethod",
18
+ "formnovalidate",
19
+ "formtarget",
20
+ "aria-label",
21
+ "aria-pressed",
22
+ "aria-hidden",
23
+ ];
24
+ }
25
+
26
+ constructor() {
27
+ super();
28
+ this.attachShadow({ mode: "open" });
29
+ this.init();
30
+ }
31
+
32
+ connectedCallback() {
33
+ if (!this.hasAttribute("size")) {
34
+ this.setAttribute("size", "medium");
35
+ }
36
+
37
+ this.init();
38
+ }
39
+
40
+ attributeChangedCallback(name, oldValue, newValue) {
41
+ const attributes = YumeButton.observedAttributes;
42
+
43
+ if (oldValue !== newValue && attributes.includes(name)) {
44
+ if (newValue === null) {
45
+ this.button.removeAttribute(name);
46
+ } else {
47
+ this.button.setAttribute(name, newValue);
48
+ }
49
+ }
50
+
51
+ this.init();
52
+
53
+ if (["color", "size", "style-type", "disabled"].includes(name)) {
54
+ this.updateStyles();
55
+ }
56
+ }
57
+
58
+ get value() {
59
+ if (this.hasAttribute("multiple")) {
60
+ return Array.from(this.selectedValues).join(",");
61
+ } else {
62
+ return this.selectedValues.size
63
+ ? Array.from(this.selectedValues)[0]
64
+ : "";
65
+ }
66
+ }
67
+
68
+ set value(newVal) {
69
+ if (this.hasAttribute("multiple")) {
70
+ if (typeof newVal === "string") {
71
+ this.selectedValues = new Set(
72
+ newVal.split(",").map((s) => s.trim()),
73
+ );
74
+ } else if (Array.isArray(newVal)) {
75
+ this.selectedValues = new Set(newVal);
76
+ }
77
+ } else {
78
+ if (typeof newVal === "string") {
79
+ this.selectedValues = new Set([newVal.trim()]);
80
+ } else {
81
+ this.selectedValues = new Set();
82
+ }
83
+ }
84
+
85
+ this.setAttribute("value", newVal);
86
+ }
87
+
88
+ setOptions(options) {
89
+ this.setAttribute("options", JSON.stringify(options));
90
+ }
91
+
92
+ handleClick() {
93
+ const detail = {};
94
+ const eventType = this.getAttribute("data-event");
95
+
96
+ if (this.hasAttribute("disabled") || !eventType) return;
97
+
98
+ Array.from(this.attributes)
99
+ .filter((attr) => attr.name.startsWith("data-detail-"))
100
+ .forEach((attr) => {
101
+ const key = attr.name.replace("data-detail-", "");
102
+ detail[key] = attr.value;
103
+ });
104
+
105
+ this.dispatchEvent(
106
+ new CustomEvent(eventType, {
107
+ detail,
108
+ bubbles: true,
109
+ composed: true,
110
+ }),
111
+ );
112
+ }
113
+
114
+ init() {
115
+ this.applyStyles();
116
+ this.render();
117
+ this.proxyNativeOnClick();
118
+ this.addEventListeners();
119
+ }
120
+
121
+ proxyNativeOnClick() {
122
+ try {
123
+ Object.defineProperty(this, "onclick", {
124
+ get: () => this.button.onclick,
125
+ set: (value) => {
126
+ this.button.onclick = value;
127
+ },
128
+ configurable: true,
129
+ enumerable: true,
130
+ });
131
+ } catch (e) {
132
+ console.warn("Could not redefine onclick:", e);
133
+ }
134
+ }
135
+
136
+ updateButtonAttributes() {
137
+ const attributes = YumeButton.observedAttributes;
138
+
139
+ attributes.forEach((attr) => {
140
+ if (this.hasAttribute(attr)) {
141
+ this.button.setAttribute(attr, this.getAttribute(attr));
142
+ } else {
143
+ this.button.removeAttribute(attr);
144
+ }
145
+ });
146
+ }
147
+
148
+ manageSlotVisibility(slotName, selector) {
149
+ const slot = slotName
150
+ ? this.shadowRoot.querySelector(`slot[name="${slotName}"]`)
151
+ : this.shadowRoot.querySelector("slot:not([name])");
152
+ const container = this.shadowRoot.querySelector(selector);
153
+
154
+ const updateVisibility = () => {
155
+ const hasContent = slot
156
+ .assignedNodes({ flatten: true })
157
+ .some(
158
+ (n) =>
159
+ !(
160
+ n.nodeType === Node.TEXT_NODE &&
161
+ n.textContent.trim() === ""
162
+ ),
163
+ );
164
+ container.style.display = hasContent ? "inline-flex" : "none";
165
+ };
166
+
167
+ updateVisibility();
168
+ slot.addEventListener("slotchange", updateVisibility);
169
+ }
170
+
171
+ render() {
172
+ if (!this.button) {
173
+ this.button = document.createElement("button");
174
+ this.button.classList.add("button");
175
+ this.button.setAttribute("role", "button");
176
+ this.button.setAttribute("tabindex", "0");
177
+ this.button.setAttribute("part", "button");
178
+ this.shadowRoot.appendChild(this.button);
179
+ }
180
+
181
+ this.updateButtonAttributes();
182
+
183
+ if (this.hasAttribute("disabled")) {
184
+ this.button.setAttribute("disabled", "");
185
+ this.button.setAttribute("aria-disabled", "true");
186
+ } else {
187
+ this.button.removeAttribute("disabled");
188
+ this.button.setAttribute("aria-disabled", "false");
189
+ }
190
+
191
+ this.button.innerHTML = `
192
+ <span class="icon left-icon" part="left-icon"><slot name="left-icon"></slot></span>
193
+ <span class="label" part="label"><slot></slot></span>
194
+ <span class="icon right-icon" part="right-icon"><slot name="right-icon"></slot></span>
195
+ `;
196
+
197
+ this.manageSlotVisibility("left-icon", ".left-icon");
198
+ this.manageSlotVisibility("right-icon", ".right-icon");
199
+ this.manageSlotVisibility("", ".label");
200
+ }
201
+
202
+ applyStyles() {
203
+ const style = document.createElement("style");
204
+ style.textContent = `
205
+ :host {
206
+ display: inline-block;
207
+ }
208
+
209
+ @font-face {
210
+ font-family: "Lexend";
211
+ font-display: swap;
212
+ }
213
+
214
+ .button {
215
+ box-sizing: border-box;
216
+ display: inline-flex;
217
+ min-height: var(--button-min-height, var(--sizing-medium, 40px));
218
+ min-width: var(--button-min-width, var(--sizing-medium, 40px));
219
+ padding: var(--button-padding, var(--component-button-padding-medium));
220
+ gap: var(--button-gap, var(--component-button-padding-medium));
221
+ justify-content: center;
222
+ align-items: center;
223
+ position: relative;
224
+ overflow: hidden;
225
+ border-radius: var(--component-button-border-radius-outer, 4px);
226
+ border: var(--component-button-border-width, 1px) solid var(--border-color, var(--base-content--, #1D1D1D));
227
+ background: var(--background-color, #F1F6FA);
228
+ transition: background-color 0.1s, color 0.1s, border-color 0.1s;
229
+ cursor: pointer;
230
+ color: var(--text-color);
231
+ font-family: var(--font-family-body, Lexend, sans-serif);
232
+ font-size: var(--font-size-button, 1em);
233
+ line-height: 1;
234
+ }
235
+
236
+ .button:disabled {
237
+ opacity: 0.5;
238
+ cursor: not-allowed;
239
+ }
240
+
241
+ .button:hover:not(:disabled),
242
+ .button:hover:not(:disabled) .button-content {
243
+ background: var(--hover-background-color);
244
+ color: var(--hover-text-color);
245
+ border-color: var(--hover-border-color);
246
+ }
247
+ .button:focus:not(:disabled),
248
+ .button:focus:not(:disabled) .button-content {
249
+ background: var(--focus-background-color);
250
+ color: var(--focus-text-color);
251
+ border-color: var(--focus-border-color);
252
+ }
253
+ .button:active:not(:disabled),
254
+ .button:active:not(:disabled) .button-content {
255
+ background: var(--active-background-color);
256
+ color: var(--active-text-color);
257
+ border-color: var(--active-border-color);
258
+ }
259
+ .icon {
260
+ display: flex;
261
+ min-width: 16px;
262
+ min-height: 1em;
263
+ justify-content: center;
264
+ align-items: center;
265
+ }
266
+ .label {
267
+ line-height: inherit;
268
+ min-height: 1em;
269
+ align-items: center;
270
+ }
271
+ `;
272
+ this.shadowRoot.appendChild(style);
273
+ }
274
+
275
+ addEventListeners() {
276
+ this.button.addEventListener("focus", () => {
277
+ this.dispatchEvent(
278
+ new CustomEvent("focus", { bubbles: true, composed: true }),
279
+ );
280
+ });
281
+
282
+ this.button.addEventListener("blur", () => {
283
+ this.dispatchEvent(
284
+ new CustomEvent("blur", { bubbles: true, composed: true }),
285
+ );
286
+ });
287
+
288
+ this.button.addEventListener("keydown", (event) => {
289
+ this.dispatchEvent(
290
+ new CustomEvent("keydown", {
291
+ detail: { key: event.key, code: event.code },
292
+ bubbles: true,
293
+ composed: true,
294
+ }),
295
+ );
296
+ });
297
+
298
+ this.button.addEventListener("keyup", (event) => {
299
+ this.dispatchEvent(
300
+ new CustomEvent("keyup", {
301
+ detail: { key: event.key, code: event.code },
302
+ bubbles: true,
303
+ composed: true,
304
+ }),
305
+ );
306
+ });
307
+
308
+ this.button.addEventListener("click", (event) => {
309
+ this.handleClick();
310
+
311
+ if (this.getAttribute("type") === "submit") {
312
+ const form = this.closest("form");
313
+ if (form) {
314
+ event.preventDefault();
315
+ form.requestSubmit();
316
+ }
317
+ }
318
+ });
319
+ }
320
+
321
+ updateStyles() {
322
+ const color = this.getAttribute("color") || "base";
323
+ const size = this.getAttribute("size") || "medium";
324
+ const styleType = this.getAttribute("style-type") || "outlined";
325
+
326
+ const colorVars = {
327
+ primary: [
328
+ "--primary-content--",
329
+ "--primary-content-hover",
330
+ "--primary-content-active",
331
+ "--primary-background-component",
332
+ "--primary-background-hover",
333
+ "--primary-background-active",
334
+ ],
335
+ secondary: [
336
+ "--secondary-content--",
337
+ "--secondary-content-hover",
338
+ "--secondary-content-active",
339
+ "--secondary-background-component",
340
+ "--secondary-background-hover",
341
+ "--secondary-background-active",
342
+ ],
343
+ base: [
344
+ "--base-content--",
345
+ "--base-content-lighter",
346
+ "--base-content-lightest",
347
+ "--base-background-component",
348
+ "--base-background-hover",
349
+ "--base-background-active",
350
+ ],
351
+ success: [
352
+ "--success-content--",
353
+ "--success-content-hover",
354
+ "--success-content-active",
355
+ "--success-background-component",
356
+ "--success-background-hover",
357
+ "--success-background-active",
358
+ ],
359
+ error: [
360
+ "--error-content--",
361
+ "--error-content-hover",
362
+ "--error-content-active",
363
+ "--error-background-component",
364
+ "--error-background-hover",
365
+ "--error-background-active",
366
+ ],
367
+ warning: [
368
+ "--warning-content--",
369
+ "--warning-content-hover",
370
+ "--warning-content-active",
371
+ "--warning-background-component",
372
+ "--warning-background-hover",
373
+ "--warning-background-active",
374
+ ],
375
+ help: [
376
+ "--help-content--",
377
+ "--help-content-hover",
378
+ "--help-content-active",
379
+ "--help-background-component",
380
+ "--help-background-hover",
381
+ "--help-background-active",
382
+ ],
383
+ };
384
+
385
+ const sizeVars = {
386
+ small: [
387
+ "--component-button-padding-small",
388
+ "--component-button-padding-small",
389
+ ],
390
+ medium: [
391
+ "--component-button-padding-medium",
392
+ "--component-button-padding-medium",
393
+ ],
394
+ large: [
395
+ "--component-button-padding-large",
396
+ "--component-button-padding-large",
397
+ ],
398
+ };
399
+
400
+ const styleVars = {
401
+ outlined: {
402
+ "--background-color": `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
403
+ "--border-color": `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
404
+ "--text-color": `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
405
+ },
406
+ filled: {
407
+ "--background-color": `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
408
+ "--border-color": `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
409
+ "--text-color": `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
410
+ },
411
+ flat: {
412
+ "--background-color": `var(${colorVars[color][3]},rgba(241,246,250,1))`,
413
+ "--border-color": `var(${colorVars[color][3]},rgba(241,246,250,1))`,
414
+ "--text-color": `var(${colorVars[color][0]},rgba(29,29,29,1))`,
415
+ },
416
+ };
417
+
418
+ const currentStyle = styleVars[styleType] || styleVars.outlined;
419
+ Object.entries(currentStyle).forEach(([key, value]) => {
420
+ this.button.style.setProperty(key, value);
421
+ });
422
+
423
+ if (styleType === "filled") {
424
+ this.button.style.setProperty(
425
+ "--hover-background-color",
426
+ `var(${colorVars[color][1]}, rgba(215,219,222,1))`,
427
+ );
428
+ this.button.style.setProperty(
429
+ "--hover-text-color",
430
+ `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
431
+ );
432
+ this.button.style.setProperty(
433
+ "--hover-border-color",
434
+ `var(${colorVars[color][1]}, rgba(215,219,222,1))`,
435
+ );
436
+ this.button.style.setProperty(
437
+ "--focus-background-color",
438
+ `var(${colorVars[color][2]}, rgba(188,192,195,1))`,
439
+ );
440
+ this.button.style.setProperty(
441
+ "--focus-text-color",
442
+ `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
443
+ );
444
+ this.button.style.setProperty(
445
+ "--focus-border-color",
446
+ `var(${colorVars[color][2]}, rgba(188,192,195,1))`,
447
+ );
448
+ this.button.style.setProperty(
449
+ "--active-background-color",
450
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
451
+ );
452
+ this.button.style.setProperty(
453
+ "--active-text-color",
454
+ `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
455
+ );
456
+ this.button.style.setProperty(
457
+ "--active-border-color",
458
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
459
+ );
460
+ } else {
461
+ const borderColor = `var(${colorVars[color][0]}, rgba(29,29,29,1))`;
462
+
463
+ this.button.style.setProperty(
464
+ "--hover-background-color",
465
+ `var(${colorVars[color][4]}, rgba(215,219,222,1))`,
466
+ );
467
+ this.button.style.setProperty(
468
+ "--hover-text-color",
469
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
470
+ );
471
+ this.button.style.setProperty(
472
+ "--focus-background-color",
473
+ `var(${colorVars[color][5]}, rgba(188,192,195,1))`,
474
+ );
475
+ this.button.style.setProperty(
476
+ "--focus-text-color",
477
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
478
+ );
479
+ this.button.style.setProperty(
480
+ "--active-background-color",
481
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
482
+ );
483
+ this.button.style.setProperty(
484
+ "--active-text-color",
485
+ `var(${colorVars[color][3]}, rgba(241,246,250,1))`,
486
+ );
487
+
488
+ if (styleType === "outlined") {
489
+ // Outlined buttons keep their border color across all states
490
+ this.button.style.setProperty(
491
+ "--hover-border-color",
492
+ borderColor,
493
+ );
494
+ this.button.style.setProperty(
495
+ "--focus-border-color",
496
+ borderColor,
497
+ );
498
+ this.button.style.setProperty(
499
+ "--active-border-color",
500
+ borderColor,
501
+ );
502
+ } else {
503
+ // Flat buttons match border to background
504
+ this.button.style.setProperty(
505
+ "--hover-border-color",
506
+ `var(${colorVars[color][4]}, rgba(215,219,222,1))`,
507
+ );
508
+ this.button.style.setProperty(
509
+ "--focus-border-color",
510
+ `var(${colorVars[color][5]}, rgba(188,192,195,1))`,
511
+ );
512
+ this.button.style.setProperty(
513
+ "--active-border-color",
514
+ `var(${colorVars[color][0]}, rgba(29,29,29,1))`,
515
+ );
516
+ }
517
+ }
518
+
519
+ const [contentPadding, buttonPadding] =
520
+ sizeVars[size] || sizeVars.medium;
521
+ this.button.style.setProperty(
522
+ "--button-padding",
523
+ `var(${buttonPadding}, var(--component-button-padding-medium))`,
524
+ );
525
+ this.button.style.setProperty(
526
+ "--button-gap",
527
+ `var(${contentPadding}, var(--component-button-padding-medium))`,
528
+ );
529
+
530
+ const minSizeMapping = {
531
+ small: "var(--sizing-small, 32px)",
532
+ medium: "var(--sizing-medium, 40px)",
533
+ large: "var(--sizing-large, 56px)",
534
+ };
535
+ this.button.style.setProperty(
536
+ "--button-min-height",
537
+ minSizeMapping[size] || "40px",
538
+ );
539
+ this.button.style.setProperty(
540
+ "--button-min-width",
541
+ minSizeMapping[size] || "40px",
542
+ );
543
+ }
544
+ }
545
+
546
+ if (!customElements.get("y-button")) {
547
+ customElements.define("y-button", YumeButton);
548
+ }
549
+
550
+ class YumeMenu extends HTMLElement {
551
+ static get observedAttributes() {
552
+ return ["items", "anchor", "visible", "direction", "size"];
553
+ }
554
+
555
+ constructor() {
556
+ super();
557
+ this.attachShadow({ mode: "open" });
558
+ this._onAnchorClick = this._onAnchorClick.bind(this);
559
+ this._onDocumentClick = this._onDocumentClick.bind(this);
560
+ this._onScrollOrResize = this._onScrollOrResize.bind(this);
561
+ }
562
+
563
+ connectedCallback() {
564
+ if (!this.hasAttribute("items")) this.items = [];
565
+ this._setupAnchor();
566
+ this.render();
567
+ document.addEventListener("click", this._onDocumentClick);
568
+ window.addEventListener("scroll", this._onScrollOrResize, true);
569
+ window.addEventListener("resize", this._onScrollOrResize);
570
+ this.style.position = "fixed";
571
+ this.style.zIndex = "1000";
572
+ this.style.display = "none";
573
+ }
574
+
575
+ disconnectedCallback() {
576
+ this._teardownAnchor();
577
+ document.removeEventListener("click", this._onDocumentClick);
578
+ window.removeEventListener("scroll", this._onScrollOrResize, true);
579
+ window.removeEventListener("resize", this._onScrollOrResize);
580
+ }
581
+
582
+ attributeChangedCallback(name, oldVal, newVal) {
583
+ if (oldVal === newVal) return;
584
+ if (name === "items" || name === "size") this.render();
585
+ if (name === "anchor") {
586
+ this._teardownAnchor();
587
+ this._setupAnchor();
588
+ }
589
+ if (name === "visible") {
590
+ this._updatePosition();
591
+ }
592
+ if (name === "direction") {
593
+ this._updatePosition();
594
+ }
595
+ }
596
+
597
+ get items() {
598
+ try {
599
+ return JSON.parse(this.getAttribute("items")) || [];
600
+ } catch {
601
+ return [];
602
+ }
603
+ }
604
+ set items(val) {
605
+ this.setAttribute("items", JSON.stringify(val));
606
+ }
607
+
608
+ get anchor() {
609
+ return this.getAttribute("anchor");
610
+ }
611
+ set anchor(val) {
612
+ this.setAttribute("anchor", val);
613
+ }
614
+
615
+ get visible() {
616
+ return this.hasAttribute("visible");
617
+ }
618
+ set visible(val) {
619
+ if (val) this.setAttribute("visible", "");
620
+ else this.removeAttribute("visible");
621
+ }
622
+
623
+ get direction() {
624
+ return this.getAttribute("direction") || "down";
625
+ }
626
+ set direction(val) {
627
+ this.setAttribute("direction", val);
628
+ }
629
+
630
+ get size() {
631
+ const sz = this.getAttribute("size");
632
+ return ["small", "medium", "large"].includes(sz) ? sz : "medium";
633
+ }
634
+ set size(val) {
635
+ if (["small", "medium", "large"].includes(val))
636
+ this.setAttribute("size", val);
637
+ else this.setAttribute("size", "medium");
638
+ }
639
+
640
+ _createMenuList(items) {
641
+ const ul = document.createElement("ul");
642
+
643
+ items.forEach((item) => {
644
+ const li = document.createElement("li");
645
+ li.className = "menuitem";
646
+ li.setAttribute("role", "menuitem");
647
+ li.tabIndex = 0;
648
+
649
+ const contentWrapper = document.createElement("span");
650
+ contentWrapper.className = "item-content";
651
+
652
+ if (item["icon-template"]) {
653
+ const iconTpl = this._findTemplate(item["icon-template"]);
654
+ if (iconTpl)
655
+ contentWrapper.appendChild(iconTpl.content.cloneNode(true));
656
+ }
657
+
658
+ if (item.template) {
659
+ const textTpl = this._findTemplate(item.template);
660
+ if (textTpl) {
661
+ contentWrapper.appendChild(textTpl.content.cloneNode(true));
662
+ } else {
663
+ contentWrapper.textContent = item.text;
664
+ }
665
+ } else {
666
+ contentWrapper.textContent = item.text;
667
+ }
668
+
669
+ li.appendChild(contentWrapper);
670
+
671
+ if (item.url) {
672
+ li.addEventListener("click", () => {
673
+ window.location.href = item.url;
674
+ });
675
+ }
676
+
677
+ if (item.children?.length) {
678
+ const indicator = document.createElement("span");
679
+ indicator.className = "submenu-indicator";
680
+ indicator.textContent = "▶";
681
+ li.appendChild(indicator);
682
+
683
+ const submenu = this._createMenuList(item.children);
684
+ submenu.classList.add("submenu");
685
+ submenu.setAttribute("role", "menu");
686
+ li.appendChild(submenu);
687
+ }
688
+
689
+ ul.appendChild(li);
690
+ });
691
+
692
+ return ul;
693
+ }
694
+
695
+ _findTemplate(name) {
696
+ return this.querySelector(`template[slot="${name}"]`);
697
+ }
698
+
699
+ _onAnchorClick(e) {
700
+ e.stopPropagation();
701
+ this.visible = !this.visible;
702
+ }
703
+
704
+ _onDocumentClick(e) {
705
+ const path = e.composedPath();
706
+ if (this._anchorEl && path.includes(this._anchorEl)) return;
707
+ if (path.includes(this)) return;
708
+ this.visible = false;
709
+ }
710
+
711
+ _onScrollOrResize() {
712
+ if (this.visible) this._updatePosition();
713
+ }
714
+
715
+ _setupAnchor() {
716
+ const id = this.anchor;
717
+ if (id) {
718
+ const root = this.getRootNode();
719
+ const el = root?.getElementById
720
+ ? root.getElementById(id)
721
+ : document.getElementById(id);
722
+ if (el) {
723
+ this._anchorEl = el;
724
+ this._anchorEl.addEventListener("click", this._onAnchorClick);
725
+ }
726
+ }
727
+ }
728
+
729
+ _teardownAnchor() {
730
+ if (this._anchorEl) {
731
+ this._anchorEl.removeEventListener("click", this._onAnchorClick);
732
+ this._anchorEl = null;
733
+ }
734
+ }
735
+
736
+ _updatePosition() {
737
+ if (!this.visible || !this._anchorEl) {
738
+ this.style.display = "none";
739
+ return;
740
+ }
741
+
742
+ const anchorRect = this._anchorEl.getBoundingClientRect();
743
+ const menuRect = this.getBoundingClientRect();
744
+ const vw = window.innerWidth;
745
+ const vh = window.innerHeight;
746
+
747
+ let top, left;
748
+
749
+ if (this.direction === "right") {
750
+ top = anchorRect.top;
751
+ left = anchorRect.right;
752
+
753
+ if (left + menuRect.width > vw) {
754
+ left = anchorRect.left - menuRect.width;
755
+ }
756
+ if (top + menuRect.height > vh) {
757
+ top = vh - menuRect.height - 10;
758
+ }
759
+ } else {
760
+ top = anchorRect.bottom;
761
+ left = anchorRect.left;
762
+
763
+ if (top + menuRect.height > vh) {
764
+ top = anchorRect.top - menuRect.height;
765
+ }
766
+ if (left + menuRect.width > vw) {
767
+ left = vw - menuRect.width - 10;
768
+ }
769
+ }
770
+
771
+ top = Math.max(10, Math.min(top, vh - menuRect.height - 10));
772
+ left = Math.max(10, Math.min(left, vw - menuRect.width - 10));
773
+
774
+ this.style.top = `${top}px`;
775
+ this.style.left = `${left}px`;
776
+ this.style.display = "block";
777
+ }
778
+
779
+ render() {
780
+ this.shadowRoot.innerHTML = "";
781
+
782
+ const paddingVar = `var(--component-button-padding-${this.size}, 0.5rem)`;
783
+
784
+ const style = document.createElement("style");
785
+ style.textContent = `
786
+ ul.menu,
787
+ ul.submenu {
788
+ list-style: none;
789
+ margin: 0;
790
+ padding: 0;
791
+ background: var(--component-menu-background, #fff);
792
+ border: var(--component-menu-border-width, 1px) solid var(--component-menu-border-color, #ccc);
793
+ border-radius: var(--component-menu-border-radius, 4px);
794
+ box-shadow: var(--component-menu-shadow, 0 2px 8px rgba(0, 0, 0, 0.15));
795
+ min-width: 150px;
796
+ max-height: 300px;
797
+ overflow-y: auto;
798
+ }
799
+
800
+ li.menuitem {
801
+ cursor: pointer;
802
+ padding: ${paddingVar};
803
+ display: flex;
804
+ align-items: center;
805
+ justify-content: space-between;
806
+ white-space: nowrap;
807
+ color: var(--component-menu-color, #000);
808
+ font-size: var(--font-size-button, 1em);
809
+ }
810
+
811
+ li.menuitem:hover {
812
+ background: var(--component-menu-hover-background, #eee);
813
+ }
814
+
815
+ ul.submenu {
816
+ position: absolute;
817
+ top: 0;
818
+ left: 100%;
819
+ display: none;
820
+ z-index: 1001;
821
+ }
822
+
823
+ li.menuitem:hover > ul.submenu {
824
+ display: block;
825
+ }
826
+
827
+ .submenu-indicator {
828
+ font-size: 0.75em;
829
+ margin-left: 0.5rem;
830
+ opacity: 0.6;
831
+ }
832
+
833
+ .item-content {
834
+ flex: 1;
835
+ }
836
+ `;
837
+ this.shadowRoot.appendChild(style);
838
+
839
+ const rootUl = this._createMenuList(this.items);
840
+ rootUl.classList.add("menu");
841
+ rootUl.setAttribute("role", "menu");
842
+ this.shadowRoot.appendChild(rootUl);
843
+ }
844
+ }
845
+
846
+ if (!customElements.get("y-menu")) {
847
+ customElements.define("y-menu", YumeMenu);
848
+ }
849
+
850
+ /* ================================================================== */
851
+ /* Centralized SVG icon strings for the YumeKit component library. */
852
+ /* */
853
+ /* Each static icon also lives in its own .svg file in this directory */
854
+ /* so it can be used standalone (e.g. <img src="…">, CSS background, */
855
+ /* design tools, etc.). The strings below mirror those files — keep */
856
+ /* them in sync when editing an icon. */
857
+ /* ================================================================== */
858
+
859
+ /* ── Chevrons ─────────────────────────────────────────────────────── */
860
+
861
+ const chevronRight = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>`;
862
+
863
+ const chevronDown = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`;
864
+
865
+ /* ── Double chevrons (collapse / expand) ──────────────────────────── */
866
+
867
+ const collapseLeft = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg>`;
868
+
869
+ const expandRight = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></svg>`;
870
+
871
+ class YumeAppbar extends HTMLElement {
872
+ static get observedAttributes() {
873
+ return [
874
+ "orientation",
875
+ "collapsed",
876
+ "items",
877
+ "size",
878
+ "menu-direction",
879
+ "sticky",
880
+ ];
881
+ }
882
+
883
+ constructor() {
884
+ super();
885
+ this.attachShadow({ mode: "open" });
886
+ this._onCollapseClick = this._onCollapseClick.bind(this);
887
+ this._idCounter = 0;
888
+ }
889
+
890
+ connectedCallback() {
891
+ this.render();
892
+ }
893
+
894
+ disconnectedCallback() {}
895
+
896
+ attributeChangedCallback(name, oldVal, newVal) {
897
+ if (oldVal === newVal) return;
898
+ this.render();
899
+ }
900
+
901
+ get orientation() {
902
+ return this.getAttribute("orientation") || "vertical";
903
+ }
904
+ set orientation(val) {
905
+ this.setAttribute("orientation", val);
906
+ }
907
+
908
+ get collapsed() {
909
+ return this.hasAttribute("collapsed");
910
+ }
911
+ set collapsed(val) {
912
+ if (val) this.setAttribute("collapsed", "");
913
+ else this.removeAttribute("collapsed");
914
+ }
915
+
916
+ get items() {
917
+ try {
918
+ return JSON.parse(this.getAttribute("items")) || [];
919
+ } catch {
920
+ return [];
921
+ }
922
+ }
923
+ set items(val) {
924
+ this.setAttribute("items", JSON.stringify(val));
925
+ }
926
+
927
+ get size() {
928
+ return this.getAttribute("size") || "medium";
929
+ }
930
+ set size(val) {
931
+ this.setAttribute("size", val);
932
+ }
933
+
934
+ /**
935
+ * Direction menus pop out from nav buttons:
936
+ * "right", "down", or unset (auto: vertical → right, horizontal → down).
937
+ */
938
+ get menuDirection() {
939
+ return this.getAttribute("menu-direction") || "";
940
+ }
941
+ set menuDirection(val) {
942
+ if (val) this.setAttribute("menu-direction", val);
943
+ else this.removeAttribute("menu-direction");
944
+ }
945
+
946
+ get sticky() {
947
+ const val = this.getAttribute("sticky");
948
+ return ["start", "end"].includes(val) ? val : false;
949
+ }
950
+ set sticky(val) {
951
+ if (val === "start" || val === "end") this.setAttribute("sticky", val);
952
+ else this.removeAttribute("sticky");
953
+ }
954
+
955
+ toggle() {
956
+ this.collapsed = !this.collapsed;
957
+ }
958
+
959
+ _onCollapseClick() {
960
+ this.toggle();
961
+ }
962
+
963
+ _uid(prefix) {
964
+ return `${prefix}-${this._idCounter++}`;
965
+ }
966
+
967
+ _isItemActive(item) {
968
+ if (item.selected) return true;
969
+ if (item.href) {
970
+ const loc = window.location;
971
+ const current = loc.pathname + loc.search + loc.hash;
972
+ return item.href === current || item.href === loc.href;
973
+ }
974
+ return false;
975
+ }
976
+
977
+ render() {
978
+ const isVertical = this.orientation === "vertical";
979
+ const isCollapsed = this.collapsed && isVertical;
980
+ const size = this.size;
981
+ const menuDir = this.menuDirection || (isVertical ? "right" : "down");
982
+
983
+ const sizeConfig = {
984
+ small: {
985
+ padding: "var(--spacing-x-small, 4px)",
986
+ collapsedWidth: "40px",
987
+ bodyGap: "2px",
988
+ buttonSize: "small",
989
+ },
990
+ medium: {
991
+ padding: "var(--spacing-small, 6px)",
992
+ collapsedWidth: "52px",
993
+ bodyGap: "3px",
994
+ buttonSize: "medium",
995
+ },
996
+ large: {
997
+ padding: "var(--spacing-medium, 8px)",
998
+ collapsedWidth: "64px",
999
+ bodyGap: "4px",
1000
+ buttonSize: "large",
1001
+ },
1002
+ };
1003
+ const cfg = sizeConfig[size] || sizeConfig.medium;
1004
+
1005
+ this.shadowRoot.innerHTML = "";
1006
+ this._idCounter = 0;
1007
+
1008
+ const style = document.createElement("style");
1009
+ style.textContent = `
1010
+ :host {
1011
+ display: block;
1012
+ font-family: var(--font-family-body, sans-serif);
1013
+ color: var(--component-appbar-color, #fff);
1014
+ }
1015
+
1016
+ :host([sticky]) {
1017
+ position: sticky;
1018
+ z-index: 100;
1019
+ }
1020
+ :host([orientation="vertical"][sticky="start"]),
1021
+ :host(:not([orientation])[sticky="start"]) {
1022
+ left: 0;
1023
+ top: 0;
1024
+ height: 100%;
1025
+ }
1026
+ :host([orientation="vertical"][sticky="end"]),
1027
+ :host(:not([orientation])[sticky="end"]) {
1028
+ right: 0;
1029
+ top: 0;
1030
+ height: 100%;
1031
+ }
1032
+ :host([orientation="horizontal"][sticky="start"]) {
1033
+ top: 0;
1034
+ left: 0;
1035
+ width: 100%;
1036
+ }
1037
+ :host([orientation="horizontal"][sticky="end"]) {
1038
+ bottom: 0;
1039
+ left: 0;
1040
+ width: 100%;
1041
+ }
1042
+
1043
+ :host([sticky]) .appbar {
1044
+ border-radius: 0;
1045
+ border: none;
1046
+ }
1047
+ :host([orientation="vertical"][sticky="start"]) .appbar,
1048
+ :host(:not([orientation])[sticky="start"]) .appbar {
1049
+ border-right: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1050
+ }
1051
+ :host([orientation="vertical"][sticky="end"]) .appbar,
1052
+ :host(:not([orientation])[sticky="end"]) .appbar {
1053
+ border-left: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1054
+ }
1055
+ :host([orientation="horizontal"][sticky="start"]) .appbar {
1056
+ border-bottom: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1057
+ }
1058
+ :host([orientation="horizontal"][sticky="end"]) .appbar {
1059
+ border-top: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1060
+ }
1061
+
1062
+ .appbar {
1063
+ display: flex;
1064
+ background: var(--component-appbar-background, #111);
1065
+ border: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1066
+ border-radius: var(--component-appbar-border-radius, var(--component-sidebar-border-radius, 4px));
1067
+ overflow: visible;
1068
+ padding: var(--_appbar-padding);
1069
+ box-sizing: border-box;
1070
+ }
1071
+
1072
+ .appbar.vertical {
1073
+ flex-direction: column;
1074
+ width: var(--component-appbar-width, 240px);
1075
+ height: 100%;
1076
+ transition: width 0.2s ease;
1077
+ }
1078
+ .appbar.vertical.collapsed {
1079
+ width: var(--_appbar-collapsed-width);
1080
+ }
1081
+
1082
+ .appbar.horizontal {
1083
+ flex-direction: row;
1084
+ width: 100%;
1085
+ height: auto;
1086
+ align-items: center;
1087
+ }
1088
+
1089
+ .appbar-header,
1090
+ .appbar-body,
1091
+ .appbar-footer {
1092
+ flex-shrink: 0;
1093
+ }
1094
+
1095
+ .appbar-body {
1096
+ flex: 1;
1097
+ overflow-y: auto;
1098
+ overflow-x: hidden;
1099
+ display: flex;
1100
+ gap: var(--_appbar-body-gap);
1101
+ }
1102
+
1103
+ .appbar.vertical .appbar-body {
1104
+ flex-direction: column;
1105
+ }
1106
+ .appbar.horizontal .appbar-body {
1107
+ flex-direction: row;
1108
+ overflow-y: hidden;
1109
+ overflow-x: auto;
1110
+ align-items: center;
1111
+ }
1112
+
1113
+ .appbar.vertical .appbar-header {
1114
+ border-bottom: var(--component-appbar-inner-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1115
+ padding-bottom: var(--_appbar-padding);
1116
+ margin-bottom: var(--_appbar-padding);
1117
+ }
1118
+ .appbar.vertical .appbar-footer {
1119
+ border-top: var(--component-appbar-inner-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1120
+ padding-top: var(--_appbar-padding);
1121
+ margin-top: var(--_appbar-padding);
1122
+ }
1123
+
1124
+ .appbar.horizontal .appbar-header {
1125
+ border-right: var(--component-appbar-inner-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1126
+ padding-right: var(--_appbar-padding);
1127
+ margin-right: var(--_appbar-padding);
1128
+ }
1129
+ .appbar.horizontal .appbar-footer {
1130
+ border-left: var(--component-appbar-inner-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #333);
1131
+ padding-left: var(--_appbar-padding);
1132
+ margin-left: var(--_appbar-padding);
1133
+ }
1134
+
1135
+ .header-content {
1136
+ display: flex;
1137
+ align-items: center;
1138
+ gap: var(--spacing-small, 8px);
1139
+ overflow: hidden;
1140
+ }
1141
+ .appbar.horizontal .header-content,
1142
+ .appbar.vertical .header-content {
1143
+ flex-direction: row;
1144
+ }
1145
+ .logo-wrapper {
1146
+ width: var(--_icon-col-width);
1147
+ display: flex;
1148
+ justify-content: center;
1149
+ align-items: center;
1150
+ flex-shrink: 0;
1151
+ }
1152
+
1153
+ .header-title {
1154
+ font-weight: bold;
1155
+ white-space: nowrap;
1156
+ overflow: hidden;
1157
+ text-overflow: ellipsis;
1158
+ font-size: var(--font-size-label, 0.9em);
1159
+ }
1160
+ .appbar.vertical.collapsed .header-title {
1161
+ display: none;
1162
+ }
1163
+
1164
+ .nav-item {
1165
+ display: flex;
1166
+ align-items: center;
1167
+ position: relative;
1168
+ }
1169
+ .appbar.vertical .nav-item {
1170
+ width: 100%;
1171
+ }
1172
+ .appbar.vertical .nav-item y-button {
1173
+ display: block;
1174
+ width: 100%;
1175
+ }
1176
+ .appbar.vertical .nav-item y-button::part(button),
1177
+ .appbar.vertical .appbar-footer y-button::part(button) {
1178
+ width: 100%;
1179
+ justify-content: flex-start;
1180
+ padding-left: 0;
1181
+ padding-right: 0;
1182
+ }
1183
+
1184
+ /* Fixed-width icon column — matches collapsed inner width so icons stay centred across states */
1185
+ .appbar.vertical .nav-item y-button::part(left-icon),
1186
+ .appbar.vertical .appbar-footer y-button::part(left-icon) {
1187
+ width: var(--_icon-col-width);
1188
+ display: flex;
1189
+ justify-content: center;
1190
+ flex-shrink: 0;
1191
+ }
1192
+
1193
+ .appbar.vertical .nav-item y-button::part(right-icon) {
1194
+ margin-left: auto;
1195
+ }
1196
+
1197
+ .appbar.vertical.collapsed .nav-item y-button::part(button),
1198
+ .appbar.vertical.collapsed .appbar-footer y-button::part(button) {
1199
+ min-width: 0;
1200
+ }
1201
+ .appbar.vertical.collapsed .nav-item y-button::part(label),
1202
+ .appbar.vertical.collapsed .appbar-footer y-button::part(label) {
1203
+ display: none;
1204
+ }
1205
+ .appbar.vertical.collapsed .nav-item y-button::part(right-icon),
1206
+ .appbar.vertical.collapsed .appbar-footer y-button::part(right-icon) {
1207
+ display: none;
1208
+ }
1209
+
1210
+ .appbar-footer {
1211
+ display: flex;
1212
+ flex-direction: column;
1213
+ gap: 2px;
1214
+ }
1215
+ .appbar.horizontal .appbar-footer {
1216
+ flex-direction: row;
1217
+ align-items: center;
1218
+ }
1219
+ .appbar.vertical .appbar-footer y-button {
1220
+ display: block;
1221
+ width: 100%;
1222
+ }
1223
+
1224
+ .appbar.vertical.collapsed .appbar-header,
1225
+ .appbar.vertical.collapsed .appbar-body,
1226
+ .appbar.vertical.collapsed .appbar-footer {
1227
+ align-items: center;
1228
+ }
1229
+
1230
+ ::slotted(*) {
1231
+ display: block;
1232
+ }
1233
+ `;
1234
+ this.shadowRoot.appendChild(style);
1235
+
1236
+ // Clone document stylesheets so CSS-class-based icons (e.g. Font Awesome) render in shadow DOM
1237
+ document.querySelectorAll('link[rel="stylesheet"]').forEach((link) => {
1238
+ this.shadowRoot.appendChild(link.cloneNode(true));
1239
+ });
1240
+
1241
+ const bar = document.createElement("div");
1242
+ bar.className = `appbar ${isVertical ? "vertical" : "horizontal"}`;
1243
+ if (isCollapsed) bar.classList.add("collapsed");
1244
+ bar.setAttribute("role", "navigation");
1245
+ bar.style.setProperty("--_appbar-padding", cfg.padding);
1246
+ bar.style.setProperty("--_appbar-collapsed-width", cfg.collapsedWidth);
1247
+ bar.style.setProperty("--_appbar-body-gap", cfg.bodyGap);
1248
+ bar.style.setProperty(
1249
+ "--_icon-col-width",
1250
+ `calc(${cfg.collapsedWidth} - 2 * var(--_appbar-padding) - 2 * var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)))`,
1251
+ );
1252
+
1253
+ /* --- Header: logo + title --- */
1254
+ const header = document.createElement("div");
1255
+ header.className = "appbar-header";
1256
+
1257
+ const headerContent = document.createElement("div");
1258
+ headerContent.className = "header-content";
1259
+
1260
+ const logoWrapper = document.createElement("div");
1261
+ logoWrapper.className = "logo-wrapper";
1262
+ const logoSlot = document.createElement("slot");
1263
+ logoSlot.name = "logo";
1264
+ logoWrapper.appendChild(logoSlot);
1265
+ headerContent.appendChild(logoWrapper);
1266
+
1267
+ const titleWrapper = document.createElement("div");
1268
+ titleWrapper.className = "header-title";
1269
+ const titleSlot = document.createElement("slot");
1270
+ titleSlot.name = "title";
1271
+ titleWrapper.appendChild(titleSlot);
1272
+ headerContent.appendChild(titleWrapper);
1273
+
1274
+ header.appendChild(headerContent);
1275
+ bar.appendChild(header);
1276
+
1277
+ /* --- Body: y-button nav items --- */
1278
+ const body = document.createElement("div");
1279
+ body.className = "appbar-body";
1280
+
1281
+ const navItems = this.items;
1282
+ navItems.forEach((item) => {
1283
+ const hasChildren = item.children?.length > 0;
1284
+ const wrapper = document.createElement("div");
1285
+ wrapper.className = "nav-item";
1286
+
1287
+ const btn = document.createElement("y-button");
1288
+ const btnId = this._uid("appbar-btn");
1289
+ btn.id = btnId;
1290
+ const isActive = this._isItemActive(item);
1291
+ btn.setAttribute("color", isActive ? "primary" : "base");
1292
+ btn.setAttribute("style-type", "flat");
1293
+ btn.setAttribute("size", cfg.buttonSize);
1294
+
1295
+ if (item.icon) {
1296
+ const iconEl = document.createElement("span");
1297
+ iconEl.slot = "left-icon";
1298
+ iconEl.innerHTML = item.icon;
1299
+ btn.appendChild(iconEl);
1300
+ }
1301
+
1302
+ if (item.text && !isCollapsed) {
1303
+ const label = document.createTextNode(item.text);
1304
+ btn.appendChild(label);
1305
+ }
1306
+
1307
+ if (hasChildren && !isCollapsed) {
1308
+ const arrow = document.createElement("span");
1309
+ arrow.slot = "right-icon";
1310
+ arrow.innerHTML = isVertical ? chevronRight : chevronDown;
1311
+ btn.appendChild(arrow);
1312
+ }
1313
+
1314
+ if (item.href && !hasChildren) {
1315
+ btn.addEventListener("click", () => {
1316
+ window.location.href = item.href;
1317
+ });
1318
+ }
1319
+
1320
+ wrapper.appendChild(btn);
1321
+
1322
+ if (hasChildren) {
1323
+ const menu = document.createElement("y-menu");
1324
+ menu.setAttribute("anchor", btnId);
1325
+ menu.setAttribute("direction", menuDir);
1326
+ menu.setAttribute("size", cfg.buttonSize);
1327
+ menu.items = item.children;
1328
+ wrapper.appendChild(menu);
1329
+ }
1330
+
1331
+ body.appendChild(wrapper);
1332
+ });
1333
+
1334
+ bar.appendChild(body);
1335
+
1336
+ /* --- Footer: slot + collapse toggle (vertical only) --- */
1337
+ const footer = document.createElement("div");
1338
+ footer.className = "appbar-footer";
1339
+
1340
+ const footerSlot = document.createElement("slot");
1341
+ footerSlot.name = "footer";
1342
+ footer.appendChild(footerSlot);
1343
+
1344
+ if (isVertical) {
1345
+ const collapseBtn = document.createElement("y-button");
1346
+ collapseBtn.setAttribute("color", "base");
1347
+ collapseBtn.setAttribute("style-type", "flat");
1348
+ collapseBtn.setAttribute("size", cfg.buttonSize);
1349
+ collapseBtn.setAttribute(
1350
+ "aria-label",
1351
+ isCollapsed ? "Expand sidebar" : "Collapse sidebar",
1352
+ );
1353
+ collapseBtn.className = "collapse-btn";
1354
+
1355
+ const collapseIcon = document.createElement("span");
1356
+ collapseIcon.slot = "left-icon";
1357
+ collapseIcon.innerHTML = isCollapsed ? expandRight : collapseLeft;
1358
+ collapseBtn.appendChild(collapseIcon);
1359
+
1360
+ if (!isCollapsed) {
1361
+ const collapseLabel = document.createTextNode("Collapse");
1362
+ collapseBtn.appendChild(collapseLabel);
1363
+ }
1364
+
1365
+ collapseBtn.addEventListener("click", this._onCollapseClick);
1366
+ footer.appendChild(collapseBtn);
1367
+ }
1368
+
1369
+ bar.appendChild(footer);
1370
+ this.shadowRoot.appendChild(bar);
1371
+ }
1372
+ }
1373
+
1374
+ if (!customElements.get("y-appbar")) {
1375
+ customElements.define("y-appbar", YumeAppbar);
1376
+ }
1377
+
1378
+ export { YumeAppbar };