@waggylabs/yumekit 0.5.0 → 0.5.1-beta.3
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/CHANGELOG.md +39 -1
- package/CONTRIBUTING.md +1 -1
- package/dist/components/y-appbar.js +27 -15
- package/dist/components/y-banner.js +27 -15
- package/dist/components/y-breadcrumbs.js +25 -13
- package/dist/components/y-break/y-break.d.ts +1 -1
- package/dist/components/y-break.d.ts +1 -1
- package/dist/components/y-break.js +30 -18
- package/dist/components/y-button.js +3 -3
- package/dist/components/y-checkbox.js +25 -13
- package/dist/components/y-code/tokenizer.d.ts +2 -0
- package/dist/components/y-code/y-code.d.ts +70 -0
- package/dist/components/y-code.d.ts +70 -0
- package/dist/components/y-code.js +2450 -0
- package/dist/components/y-color.js +95 -14
- package/dist/components/y-colorpicker.js +95 -14
- package/dist/components/y-data-grid/y-data-grid.d.ts +260 -0
- package/dist/components/y-data-grid.d.ts +260 -0
- package/dist/components/y-data-grid.js +11848 -0
- package/dist/components/y-date.js +98 -17
- package/dist/components/y-datepicker.js +98 -17
- package/dist/components/y-dock.js +25 -13
- package/dist/components/y-droplist/y-droplist.d.ts +8 -0
- package/dist/components/y-droplist.d.ts +8 -0
- package/dist/components/y-droplist.js +50 -4
- package/dist/components/y-gallery.js +25 -13
- package/dist/components/y-help/y-help.d.ts +143 -0
- package/dist/components/y-help.d.ts +143 -0
- package/dist/components/y-help.js +4025 -0
- package/dist/components/y-icon/y-icon.d.ts +7 -2
- package/dist/components/y-icon.d.ts +7 -2
- package/dist/components/y-icon.js +25 -13
- package/dist/components/y-menu.js +25 -13
- package/dist/components/y-paginator/y-paginator.d.ts +17 -0
- package/dist/components/y-paginator.d.ts +17 -0
- package/dist/components/y-paginator.js +270 -18
- package/dist/components/y-popover/y-popover.d.ts +163 -0
- package/dist/components/y-popover.d.ts +163 -0
- package/dist/components/y-popover.js +2155 -0
- package/dist/components/y-rating/y-rating.d.ts +2 -2
- package/dist/components/y-rating.d.ts +2 -2
- package/dist/components/y-rating.js +47 -14
- package/dist/components/y-select/y-select.d.ts +11 -0
- package/dist/components/y-select.d.ts +11 -0
- package/dist/components/y-select.js +96 -15
- package/dist/components/y-shape/y-shape.d.ts +39 -0
- package/dist/components/y-shape.d.ts +39 -0
- package/dist/components/y-shape.js +302 -0
- package/dist/components/y-sidebar.js +27 -15
- package/dist/components/y-splitter.js +25 -13
- package/dist/components/y-stepper/y-stepper.d.ts +16 -0
- package/dist/components/y-stepper.d.ts +16 -0
- package/dist/components/y-stepper.js +124 -21
- package/dist/components/y-tabs.js +25 -13
- package/dist/components/y-tag.js +2 -6
- package/dist/components/y-theme.js +23 -23
- package/dist/components/y-toast.js +25 -13
- package/dist/components/y-tree-item.js +25 -13
- package/dist/components/y-tree.js +25 -13
- package/dist/icons/all-filled.d.ts +1 -0
- package/dist/icons/all-filled.js +362 -0
- package/dist/icons/all.js +7 -3
- package/dist/icons/registry.d.ts +2 -0
- package/dist/icons/registry.js +6 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +11765 -6150
- package/dist/modules/floating.d.ts +126 -0
- package/dist/modules/floating.js +285 -0
- package/dist/modules/floating.test.js +180 -0
- package/dist/modules/helpers.d.ts +15 -7
- package/dist/modules/helpers.js +18 -15
- package/dist/modules/helpers.test.js +61 -0
- package/dist/react.d.ts +127 -2
- package/dist/styles/blue-dark.css +5 -0
- package/dist/styles/blue-light.css +5 -0
- package/dist/styles/brown-dark.css +5 -0
- package/dist/styles/brown-light.css +5 -0
- package/dist/styles/green-dark.css +5 -0
- package/dist/styles/green-light.css +5 -0
- package/dist/styles/indigo-dark.css +5 -0
- package/dist/styles/indigo-light.css +5 -0
- package/dist/styles/olive-dark.css +5 -0
- package/dist/styles/olive-light.css +5 -0
- package/dist/styles/orange-dark.css +5 -0
- package/dist/styles/orange-light.css +5 -0
- package/dist/styles/pink-dark.css +5 -0
- package/dist/styles/pink-light.css +5 -0
- package/dist/styles/purple-dark.css +5 -0
- package/dist/styles/purple-light.css +5 -0
- package/dist/styles/red-dark.css +5 -0
- package/dist/styles/red-light.css +5 -0
- package/dist/styles/teal-dark.css +5 -0
- package/dist/styles/teal-light.css +5 -0
- package/dist/styles/variables.css +46 -0
- package/dist/styles/yellow-dark.css +5 -0
- package/dist/styles/yellow-light.css +5 -0
- package/dist/yumekit.min.js +1 -1
- package/llm.txt +335 -9
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -31,7 +31,45 @@ Delete any empty sections before publishing.
|
|
|
31
31
|
<!-- ### Security -->
|
|
32
32
|
<!-- Vulnerability patches or hardening changes -->
|
|
33
33
|
|
|
34
|
-
## [0.5.
|
|
34
|
+
## [0.5.1]
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- New `y-data-grid` component — interactive grid for large or dynamic datasets with client- or server-side sorting, filtering, and pagination, single/multi row selection, inline cell editing, nested row grouping with aggregates, multi-column header groups, virtual scrolling, an optional per-column header menu (filter, sort, visibility, reorder), and a sticky header.
|
|
39
|
+
|
|
40
|
+
- New `y-popover` component — target-anchored floating panel with rich slotted content, composable triggers (`click` / `hover` / `focus` / `context-menu` / `manual`), flip-on-collision positioning, optional modal mode with focus trap, and an opt-in `portal` mode that renders into `<body>` to escape ancestor stacking contexts.
|
|
41
|
+
|
|
42
|
+
- New `y-help` component — guided product-tour / onboarding overlay. Walks users through an ordered list of steps with a dimmed SVG highlight, anchored tooltip, prev/next controls, overlay-edge arrows, and keyboard shortcuts.
|
|
43
|
+
|
|
44
|
+
- New `y-code` component — code-block container with line numbers, copy-to-clipboard (block and per-line), `max-lines` collapse, and an optional filename header. Built-in tokenizer covers JavaScript, TypeScript, JSON, CSS, Python, Bash, and HTML; emits Prism-compatible classes so existing Prism stylesheets layer on cleanly, or pipe an external highlighter's output through the sanitized `highlighted` slot.
|
|
45
|
+
|
|
46
|
+
- New `y-shape` component — presentational container that clips its slotted content into a geometric shape (rectangle, circle, ellipse, star, heart, chat-bubble, times, cross, or a custom `polygon`) via CSS `clip-path`. Suitable for avatar masks, decorative panels, and non-rectangular skeletons.
|
|
47
|
+
|
|
48
|
+
- `y-stepper` — `responsive` and `responsive-breakpoint` attributes; auto-flips horizontal layouts to vertical below the breakpoint (600px default). Enabled by default; set `responsive="false"` to opt out.
|
|
49
|
+
|
|
50
|
+
- Filled icon variants for `y-icon` via a new `weight="filled"` value, with automatic fallback to the line icon when no filled version is registered. 116 filled icons ship under `icons/all-filled.js` (also pulled in by `icons/all.js`); the remaining component-illustration icons fall back to their line versions.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Custom color support expanded to the browser-native color functions — `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, and `color()` — in addition to `#hex`, `rgb()`/`rgba()`, and `hsl()`/`hsla()`. The shared `isSafeCssColor` gate now also tightens its argument allowlist (rejecting semicolons, braces, angle brackets, and nested functions). Applies anywhere a `color` accepts a custom value, including `y-badge`, `y-select`, `y-popover`, `y-button`, `y-tag`, `y-icon`, and `y-rating`.
|
|
55
|
+
|
|
56
|
+
- `y-rating` — selected icons now swap in the registered `filled` weight variant instead of just thickening the line stroke, falling back to the thick stroke when no filled variant is available.
|
|
57
|
+
|
|
58
|
+
- `y-paginator` — page-button list now auto-shrinks to fit the host width, growing back when space allows.
|
|
59
|
+
|
|
60
|
+
- `y-select` — new opt-in `portal` attribute renders the dropdown into `<body>` so it escapes scrollable or clipped ancestors (e.g. inside a data-grid cell editor).
|
|
61
|
+
|
|
62
|
+
- **Breaking** `y-break`: `inset` values renamed from `"sm"` / `"md"` / `"lg"` to `"small"` / `"medium"` / `"large"` to match the size scale used by every other component. Spacing mapping is unchanged (`small` → `--spacing-x-small`, `medium` → `--spacing-medium`, `large` → `--spacing-x-large`), so visual output is identical after the find-and-replace.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- `y-button` — no longer throws when `color` is set to an unrecognized value that isn't a valid custom color; it now falls back to the `base` theme instead of crashing while reading the color-token map.
|
|
67
|
+
|
|
68
|
+
- `y-droplist` — touch drag now works reliably on iOS Safari and Chrome Android. `touch-action: none` is applied to the press target at decoration time instead of from inside `pointerdown`, so mobile browsers stop preempting the press as a scroll gesture.
|
|
69
|
+
|
|
70
|
+
- Corrected the arrow direction on the `left-from-bracket`, `right-from-bracket`, and `left-to-bracket` icons, which previously pointed the wrong way relative to their bracket (the `*-from-*` arrows now exit the bracket and the `*-to-*` arrow enters it).
|
|
71
|
+
|
|
72
|
+
## [0.5.0] - 2026-05-25
|
|
35
73
|
|
|
36
74
|
### Added
|
|
37
75
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -87,7 +87,7 @@ if (this.querySelector('[slot="icon"]')) { ... }
|
|
|
87
87
|
|
|
88
88
|
The DOM-element, icon, and slot rules above exist primarily to keep user-controlled values out of `innerHTML` template strings, where attribute interpolation can break out of the surrounding quote. Building the shadow tree with `_el` and writing values through `setAttribute` makes that breakout impossible — the entire string becomes a single attribute value, no matter what quotes or angle brackets it contains. Two shared helpers cover the cases where `_el` alone isn't enough:
|
|
89
89
|
|
|
90
|
-
- **CSS color literals (`isSafeCssColor` from `src/modules/helpers.js`).** When a component paints a user-supplied color into any CSS context — an inline `style` attribute, a `<style>` block built via `replaceSync`, or a CSS variable — gate the value through `isSafeCssColor` first. It accepts
|
|
90
|
+
- **CSS color literals (`isSafeCssColor` from `src/modules/helpers.js`).** When a component paints a user-supplied color into any CSS context — an inline `style` attribute, a `<style>` block built via `replaceSync`, or a CSS variable — gate the value through `isSafeCssColor` first. It accepts `#hex` and the browser-native color functions (`rgb()`/`rgba()`, `hsl()`/`hsla()`, `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, `color()`); the function body may contain only the characters those forms use, so named colors, `var(...)`, `currentColor`, nested functions, and anything with semicolons, braces, or angle brackets are rejected. Fall back to a semantic theme default when the check fails. `y-badge` and `y-select` (per-option `color`) follow this pattern.
|
|
91
91
|
|
|
92
92
|
- **SVG markup (`getSanitizedIcon` / `sanitizeSvg` from `src/modules/svg-sanitizer.js`).** Any time you would render an SVG that originated outside the bundled icon set — e.g. anything coming back from `getIcon(name)` where `name` could resolve to a `registerIcon` payload — run it through the shared sanitizer first. `<y-icon>` is the simplest path; if you need raw markup, call `getSanitizedIcon(name)` and inject the returned string. The sanitizer strips every element and attribute outside the SVG allowlist (no `<script>`, no `onload`, no `xlink:href`, …) and memoizes results per icon name.
|
|
93
93
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { contrastTextColor, createElement, resolveAnchor, isNavItemActive, buildNavItemIcon, navigateFrom } from '../../modules/helpers.js';
|
|
1
|
+
import { contrastTextColor, isSafeCssColor, createElement, resolveAnchor, isNavItemActive, buildNavItemIcon, navigateFrom } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
class YumeButton extends HTMLElement {
|
|
4
4
|
static get observedAttributes() {
|
|
@@ -519,12 +519,12 @@ class YumeButton extends HTMLElement {
|
|
|
519
519
|
const { color, size, styleType } = this;
|
|
520
520
|
const colorVars = this._getColorVarsMap();
|
|
521
521
|
|
|
522
|
-
if (!colorVars[color] &&
|
|
522
|
+
if (!colorVars[color] && isSafeCssColor(color)) {
|
|
523
523
|
this._applyCustomColorStyles(color, styleType, size);
|
|
524
524
|
return;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
const vars = colorVars[color];
|
|
527
|
+
const vars = colorVars[color] || colorVars.base;
|
|
528
528
|
|
|
529
529
|
const styleVars = {
|
|
530
530
|
outlined: {
|
|
@@ -784,7 +784,11 @@ class YumeIcon extends HTMLElement {
|
|
|
784
784
|
this.setAttribute("size", val);
|
|
785
785
|
}
|
|
786
786
|
|
|
787
|
-
/**
|
|
787
|
+
/**
|
|
788
|
+
* Weight: "x-thin" | "thin" | "regular" (default) | "thick" | "x-thick" set
|
|
789
|
+
* the stroke width of line icons; "filled" swaps in the filled variant
|
|
790
|
+
* (falling back to the line icon when no filled variant is registered).
|
|
791
|
+
*/
|
|
788
792
|
get weight() {
|
|
789
793
|
return this.getAttribute("weight") || "regular";
|
|
790
794
|
}
|
|
@@ -798,9 +802,10 @@ class YumeIcon extends HTMLElement {
|
|
|
798
802
|
// -------------------------------------------------------------------------
|
|
799
803
|
|
|
800
804
|
render() {
|
|
801
|
-
const svg = getSanitizedIcon(this.
|
|
805
|
+
const svg = getSanitizedIcon(this._resolveIconName());
|
|
802
806
|
const sizeVal = this._getSize(this.size);
|
|
803
807
|
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
808
|
+
// "filled" maps to no stroke width, so the filled variant ignores weight.
|
|
804
809
|
const weightVal = this._getWeight(this.weight);
|
|
805
810
|
|
|
806
811
|
this._updateAria();
|
|
@@ -809,7 +814,7 @@ class YumeIcon extends HTMLElement {
|
|
|
809
814
|
wrapper.innerHTML = svg;
|
|
810
815
|
|
|
811
816
|
this.shadowRoot.adoptedStyleSheets = [
|
|
812
|
-
this._buildStyleSheet(sizeVal, colorVal, weightVal),
|
|
817
|
+
this._buildStyleSheet(sizeVal, colorVal, weightVal, this.weight === "filled"),
|
|
813
818
|
];
|
|
814
819
|
this.shadowRoot.replaceChildren(wrapper);
|
|
815
820
|
}
|
|
@@ -818,7 +823,7 @@ class YumeIcon extends HTMLElement {
|
|
|
818
823
|
// Private
|
|
819
824
|
// -------------------------------------------------------------------------
|
|
820
825
|
|
|
821
|
-
_buildStyleSheet(sizeVal, colorVal, weightVal) {
|
|
826
|
+
_buildStyleSheet(sizeVal, colorVal, weightVal, isFilled = false) {
|
|
822
827
|
const sheet = new CSSStyleSheet();
|
|
823
828
|
sheet.replaceSync(`
|
|
824
829
|
:host {
|
|
@@ -834,6 +839,12 @@ class YumeIcon extends HTMLElement {
|
|
|
834
839
|
width: 100%;
|
|
835
840
|
height: 100%;
|
|
836
841
|
}
|
|
842
|
+
${isFilled ? `.icon-wrapper svg {
|
|
843
|
+
/* Line icons render a ~1px stroke that extends past their path;
|
|
844
|
+
scale filled icons up so they read at the same optical size. */
|
|
845
|
+
transform: scale(1.1);
|
|
846
|
+
transform-origin: center;
|
|
847
|
+
}` : ""}
|
|
837
848
|
${this._getWeightCSS(weightVal)}
|
|
838
849
|
`);
|
|
839
850
|
return sheet;
|
|
@@ -850,14 +861,7 @@ class YumeIcon extends HTMLElement {
|
|
|
850
861
|
help: "var(--help-content--, #5405ff)",
|
|
851
862
|
};
|
|
852
863
|
if (map[color]) return map[color];
|
|
853
|
-
if (
|
|
854
|
-
color &&
|
|
855
|
-
(color.startsWith("#") ||
|
|
856
|
-
color.startsWith("rgb") ||
|
|
857
|
-
color.startsWith("hsl"))
|
|
858
|
-
) {
|
|
859
|
-
return color;
|
|
860
|
-
}
|
|
864
|
+
if (isSafeCssColor(color)) return color;
|
|
861
865
|
return map.base;
|
|
862
866
|
}
|
|
863
867
|
|
|
@@ -889,6 +893,14 @@ class YumeIcon extends HTMLElement {
|
|
|
889
893
|
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`;
|
|
890
894
|
}
|
|
891
895
|
|
|
896
|
+
_resolveIconName() {
|
|
897
|
+
if (this.weight === "filled") {
|
|
898
|
+
const filled = `${this.name}-fill`;
|
|
899
|
+
if (getSanitizedIcon(filled)) return filled;
|
|
900
|
+
}
|
|
901
|
+
return this.name;
|
|
902
|
+
}
|
|
903
|
+
|
|
892
904
|
_updateAria() {
|
|
893
905
|
if (this.label) {
|
|
894
906
|
this.setAttribute("role", "img");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { contrastTextColor, createElement } from '../../modules/helpers.js';
|
|
1
|
+
import { contrastTextColor, isSafeCssColor, createElement } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
class YumeButton extends HTMLElement {
|
|
4
4
|
static get observedAttributes() {
|
|
@@ -519,12 +519,12 @@ class YumeButton extends HTMLElement {
|
|
|
519
519
|
const { color, size, styleType } = this;
|
|
520
520
|
const colorVars = this._getColorVarsMap();
|
|
521
521
|
|
|
522
|
-
if (!colorVars[color] &&
|
|
522
|
+
if (!colorVars[color] && isSafeCssColor(color)) {
|
|
523
523
|
this._applyCustomColorStyles(color, styleType, size);
|
|
524
524
|
return;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
const vars = colorVars[color];
|
|
527
|
+
const vars = colorVars[color] || colorVars.base;
|
|
528
528
|
|
|
529
529
|
const styleVars = {
|
|
530
530
|
outlined: {
|
|
@@ -784,7 +784,11 @@ class YumeIcon extends HTMLElement {
|
|
|
784
784
|
this.setAttribute("size", val);
|
|
785
785
|
}
|
|
786
786
|
|
|
787
|
-
/**
|
|
787
|
+
/**
|
|
788
|
+
* Weight: "x-thin" | "thin" | "regular" (default) | "thick" | "x-thick" set
|
|
789
|
+
* the stroke width of line icons; "filled" swaps in the filled variant
|
|
790
|
+
* (falling back to the line icon when no filled variant is registered).
|
|
791
|
+
*/
|
|
788
792
|
get weight() {
|
|
789
793
|
return this.getAttribute("weight") || "regular";
|
|
790
794
|
}
|
|
@@ -798,9 +802,10 @@ class YumeIcon extends HTMLElement {
|
|
|
798
802
|
// -------------------------------------------------------------------------
|
|
799
803
|
|
|
800
804
|
render() {
|
|
801
|
-
const svg = getSanitizedIcon(this.
|
|
805
|
+
const svg = getSanitizedIcon(this._resolveIconName());
|
|
802
806
|
const sizeVal = this._getSize(this.size);
|
|
803
807
|
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
808
|
+
// "filled" maps to no stroke width, so the filled variant ignores weight.
|
|
804
809
|
const weightVal = this._getWeight(this.weight);
|
|
805
810
|
|
|
806
811
|
this._updateAria();
|
|
@@ -809,7 +814,7 @@ class YumeIcon extends HTMLElement {
|
|
|
809
814
|
wrapper.innerHTML = svg;
|
|
810
815
|
|
|
811
816
|
this.shadowRoot.adoptedStyleSheets = [
|
|
812
|
-
this._buildStyleSheet(sizeVal, colorVal, weightVal),
|
|
817
|
+
this._buildStyleSheet(sizeVal, colorVal, weightVal, this.weight === "filled"),
|
|
813
818
|
];
|
|
814
819
|
this.shadowRoot.replaceChildren(wrapper);
|
|
815
820
|
}
|
|
@@ -818,7 +823,7 @@ class YumeIcon extends HTMLElement {
|
|
|
818
823
|
// Private
|
|
819
824
|
// -------------------------------------------------------------------------
|
|
820
825
|
|
|
821
|
-
_buildStyleSheet(sizeVal, colorVal, weightVal) {
|
|
826
|
+
_buildStyleSheet(sizeVal, colorVal, weightVal, isFilled = false) {
|
|
822
827
|
const sheet = new CSSStyleSheet();
|
|
823
828
|
sheet.replaceSync(`
|
|
824
829
|
:host {
|
|
@@ -834,6 +839,12 @@ class YumeIcon extends HTMLElement {
|
|
|
834
839
|
width: 100%;
|
|
835
840
|
height: 100%;
|
|
836
841
|
}
|
|
842
|
+
${isFilled ? `.icon-wrapper svg {
|
|
843
|
+
/* Line icons render a ~1px stroke that extends past their path;
|
|
844
|
+
scale filled icons up so they read at the same optical size. */
|
|
845
|
+
transform: scale(1.1);
|
|
846
|
+
transform-origin: center;
|
|
847
|
+
}` : ""}
|
|
837
848
|
${this._getWeightCSS(weightVal)}
|
|
838
849
|
`);
|
|
839
850
|
return sheet;
|
|
@@ -850,14 +861,7 @@ class YumeIcon extends HTMLElement {
|
|
|
850
861
|
help: "var(--help-content--, #5405ff)",
|
|
851
862
|
};
|
|
852
863
|
if (map[color]) return map[color];
|
|
853
|
-
if (
|
|
854
|
-
color &&
|
|
855
|
-
(color.startsWith("#") ||
|
|
856
|
-
color.startsWith("rgb") ||
|
|
857
|
-
color.startsWith("hsl"))
|
|
858
|
-
) {
|
|
859
|
-
return color;
|
|
860
|
-
}
|
|
864
|
+
if (isSafeCssColor(color)) return color;
|
|
861
865
|
return map.base;
|
|
862
866
|
}
|
|
863
867
|
|
|
@@ -889,6 +893,14 @@ class YumeIcon extends HTMLElement {
|
|
|
889
893
|
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`;
|
|
890
894
|
}
|
|
891
895
|
|
|
896
|
+
_resolveIconName() {
|
|
897
|
+
if (this.weight === "filled") {
|
|
898
|
+
const filled = `${this.name}-fill`;
|
|
899
|
+
if (getSanitizedIcon(filled)) return filled;
|
|
900
|
+
}
|
|
901
|
+
return this.name;
|
|
902
|
+
}
|
|
903
|
+
|
|
892
904
|
_updateAria() {
|
|
893
905
|
if (this.label) {
|
|
894
906
|
this.setAttribute("role", "img");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement } from '../../modules/helpers.js';
|
|
1
|
+
import { createElement, isSafeCssColor } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
@@ -226,7 +226,11 @@ class YumeIcon extends HTMLElement {
|
|
|
226
226
|
this.setAttribute("size", val);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* Weight: "x-thin" | "thin" | "regular" (default) | "thick" | "x-thick" set
|
|
231
|
+
* the stroke width of line icons; "filled" swaps in the filled variant
|
|
232
|
+
* (falling back to the line icon when no filled variant is registered).
|
|
233
|
+
*/
|
|
230
234
|
get weight() {
|
|
231
235
|
return this.getAttribute("weight") || "regular";
|
|
232
236
|
}
|
|
@@ -240,9 +244,10 @@ class YumeIcon extends HTMLElement {
|
|
|
240
244
|
// -------------------------------------------------------------------------
|
|
241
245
|
|
|
242
246
|
render() {
|
|
243
|
-
const svg = getSanitizedIcon(this.
|
|
247
|
+
const svg = getSanitizedIcon(this._resolveIconName());
|
|
244
248
|
const sizeVal = this._getSize(this.size);
|
|
245
249
|
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
250
|
+
// "filled" maps to no stroke width, so the filled variant ignores weight.
|
|
246
251
|
const weightVal = this._getWeight(this.weight);
|
|
247
252
|
|
|
248
253
|
this._updateAria();
|
|
@@ -251,7 +256,7 @@ class YumeIcon extends HTMLElement {
|
|
|
251
256
|
wrapper.innerHTML = svg;
|
|
252
257
|
|
|
253
258
|
this.shadowRoot.adoptedStyleSheets = [
|
|
254
|
-
this._buildStyleSheet(sizeVal, colorVal, weightVal),
|
|
259
|
+
this._buildStyleSheet(sizeVal, colorVal, weightVal, this.weight === "filled"),
|
|
255
260
|
];
|
|
256
261
|
this.shadowRoot.replaceChildren(wrapper);
|
|
257
262
|
}
|
|
@@ -260,7 +265,7 @@ class YumeIcon extends HTMLElement {
|
|
|
260
265
|
// Private
|
|
261
266
|
// -------------------------------------------------------------------------
|
|
262
267
|
|
|
263
|
-
_buildStyleSheet(sizeVal, colorVal, weightVal) {
|
|
268
|
+
_buildStyleSheet(sizeVal, colorVal, weightVal, isFilled = false) {
|
|
264
269
|
const sheet = new CSSStyleSheet();
|
|
265
270
|
sheet.replaceSync(`
|
|
266
271
|
:host {
|
|
@@ -276,6 +281,12 @@ class YumeIcon extends HTMLElement {
|
|
|
276
281
|
width: 100%;
|
|
277
282
|
height: 100%;
|
|
278
283
|
}
|
|
284
|
+
${isFilled ? `.icon-wrapper svg {
|
|
285
|
+
/* Line icons render a ~1px stroke that extends past their path;
|
|
286
|
+
scale filled icons up so they read at the same optical size. */
|
|
287
|
+
transform: scale(1.1);
|
|
288
|
+
transform-origin: center;
|
|
289
|
+
}` : ""}
|
|
279
290
|
${this._getWeightCSS(weightVal)}
|
|
280
291
|
`);
|
|
281
292
|
return sheet;
|
|
@@ -292,14 +303,7 @@ class YumeIcon extends HTMLElement {
|
|
|
292
303
|
help: "var(--help-content--, #5405ff)",
|
|
293
304
|
};
|
|
294
305
|
if (map[color]) return map[color];
|
|
295
|
-
if (
|
|
296
|
-
color &&
|
|
297
|
-
(color.startsWith("#") ||
|
|
298
|
-
color.startsWith("rgb") ||
|
|
299
|
-
color.startsWith("hsl"))
|
|
300
|
-
) {
|
|
301
|
-
return color;
|
|
302
|
-
}
|
|
306
|
+
if (isSafeCssColor(color)) return color;
|
|
303
307
|
return map.base;
|
|
304
308
|
}
|
|
305
309
|
|
|
@@ -331,6 +335,14 @@ class YumeIcon extends HTMLElement {
|
|
|
331
335
|
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`;
|
|
332
336
|
}
|
|
333
337
|
|
|
338
|
+
_resolveIconName() {
|
|
339
|
+
if (this.weight === "filled") {
|
|
340
|
+
const filled = `${this.name}-fill`;
|
|
341
|
+
if (getSanitizedIcon(filled)) return filled;
|
|
342
|
+
}
|
|
343
|
+
return this.name;
|
|
344
|
+
}
|
|
345
|
+
|
|
334
346
|
_updateAria() {
|
|
335
347
|
if (this.label) {
|
|
336
348
|
this.setAttribute("role", "img");
|
|
@@ -9,7 +9,7 @@ export class YumeBreak extends HTMLElement {
|
|
|
9
9
|
/** Convenience icon name rendered in the center when no slotted content is present. */
|
|
10
10
|
get icon(): string;
|
|
11
11
|
set inset(val: string);
|
|
12
|
-
/** Outer end padding token: "none" | "
|
|
12
|
+
/** Outer end padding token: "none" (default) | "small" | "medium" | "large". */
|
|
13
13
|
get inset(): string;
|
|
14
14
|
set label(val: string);
|
|
15
15
|
/** Convenience text label rendered in the center when no slotted content is present. */
|
|
@@ -9,7 +9,7 @@ export class YumeBreak extends HTMLElement {
|
|
|
9
9
|
/** Convenience icon name rendered in the center when no slotted content is present. */
|
|
10
10
|
get icon(): string;
|
|
11
11
|
set inset(val: string);
|
|
12
|
-
/** Outer end padding token: "none" | "
|
|
12
|
+
/** Outer end padding token: "none" (default) | "small" | "medium" | "large". */
|
|
13
13
|
get inset(): string;
|
|
14
14
|
set label(val: string);
|
|
15
15
|
/** Convenience text label rendered in the center when no slotted content is present. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement } from '../../modules/helpers.js';
|
|
1
|
+
import { createElement, isSafeCssColor } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
@@ -226,7 +226,11 @@ class YumeIcon extends HTMLElement {
|
|
|
226
226
|
this.setAttribute("size", val);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* Weight: "x-thin" | "thin" | "regular" (default) | "thick" | "x-thick" set
|
|
231
|
+
* the stroke width of line icons; "filled" swaps in the filled variant
|
|
232
|
+
* (falling back to the line icon when no filled variant is registered).
|
|
233
|
+
*/
|
|
230
234
|
get weight() {
|
|
231
235
|
return this.getAttribute("weight") || "regular";
|
|
232
236
|
}
|
|
@@ -240,9 +244,10 @@ class YumeIcon extends HTMLElement {
|
|
|
240
244
|
// -------------------------------------------------------------------------
|
|
241
245
|
|
|
242
246
|
render() {
|
|
243
|
-
const svg = getSanitizedIcon(this.
|
|
247
|
+
const svg = getSanitizedIcon(this._resolveIconName());
|
|
244
248
|
const sizeVal = this._getSize(this.size);
|
|
245
249
|
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
250
|
+
// "filled" maps to no stroke width, so the filled variant ignores weight.
|
|
246
251
|
const weightVal = this._getWeight(this.weight);
|
|
247
252
|
|
|
248
253
|
this._updateAria();
|
|
@@ -251,7 +256,7 @@ class YumeIcon extends HTMLElement {
|
|
|
251
256
|
wrapper.innerHTML = svg;
|
|
252
257
|
|
|
253
258
|
this.shadowRoot.adoptedStyleSheets = [
|
|
254
|
-
this._buildStyleSheet(sizeVal, colorVal, weightVal),
|
|
259
|
+
this._buildStyleSheet(sizeVal, colorVal, weightVal, this.weight === "filled"),
|
|
255
260
|
];
|
|
256
261
|
this.shadowRoot.replaceChildren(wrapper);
|
|
257
262
|
}
|
|
@@ -260,7 +265,7 @@ class YumeIcon extends HTMLElement {
|
|
|
260
265
|
// Private
|
|
261
266
|
// -------------------------------------------------------------------------
|
|
262
267
|
|
|
263
|
-
_buildStyleSheet(sizeVal, colorVal, weightVal) {
|
|
268
|
+
_buildStyleSheet(sizeVal, colorVal, weightVal, isFilled = false) {
|
|
264
269
|
const sheet = new CSSStyleSheet();
|
|
265
270
|
sheet.replaceSync(`
|
|
266
271
|
:host {
|
|
@@ -276,6 +281,12 @@ class YumeIcon extends HTMLElement {
|
|
|
276
281
|
width: 100%;
|
|
277
282
|
height: 100%;
|
|
278
283
|
}
|
|
284
|
+
${isFilled ? `.icon-wrapper svg {
|
|
285
|
+
/* Line icons render a ~1px stroke that extends past their path;
|
|
286
|
+
scale filled icons up so they read at the same optical size. */
|
|
287
|
+
transform: scale(1.1);
|
|
288
|
+
transform-origin: center;
|
|
289
|
+
}` : ""}
|
|
279
290
|
${this._getWeightCSS(weightVal)}
|
|
280
291
|
`);
|
|
281
292
|
return sheet;
|
|
@@ -292,14 +303,7 @@ class YumeIcon extends HTMLElement {
|
|
|
292
303
|
help: "var(--help-content--, #5405ff)",
|
|
293
304
|
};
|
|
294
305
|
if (map[color]) return map[color];
|
|
295
|
-
if (
|
|
296
|
-
color &&
|
|
297
|
-
(color.startsWith("#") ||
|
|
298
|
-
color.startsWith("rgb") ||
|
|
299
|
-
color.startsWith("hsl"))
|
|
300
|
-
) {
|
|
301
|
-
return color;
|
|
302
|
-
}
|
|
306
|
+
if (isSafeCssColor(color)) return color;
|
|
303
307
|
return map.base;
|
|
304
308
|
}
|
|
305
309
|
|
|
@@ -331,6 +335,14 @@ class YumeIcon extends HTMLElement {
|
|
|
331
335
|
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`;
|
|
332
336
|
}
|
|
333
337
|
|
|
338
|
+
_resolveIconName() {
|
|
339
|
+
if (this.weight === "filled") {
|
|
340
|
+
const filled = `${this.name}-fill`;
|
|
341
|
+
if (getSanitizedIcon(filled)) return filled;
|
|
342
|
+
}
|
|
343
|
+
return this.name;
|
|
344
|
+
}
|
|
345
|
+
|
|
334
346
|
_updateAria() {
|
|
335
347
|
if (this.label) {
|
|
336
348
|
this.setAttribute("role", "img");
|
|
@@ -351,13 +363,13 @@ if (!customElements.get("y-icon")) {
|
|
|
351
363
|
const VALID_ORIENTATIONS = new Set(["horizontal", "vertical"]);
|
|
352
364
|
const VALID_ALIGNMENTS = new Set(["start", "center", "end"]);
|
|
353
365
|
const VALID_VARIANTS = new Set(["solid", "dashed", "dotted"]);
|
|
354
|
-
const VALID_INSETS = new Set(["none", "
|
|
366
|
+
const VALID_INSETS = new Set(["none", "small", "medium", "large"]);
|
|
355
367
|
|
|
356
368
|
const INSET_TOKENS = {
|
|
357
369
|
none: "0",
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
370
|
+
small: "var(--spacing-x-small)",
|
|
371
|
+
medium: "var(--spacing-medium)",
|
|
372
|
+
large: "var(--spacing-x-large)",
|
|
361
373
|
};
|
|
362
374
|
|
|
363
375
|
class YumeBreak extends HTMLElement {
|
|
@@ -409,7 +421,7 @@ class YumeBreak extends HTMLElement {
|
|
|
409
421
|
else this.removeAttribute("icon");
|
|
410
422
|
}
|
|
411
423
|
|
|
412
|
-
/** Outer end padding token: "none" | "
|
|
424
|
+
/** Outer end padding token: "none" (default) | "small" | "medium" | "large". */
|
|
413
425
|
get inset() {
|
|
414
426
|
const v = this.getAttribute("inset");
|
|
415
427
|
return VALID_INSETS.has(v) ? v : "none";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { contrastTextColor } from '../../modules/helpers.js';
|
|
1
|
+
import { contrastTextColor, isSafeCssColor } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
class YumeButton extends HTMLElement {
|
|
4
4
|
static get observedAttributes() {
|
|
@@ -519,12 +519,12 @@ class YumeButton extends HTMLElement {
|
|
|
519
519
|
const { color, size, styleType } = this;
|
|
520
520
|
const colorVars = this._getColorVarsMap();
|
|
521
521
|
|
|
522
|
-
if (!colorVars[color] &&
|
|
522
|
+
if (!colorVars[color] && isSafeCssColor(color)) {
|
|
523
523
|
this._applyCustomColorStyles(color, styleType, size);
|
|
524
524
|
return;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
const vars = colorVars[color];
|
|
527
|
+
const vars = colorVars[color] || colorVars.base;
|
|
528
528
|
|
|
529
529
|
const styleVars = {
|
|
530
530
|
outlined: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement } from '../../modules/helpers.js';
|
|
1
|
+
import { createElement, isSafeCssColor } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
@@ -226,7 +226,11 @@ class YumeIcon extends HTMLElement {
|
|
|
226
226
|
this.setAttribute("size", val);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* Weight: "x-thin" | "thin" | "regular" (default) | "thick" | "x-thick" set
|
|
231
|
+
* the stroke width of line icons; "filled" swaps in the filled variant
|
|
232
|
+
* (falling back to the line icon when no filled variant is registered).
|
|
233
|
+
*/
|
|
230
234
|
get weight() {
|
|
231
235
|
return this.getAttribute("weight") || "regular";
|
|
232
236
|
}
|
|
@@ -240,9 +244,10 @@ class YumeIcon extends HTMLElement {
|
|
|
240
244
|
// -------------------------------------------------------------------------
|
|
241
245
|
|
|
242
246
|
render() {
|
|
243
|
-
const svg = getSanitizedIcon(this.
|
|
247
|
+
const svg = getSanitizedIcon(this._resolveIconName());
|
|
244
248
|
const sizeVal = this._getSize(this.size);
|
|
245
249
|
const colorVal = this.color ? this._getColor(this.color) : "inherit";
|
|
250
|
+
// "filled" maps to no stroke width, so the filled variant ignores weight.
|
|
246
251
|
const weightVal = this._getWeight(this.weight);
|
|
247
252
|
|
|
248
253
|
this._updateAria();
|
|
@@ -251,7 +256,7 @@ class YumeIcon extends HTMLElement {
|
|
|
251
256
|
wrapper.innerHTML = svg;
|
|
252
257
|
|
|
253
258
|
this.shadowRoot.adoptedStyleSheets = [
|
|
254
|
-
this._buildStyleSheet(sizeVal, colorVal, weightVal),
|
|
259
|
+
this._buildStyleSheet(sizeVal, colorVal, weightVal, this.weight === "filled"),
|
|
255
260
|
];
|
|
256
261
|
this.shadowRoot.replaceChildren(wrapper);
|
|
257
262
|
}
|
|
@@ -260,7 +265,7 @@ class YumeIcon extends HTMLElement {
|
|
|
260
265
|
// Private
|
|
261
266
|
// -------------------------------------------------------------------------
|
|
262
267
|
|
|
263
|
-
_buildStyleSheet(sizeVal, colorVal, weightVal) {
|
|
268
|
+
_buildStyleSheet(sizeVal, colorVal, weightVal, isFilled = false) {
|
|
264
269
|
const sheet = new CSSStyleSheet();
|
|
265
270
|
sheet.replaceSync(`
|
|
266
271
|
:host {
|
|
@@ -276,6 +281,12 @@ class YumeIcon extends HTMLElement {
|
|
|
276
281
|
width: 100%;
|
|
277
282
|
height: 100%;
|
|
278
283
|
}
|
|
284
|
+
${isFilled ? `.icon-wrapper svg {
|
|
285
|
+
/* Line icons render a ~1px stroke that extends past their path;
|
|
286
|
+
scale filled icons up so they read at the same optical size. */
|
|
287
|
+
transform: scale(1.1);
|
|
288
|
+
transform-origin: center;
|
|
289
|
+
}` : ""}
|
|
279
290
|
${this._getWeightCSS(weightVal)}
|
|
280
291
|
`);
|
|
281
292
|
return sheet;
|
|
@@ -292,14 +303,7 @@ class YumeIcon extends HTMLElement {
|
|
|
292
303
|
help: "var(--help-content--, #5405ff)",
|
|
293
304
|
};
|
|
294
305
|
if (map[color]) return map[color];
|
|
295
|
-
if (
|
|
296
|
-
color &&
|
|
297
|
-
(color.startsWith("#") ||
|
|
298
|
-
color.startsWith("rgb") ||
|
|
299
|
-
color.startsWith("hsl"))
|
|
300
|
-
) {
|
|
301
|
-
return color;
|
|
302
|
-
}
|
|
306
|
+
if (isSafeCssColor(color)) return color;
|
|
303
307
|
return map.base;
|
|
304
308
|
}
|
|
305
309
|
|
|
@@ -331,6 +335,14 @@ class YumeIcon extends HTMLElement {
|
|
|
331
335
|
.icon-wrapper svg * { stroke-width: ${weightVal} !important; }`;
|
|
332
336
|
}
|
|
333
337
|
|
|
338
|
+
_resolveIconName() {
|
|
339
|
+
if (this.weight === "filled") {
|
|
340
|
+
const filled = `${this.name}-fill`;
|
|
341
|
+
if (getSanitizedIcon(filled)) return filled;
|
|
342
|
+
}
|
|
343
|
+
return this.name;
|
|
344
|
+
}
|
|
345
|
+
|
|
334
346
|
_updateAria() {
|
|
335
347
|
if (this.label) {
|
|
336
348
|
this.setAttribute("role", "img");
|