@studious-creative/yumekit 0.1.6 → 0.1.8
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 +327 -9
- 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 +19 -0
- package/dist/components/y-icon.js +287 -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.d.ts +1 -1
- package/dist/components/y-toast.js +6 -4
- package/dist/components/y-tooltip.js +1 -1
- package/dist/icons/all.d.ts +1 -0
- package/dist/icons/all.js +158 -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 +540 -28
- package/dist/styles/variables.css +57 -3
- package/dist/yumekit.min.js +1 -1
- package/package.json +3 -1
|
@@ -554,6 +554,292 @@ if (!customElements.get("y-button")) {
|
|
|
554
554
|
customElements.define("y-button", YumeButton);
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
559
|
+
*
|
|
560
|
+
* Register only the icons you need for tree-shaking:
|
|
561
|
+
*
|
|
562
|
+
* import { registerIcon } from "@studious-creative/yumekit";
|
|
563
|
+
* registerIcon("home", homeSvgString);
|
|
564
|
+
*
|
|
565
|
+
* Or register all bundled icons at once (separate import):
|
|
566
|
+
*
|
|
567
|
+
* import "@studious-creative/yumekit/icons/all.js";
|
|
568
|
+
*/
|
|
569
|
+
|
|
570
|
+
const icons = new Map();
|
|
571
|
+
|
|
572
|
+
function getIcon(name) {
|
|
573
|
+
return icons.get(name) || "";
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Allowlist-based SVG sanitizer — only known-safe elements and attributes are kept.
|
|
577
|
+
const ALLOWED_ELEMENTS = new Set([
|
|
578
|
+
"svg",
|
|
579
|
+
"g",
|
|
580
|
+
"path",
|
|
581
|
+
"circle",
|
|
582
|
+
"ellipse",
|
|
583
|
+
"rect",
|
|
584
|
+
"line",
|
|
585
|
+
"polyline",
|
|
586
|
+
"polygon",
|
|
587
|
+
"text",
|
|
588
|
+
"tspan",
|
|
589
|
+
"defs",
|
|
590
|
+
"clippath",
|
|
591
|
+
"mask",
|
|
592
|
+
"lineargradient",
|
|
593
|
+
"radialgradient",
|
|
594
|
+
"stop",
|
|
595
|
+
"symbol",
|
|
596
|
+
"title",
|
|
597
|
+
"desc",
|
|
598
|
+
"metadata",
|
|
599
|
+
]);
|
|
600
|
+
|
|
601
|
+
const ALLOWED_ATTRS = new Set([
|
|
602
|
+
"viewbox",
|
|
603
|
+
"xmlns",
|
|
604
|
+
"fill",
|
|
605
|
+
"stroke",
|
|
606
|
+
"stroke-width",
|
|
607
|
+
"stroke-linecap",
|
|
608
|
+
"stroke-linejoin",
|
|
609
|
+
"stroke-dasharray",
|
|
610
|
+
"stroke-dashoffset",
|
|
611
|
+
"stroke-miterlimit",
|
|
612
|
+
"stroke-opacity",
|
|
613
|
+
"fill-opacity",
|
|
614
|
+
"fill-rule",
|
|
615
|
+
"clip-rule",
|
|
616
|
+
"opacity",
|
|
617
|
+
"d",
|
|
618
|
+
"cx",
|
|
619
|
+
"cy",
|
|
620
|
+
"r",
|
|
621
|
+
"rx",
|
|
622
|
+
"ry",
|
|
623
|
+
"x",
|
|
624
|
+
"x1",
|
|
625
|
+
"x2",
|
|
626
|
+
"y",
|
|
627
|
+
"y1",
|
|
628
|
+
"y2",
|
|
629
|
+
"width",
|
|
630
|
+
"height",
|
|
631
|
+
"points",
|
|
632
|
+
"transform",
|
|
633
|
+
"id",
|
|
634
|
+
"class",
|
|
635
|
+
"clip-path",
|
|
636
|
+
"mask",
|
|
637
|
+
"offset",
|
|
638
|
+
"stop-color",
|
|
639
|
+
"stop-opacity",
|
|
640
|
+
"gradient-units",
|
|
641
|
+
"gradienttransform",
|
|
642
|
+
"gradientunits",
|
|
643
|
+
"spreadmethod",
|
|
644
|
+
"patternunits",
|
|
645
|
+
"patterntransform",
|
|
646
|
+
"font-size",
|
|
647
|
+
"font-family",
|
|
648
|
+
"font-weight",
|
|
649
|
+
"text-anchor",
|
|
650
|
+
"dominant-baseline",
|
|
651
|
+
"alignment-baseline",
|
|
652
|
+
"dx",
|
|
653
|
+
"dy",
|
|
654
|
+
"rotate",
|
|
655
|
+
"textlength",
|
|
656
|
+
"lengthadjust",
|
|
657
|
+
"display",
|
|
658
|
+
"visibility",
|
|
659
|
+
"color",
|
|
660
|
+
"vector-effect",
|
|
661
|
+
]);
|
|
662
|
+
|
|
663
|
+
function sanitizeSvg(raw) {
|
|
664
|
+
if (!raw) return "";
|
|
665
|
+
const doc = new DOMParser().parseFromString(raw, "image/svg+xml");
|
|
666
|
+
const svg = doc.querySelector("svg");
|
|
667
|
+
if (!svg) return "";
|
|
668
|
+
|
|
669
|
+
const walk = (el) => {
|
|
670
|
+
for (const child of [...el.children]) {
|
|
671
|
+
if (!ALLOWED_ELEMENTS.has(child.tagName.toLowerCase())) {
|
|
672
|
+
child.remove();
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
for (const attr of [...child.attributes]) {
|
|
676
|
+
if (!ALLOWED_ATTRS.has(attr.name.toLowerCase())) {
|
|
677
|
+
child.removeAttribute(attr.name);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
walk(child);
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// Sanitize the <svg> element's own attributes
|
|
685
|
+
for (const attr of [...svg.attributes]) {
|
|
686
|
+
if (!ALLOWED_ATTRS.has(attr.name.toLowerCase())) {
|
|
687
|
+
svg.removeAttribute(attr.name);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
walk(svg);
|
|
691
|
+
return svg.outerHTML;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Cache sanitized SVG markup per icon name to avoid repeated DOMParser + DOM-walk
|
|
695
|
+
// on every render. The cache is naturally bounded by the number of registered icons.
|
|
696
|
+
const sanitizedSvgCache = new Map();
|
|
697
|
+
|
|
698
|
+
function getCachedSvg(name) {
|
|
699
|
+
if (sanitizedSvgCache.has(name)) {
|
|
700
|
+
return sanitizedSvgCache.get(name);
|
|
701
|
+
}
|
|
702
|
+
const result = sanitizeSvg(getIcon(name));
|
|
703
|
+
sanitizedSvgCache.set(name, result);
|
|
704
|
+
return result;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
class YumeIcon extends HTMLElement {
|
|
708
|
+
static get observedAttributes() {
|
|
709
|
+
return ["name", "size", "color", "label", "weight"];
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
constructor() {
|
|
713
|
+
super();
|
|
714
|
+
this.attachShadow({ mode: "open" });
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
connectedCallback() {
|
|
718
|
+
this.render();
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
722
|
+
if (oldVal === newVal) return;
|
|
723
|
+
this.render();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
get name() {
|
|
727
|
+
return this.getAttribute("name") || "";
|
|
728
|
+
}
|
|
729
|
+
set name(val) {
|
|
730
|
+
this.setAttribute("name", val);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
get size() {
|
|
734
|
+
return this.getAttribute("size") || "medium";
|
|
735
|
+
}
|
|
736
|
+
set size(val) {
|
|
737
|
+
this.setAttribute("size", val);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
get color() {
|
|
741
|
+
return this.getAttribute("color") || "";
|
|
742
|
+
}
|
|
743
|
+
set color(val) {
|
|
744
|
+
if (val) this.setAttribute("color", val);
|
|
745
|
+
else this.removeAttribute("color");
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
get label() {
|
|
749
|
+
return this.getAttribute("label") || "";
|
|
750
|
+
}
|
|
751
|
+
set label(val) {
|
|
752
|
+
if (val) this.setAttribute("label", val);
|
|
753
|
+
else this.removeAttribute("label");
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
get weight() {
|
|
757
|
+
return this.getAttribute("weight") || "";
|
|
758
|
+
}
|
|
759
|
+
set weight(val) {
|
|
760
|
+
if (val) this.setAttribute("weight", val);
|
|
761
|
+
else this.removeAttribute("weight");
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
_getColor(color) {
|
|
765
|
+
const map = {
|
|
766
|
+
base: "var(--base-content--, #f7f7fa)",
|
|
767
|
+
primary: "var(--primary-content--, #0576ff)",
|
|
768
|
+
secondary: "var(--secondary-content--, #04b8b8)",
|
|
769
|
+
success: "var(--success-content--, #2dba73)",
|
|
770
|
+
warning: "var(--warning-content--, #d17f04)",
|
|
771
|
+
error: "var(--error-content--, #b80421)",
|
|
772
|
+
help: "var(--help-content--, #5405ff)",
|
|
773
|
+
};
|
|
774
|
+
return map[color] || map.base;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
_getSize(size) {
|
|
778
|
+
const map = {
|
|
779
|
+
small: "var(--component-icon-size-small, 16px)",
|
|
780
|
+
medium: "var(--component-icon-size-medium, 24px)",
|
|
781
|
+
large: "var(--component-icon-size-large, 32px)",
|
|
782
|
+
};
|
|
783
|
+
return map[size] || map.medium;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
_getWeight(weight) {
|
|
787
|
+
const map = {
|
|
788
|
+
thin: "1",
|
|
789
|
+
regular: "1.5",
|
|
790
|
+
thick: "2",
|
|
791
|
+
};
|
|
792
|
+
return map[weight] || "";
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
render() {
|
|
796
|
+
const svg = getCachedSvg(this.name);
|
|
797
|
+
const sizeVal = this._getSize(this.size);
|
|
798
|
+
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
799
|
+
const weightVal = this._getWeight(this.weight);
|
|
800
|
+
const label = this.label;
|
|
801
|
+
|
|
802
|
+
if (label) {
|
|
803
|
+
this.setAttribute("role", "img");
|
|
804
|
+
this.setAttribute("aria-label", label);
|
|
805
|
+
this.removeAttribute("aria-hidden");
|
|
806
|
+
} else {
|
|
807
|
+
this.setAttribute("aria-hidden", "true");
|
|
808
|
+
this.removeAttribute("role");
|
|
809
|
+
this.removeAttribute("aria-label");
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const weightCSS = weightVal
|
|
813
|
+
? `.icon-wrapper svg,
|
|
814
|
+
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`
|
|
815
|
+
: "";
|
|
816
|
+
|
|
817
|
+
this.shadowRoot.innerHTML = `
|
|
818
|
+
<style>
|
|
819
|
+
:host {
|
|
820
|
+
display: inline-flex;
|
|
821
|
+
align-items: center;
|
|
822
|
+
justify-content: center;
|
|
823
|
+
width: ${sizeVal};
|
|
824
|
+
height: ${sizeVal};
|
|
825
|
+
color: ${colorVal};
|
|
826
|
+
line-height: 0;
|
|
827
|
+
}
|
|
828
|
+
.icon-wrapper svg {
|
|
829
|
+
width: 100%;
|
|
830
|
+
height: 100%;
|
|
831
|
+
}
|
|
832
|
+
${weightCSS}
|
|
833
|
+
</style>
|
|
834
|
+
<span class="icon-wrapper" part="icon">${svg}</span>
|
|
835
|
+
`;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (!customElements.get("y-icon")) {
|
|
840
|
+
customElements.define("y-icon", YumeIcon);
|
|
841
|
+
}
|
|
842
|
+
|
|
557
843
|
class YumeMenu extends HTMLElement {
|
|
558
844
|
static get observedAttributes() {
|
|
559
845
|
return ["items", "anchor", "visible", "direction", "size"];
|
|
@@ -762,9 +1048,30 @@ class YumeMenu extends HTMLElement {
|
|
|
762
1048
|
left = anchorRect.left - menuRect.width;
|
|
763
1049
|
}
|
|
764
1050
|
if (top + menuRect.height > vh) {
|
|
765
|
-
top =
|
|
1051
|
+
top = anchorRect.top - menuRect.height;
|
|
1052
|
+
}
|
|
1053
|
+
} else if (this.direction === "up") {
|
|
1054
|
+
top = anchorRect.top - menuRect.height;
|
|
1055
|
+
left = anchorRect.left;
|
|
1056
|
+
|
|
1057
|
+
if (top < 0) {
|
|
1058
|
+
top = anchorRect.bottom;
|
|
1059
|
+
}
|
|
1060
|
+
if (left + menuRect.width > vw) {
|
|
1061
|
+
left = vw - menuRect.width - 10;
|
|
1062
|
+
}
|
|
1063
|
+
} else if (this.direction === "left") {
|
|
1064
|
+
top = anchorRect.top;
|
|
1065
|
+
left = anchorRect.left - menuRect.width;
|
|
1066
|
+
|
|
1067
|
+
if (left < 0) {
|
|
1068
|
+
left = anchorRect.right;
|
|
1069
|
+
}
|
|
1070
|
+
if (top + menuRect.height > vh) {
|
|
1071
|
+
top = anchorRect.top - menuRect.height;
|
|
766
1072
|
}
|
|
767
1073
|
} else {
|
|
1074
|
+
// "down" (default)
|
|
768
1075
|
top = anchorRect.bottom;
|
|
769
1076
|
left = anchorRect.left;
|
|
770
1077
|
|
|
@@ -776,8 +1083,8 @@ class YumeMenu extends HTMLElement {
|
|
|
776
1083
|
}
|
|
777
1084
|
}
|
|
778
1085
|
|
|
779
|
-
top = Math.max(
|
|
780
|
-
left = Math.max(
|
|
1086
|
+
top = Math.max(0, Math.min(top, vh - menuRect.height));
|
|
1087
|
+
left = Math.max(0, Math.min(left, vw - menuRect.width));
|
|
781
1088
|
|
|
782
1089
|
this.style.top = `${top}px`;
|
|
783
1090
|
this.style.left = `${left}px`;
|
|
@@ -825,7 +1132,7 @@ class YumeMenu extends HTMLElement {
|
|
|
825
1132
|
top: 0;
|
|
826
1133
|
left: 100%;
|
|
827
1134
|
display: none;
|
|
828
|
-
z-index: 1001;
|
|
1135
|
+
z-index: var(--component-menu-z-index, 1001);
|
|
829
1136
|
}
|
|
830
1137
|
|
|
831
1138
|
li.menuitem:hover > ul.submenu {
|
|
@@ -1024,7 +1331,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1024
1331
|
|
|
1025
1332
|
:host([sticky]) {
|
|
1026
1333
|
position: sticky;
|
|
1027
|
-
z-index: 100;
|
|
1334
|
+
z-index: var(--component-appbar-z-index, 100);
|
|
1028
1335
|
}
|
|
1029
1336
|
:host([orientation="vertical"][sticky="start"]),
|
|
1030
1337
|
:host(:not([orientation])[sticky="start"]) {
|
|
@@ -1249,6 +1556,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1249
1556
|
|
|
1250
1557
|
const bar = document.createElement("div");
|
|
1251
1558
|
bar.className = `appbar ${isVertical ? "vertical" : "horizontal"}`;
|
|
1559
|
+
|
|
1252
1560
|
if (isCollapsed) bar.classList.add("collapsed");
|
|
1253
1561
|
bar.setAttribute("role", "navigation");
|
|
1254
1562
|
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
@@ -1269,6 +1577,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1269
1577
|
|
|
1270
1578
|
const logoWrapper = document.createElement("div");
|
|
1271
1579
|
logoWrapper.className = "logo-wrapper";
|
|
1580
|
+
|
|
1272
1581
|
const logoSlot = document.createElement("slot");
|
|
1273
1582
|
logoSlot.name = "logo";
|
|
1274
1583
|
logoWrapper.appendChild(logoSlot);
|
|
@@ -1276,6 +1585,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1276
1585
|
|
|
1277
1586
|
const titleWrapper = document.createElement("div");
|
|
1278
1587
|
titleWrapper.className = "header-title";
|
|
1588
|
+
|
|
1279
1589
|
const titleSlot = document.createElement("slot");
|
|
1280
1590
|
titleSlot.name = "title";
|
|
1281
1591
|
titleWrapper.appendChild(titleSlot);
|
|
@@ -1304,10 +1614,18 @@ class YumeAppbar extends HTMLElement {
|
|
|
1304
1614
|
btn.setAttribute("size", cfg.buttonSize);
|
|
1305
1615
|
|
|
1306
1616
|
if (item.icon) {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1617
|
+
if (item.icon.trim().startsWith("<")) {
|
|
1618
|
+
const iconEl = document.createElement("span");
|
|
1619
|
+
iconEl.slot = "left-icon";
|
|
1620
|
+
iconEl.innerHTML = item.icon;
|
|
1621
|
+
btn.appendChild(iconEl);
|
|
1622
|
+
} else {
|
|
1623
|
+
const iconEl = document.createElement("y-icon");
|
|
1624
|
+
iconEl.slot = "left-icon";
|
|
1625
|
+
iconEl.setAttribute("name", item.icon);
|
|
1626
|
+
iconEl.setAttribute("size", "small");
|
|
1627
|
+
btn.appendChild(iconEl);
|
|
1628
|
+
}
|
|
1311
1629
|
}
|
|
1312
1630
|
|
|
1313
1631
|
if (item.text && !isCollapsed) {
|
|
@@ -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,19 @@
|
|
|
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
|
+
set weight(val: string);
|
|
14
|
+
get weight(): string;
|
|
15
|
+
_getColor(color: any): any;
|
|
16
|
+
_getSize(size: any): any;
|
|
17
|
+
_getWeight(weight: any): any;
|
|
18
|
+
render(): void;
|
|
19
|
+
}
|