@waggylabs/yumekit 0.5.1-beta.8 → 0.5.1-beta.9
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 +8 -2
- package/dist/components/y-code/y-code.d.ts +2 -0
- package/dist/components/y-code.d.ts +2 -0
- package/dist/components/y-code.js +81 -3
- package/dist/components/y-color.js +16 -2
- package/dist/components/y-colorpicker.js +16 -2
- package/dist/components/y-data-grid.js +186 -48
- package/dist/components/y-date.js +572 -39
- package/dist/components/y-datepicker/y-datepicker.d.ts +9 -0
- package/dist/components/y-datepicker.d.ts +9 -0
- package/dist/components/y-datepicker.js +572 -39
- package/dist/components/y-drawer.js +4 -1
- package/dist/components/y-help.js +3 -11
- package/dist/components/y-paginator.js +16 -2
- package/dist/components/y-popover.js +2 -10
- package/dist/components/y-select.js +16 -2
- package/dist/components/y-sidebar/y-sidebar.d.ts +9 -0
- package/dist/components/y-sidebar.d.ts +9 -0
- package/dist/components/y-sidebar.js +24 -1
- package/dist/components/y-theme.js +29 -3
- package/dist/index.js +348 -56
- package/dist/modules/helpers.d.ts +14 -0
- package/dist/modules/helpers.js +24 -0
- package/dist/modules/helpers.test.js +46 -0
- package/dist/styles/kepler-amber.css +220 -0
- package/dist/styles/kepler-dark.css +220 -0
- package/dist/styles/kepler-galaxy.css +220 -0
- package/dist/styles/kepler-light.css +220 -0
- package/dist/styles/kepler-matrix.css +220 -0
- package/dist/styles/neobrutalist-dark.css +1 -1
- package/dist/styles/neobrutalist.css +1 -1
- package/dist/styles/slate-dark.css +215 -0
- package/dist/styles/slate-light.css +215 -0
- package/dist/styles/variables.css +19 -0
- package/dist/yumekit.min.js +1 -1
- package/llm.txt +12 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -37,7 +37,7 @@ Delete any empty sections before publishing.
|
|
|
37
37
|
|
|
38
38
|
- `y-checkbox` / `y-radio` — checked-state color hooks so themes can fill the control on selection: `--component-checkbox-checked-background` / `-checked-border-color` / `-checked-icon-color` and `--component-radio-background` / `-checked-background` / `-checked-border-color` / `-checked-dot-color`. Each falls back to the matching unchecked value, so existing themes are unchanged. The design-system themes now use them — checked checkboxes (and Shadcn/Bootstrap/Primer radios) fill with the primary color and draw the check/dot in the primary-inverse color, matching each system's native look.
|
|
39
39
|
|
|
40
|
-
- Several theme color variations including the `Catppuccin` and `Nord` color schemes, two `
|
|
40
|
+
- Several theme color variations including `Slate`, `Rose`, the `Catppuccin` and `Nord` color schemes, two `Neobrutalist` themes, as well as several new themes based on some of the most popular design systems. This includes Material, Carbon, Ant, Shadcn, Primer, Bootstrap, and a few throwback themes inspired by Yumekit's spiritual ancestor: `Kepler UI`.
|
|
41
41
|
|
|
42
42
|
- `y-tabs` — new `variant="accent"` style: minimal tabs where the active tab shows a primary-colored indicator border on its content-facing edge (bottom for top tabs, etc.). The default bordered style is unchanged. Adds `--component-tabs-accent-width` (indicator thickness). The border-width token is normalized to `--component-tabs-border-width` (matching `--component-tabs-border-color`); the legacy `--component-tab-border-width` is still honored as a fallback.
|
|
43
43
|
|
|
@@ -83,11 +83,17 @@ Delete any empty sections before publishing.
|
|
|
83
83
|
|
|
84
84
|
### Fixed
|
|
85
85
|
|
|
86
|
+
- `y-sidebar` — nav/footer icons now stay in the same position between the expanded and collapsed states under themes whose borders use a multi-value width (e.g. the Neobrutalist offset border `2px 2px 6px 2px`). The icon-column width is now derived from the sidebar's resolved horizontal border (flat buttons render no border) instead of an invalid `calc()` that multiplied the multi-value token, which had collapsed the column to `auto` and shifted icons.
|
|
87
|
+
|
|
88
|
+
- `y-drawer` — corners closest to the screen's edge are now squared in all themes.
|
|
89
|
+
|
|
90
|
+
- `y-datepicker` — the month and year pickers were reworked. The month picker (`show-days="false"`) now shows a selectable year dropdown in its header (toggled by `show-years`, omitted when `false`) above the twelve months, and the year picker (`show-months="false"`) now bounds its scrollable year grid with two start/end year inputs. Clicking a month or year now selects it and fires `change`; clicking a year in the year picker no longer shifts the visible range.
|
|
91
|
+
|
|
86
92
|
- `y-theme` — switching themes now clears the previous theme's CSS custom properties from the host before applying the new ones. Variables defined only by the outgoing theme (e.g. a theme-specific token like `--component-tabs-inactive-background`) previously lingered inline and "stuck" until a page reload; they are now removed on every switch.
|
|
87
93
|
|
|
88
94
|
- `y-tabs` — unselected tabs now use a dedicated `--component-tabs-inactive-background` (falling back to `--component-tabs-border-color`, so existing themes are unchanged) instead of always painting the tab background with the border color. This fixes the Neobrutalist themes, where the border color equals the text color and made unselected tab labels unreadable; both Neobrutalist themes now set it to a distinct surface tone.
|
|
89
95
|
|
|
90
|
-
- Portaled overlays now inherit the active theme. `y-help` (tour overlay/tooltip), `y-popover` (`portal` mode), and `y-select` (`portal` mode) previously rendered into `document.body`, escaping the `<y-theme>` subtree that scopes the theme's CSS custom properties — so they fell back to the default un-themed palette. They now mount into the nearest enclosing `<y-theme
|
|
96
|
+
- Portaled overlays now inherit the active theme. `y-help` (tour overlay/tooltip), `y-popover` (`portal` mode), and `y-select` (`portal` mode) previously rendered into `document.body`, escaping the `<y-theme>` subtree that scopes the theme's CSS custom properties — so they fell back to the default un-themed palette. They now mount into the nearest enclosing `<y-theme>`, walking up across shadow boundaries so the fix also applies when the component is used inside another component's shadow root (e.g. `y-data-grid`'s per-column header menus), falling back to `document.body` when there is no theme ancestor. The portaled surface matches the active theme and tracks live theme switches. Keep the component inside your `<y-theme>`.
|
|
91
97
|
|
|
92
98
|
- Form field components now share one field background. `y-select` (trigger and dropdown) used `base.background.app` while `y-input` / `y-textarea` / `y-color` / `y-date` used `base.background.component`; `select.background` is now `base.background.component` across all themes, so fields match when placed together on a form (most visible in the Material and Carbon themes). The select dropdown panel now also matches menus/popovers.
|
|
93
99
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class YumeCode extends HTMLElement {
|
|
2
2
|
static get observedAttributes(): string[];
|
|
3
3
|
_copiedTimer: any;
|
|
4
|
+
_lineFlashTimer: any;
|
|
4
5
|
_isExpanded: boolean;
|
|
5
6
|
connectedCallback(): void;
|
|
6
7
|
disconnectedCallback(): void;
|
|
@@ -63,6 +64,7 @@ export class YumeCode extends HTMLElement {
|
|
|
63
64
|
_readTemplateSource(tpl: any): any;
|
|
64
65
|
_render(): void;
|
|
65
66
|
_sanitizedHighlightedLines(): any[][];
|
|
67
|
+
_showLineCopiedBadge(line: any): HTMLElement;
|
|
66
68
|
_tokenizedLines(source: any): {
|
|
67
69
|
spans: string[];
|
|
68
70
|
text: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class YumeCode extends HTMLElement {
|
|
2
2
|
static get observedAttributes(): string[];
|
|
3
3
|
_copiedTimer: any;
|
|
4
|
+
_lineFlashTimer: any;
|
|
4
5
|
_isExpanded: boolean;
|
|
5
6
|
connectedCallback(): void;
|
|
6
7
|
disconnectedCallback(): void;
|
|
@@ -63,6 +64,7 @@ export class YumeCode extends HTMLElement {
|
|
|
63
64
|
_readTemplateSource(tpl: any): any;
|
|
64
65
|
_render(): void;
|
|
65
66
|
_sanitizedHighlightedLines(): any[][];
|
|
67
|
+
_showLineCopiedBadge(line: any): HTMLElement;
|
|
66
68
|
_tokenizedLines(source: any): {
|
|
67
69
|
spans: string[];
|
|
68
70
|
text: string;
|
|
@@ -1681,6 +1681,7 @@ class YumeCode extends HTMLElement {
|
|
|
1681
1681
|
super();
|
|
1682
1682
|
this.attachShadow({ mode: "open" });
|
|
1683
1683
|
this._copiedTimer = null;
|
|
1684
|
+
this._lineFlashTimer = null;
|
|
1684
1685
|
this._isExpanded = false;
|
|
1685
1686
|
}
|
|
1686
1687
|
|
|
@@ -1694,6 +1695,10 @@ class YumeCode extends HTMLElement {
|
|
|
1694
1695
|
clearTimeout(this._copiedTimer);
|
|
1695
1696
|
this._copiedTimer = null;
|
|
1696
1697
|
}
|
|
1698
|
+
if (this._lineFlashTimer) {
|
|
1699
|
+
clearTimeout(this._lineFlashTimer);
|
|
1700
|
+
this._lineFlashTimer = null;
|
|
1701
|
+
}
|
|
1697
1702
|
}
|
|
1698
1703
|
|
|
1699
1704
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -2017,6 +2022,7 @@ class YumeCode extends HTMLElement {
|
|
|
2017
2022
|
padding: 0 var(--spacing-small, 8px);
|
|
2018
2023
|
white-space: ${this.wrap ? "pre-wrap" : "pre"};
|
|
2019
2024
|
word-break: ${this.wrap ? "break-word" : "normal"};
|
|
2025
|
+
transition: background 0.4s ease;
|
|
2020
2026
|
}
|
|
2021
2027
|
|
|
2022
2028
|
.line-number {
|
|
@@ -2042,9 +2048,36 @@ class YumeCode extends HTMLElement {
|
|
|
2042
2048
|
outline: 2px solid var(--primary-content--, #1976d2);
|
|
2043
2049
|
outline-offset: -2px;
|
|
2044
2050
|
}
|
|
2045
|
-
.line.is-copied
|
|
2051
|
+
.line.is-copied,
|
|
2052
|
+
:host([line-numbers]) .line[role="button"].is-copied {
|
|
2046
2053
|
background: var(--component-code-line-copied-bg, var(--success-content-light, rgba(46, 125, 50, 0.12)));
|
|
2047
|
-
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
/* Floating "Copied!" pill anchored to the copied line. Its top is
|
|
2057
|
+
set in JS; it's pinned to the right edge of the viewport so it
|
|
2058
|
+
stays visible regardless of horizontal scroll. */
|
|
2059
|
+
.line-copied-badge {
|
|
2060
|
+
position: absolute;
|
|
2061
|
+
right: var(--spacing-small, 8px);
|
|
2062
|
+
z-index: 2;
|
|
2063
|
+
pointer-events: none;
|
|
2064
|
+
padding: 0 var(--spacing-x-small, 5px);
|
|
2065
|
+
border-radius: var(--radii-full, 999px);
|
|
2066
|
+
background: var(--component-code-line-copied-badge-bg, var(--success-content--, #2e7d32));
|
|
2067
|
+
color: var(--component-code-line-copied-badge-text-color, var(--success-content-inverse, #fff));
|
|
2068
|
+
font-family: var(--font-family-body, sans-serif);
|
|
2069
|
+
font-size: 0.625rem;
|
|
2070
|
+
font-weight: var(--font-weight-body, 400);
|
|
2071
|
+
line-height: 1.6;
|
|
2072
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
|
|
2073
|
+
animation: y-code-copied-badge-in 0.15s ease;
|
|
2074
|
+
}
|
|
2075
|
+
@keyframes y-code-copied-badge-in {
|
|
2076
|
+
from { opacity: 0; transform: translateY(-3px); }
|
|
2077
|
+
to { opacity: 1; transform: none; }
|
|
2078
|
+
}
|
|
2079
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2080
|
+
.line-copied-badge { animation: none; }
|
|
2048
2081
|
}
|
|
2049
2082
|
|
|
2050
2083
|
.line-content { flex: 1; min-width: 0; }
|
|
@@ -2180,8 +2213,22 @@ class YumeCode extends HTMLElement {
|
|
|
2180
2213
|
`.line[data-line="${index}"]`,
|
|
2181
2214
|
);
|
|
2182
2215
|
if (!line) return;
|
|
2216
|
+
|
|
2217
|
+
if (this._lineFlashTimer) clearTimeout(this._lineFlashTimer);
|
|
2218
|
+
// Clear any in-progress flash first so rapid successive copies don't
|
|
2219
|
+
// strand the highlight on a previously-copied line.
|
|
2220
|
+
this.shadowRoot
|
|
2221
|
+
.querySelectorAll(".line.is-copied")
|
|
2222
|
+
.forEach((el) => el.classList.remove("is-copied"));
|
|
2223
|
+
|
|
2183
2224
|
line.classList.add("is-copied");
|
|
2184
|
-
|
|
2225
|
+
const badge = this._showLineCopiedBadge(line);
|
|
2226
|
+
|
|
2227
|
+
this._lineFlashTimer = setTimeout(() => {
|
|
2228
|
+
this._lineFlashTimer = null;
|
|
2229
|
+
line.classList.remove("is-copied");
|
|
2230
|
+
if (badge) badge.remove();
|
|
2231
|
+
}, 1500);
|
|
2185
2232
|
}
|
|
2186
2233
|
|
|
2187
2234
|
_hasHeaderSlot() {
|
|
@@ -2382,6 +2429,37 @@ class YumeCode extends HTMLElement {
|
|
|
2382
2429
|
return lines;
|
|
2383
2430
|
}
|
|
2384
2431
|
|
|
2432
|
+
_showLineCopiedBadge(line) {
|
|
2433
|
+
const preWrap = this.shadowRoot.querySelector(".pre-wrap");
|
|
2434
|
+
const pre = this.shadowRoot.querySelector("pre.code");
|
|
2435
|
+
if (!preWrap || !pre) return null;
|
|
2436
|
+
|
|
2437
|
+
const stale = preWrap.querySelector(".line-copied-badge");
|
|
2438
|
+
if (stale) stale.remove();
|
|
2439
|
+
|
|
2440
|
+
const badge = createElement(
|
|
2441
|
+
"span",
|
|
2442
|
+
{
|
|
2443
|
+
class: "line-copied-badge",
|
|
2444
|
+
part: "line-copied-badge",
|
|
2445
|
+
role: "status",
|
|
2446
|
+
},
|
|
2447
|
+
[this.copiedLabel],
|
|
2448
|
+
);
|
|
2449
|
+
preWrap.appendChild(badge);
|
|
2450
|
+
|
|
2451
|
+
// `line.offsetTop` is measured against `.pre-wrap` (the nearest
|
|
2452
|
+
// positioned ancestor); subtract the scroll offset so the badge tracks
|
|
2453
|
+
// the copied line when the block is scrolled vertically (max-lines).
|
|
2454
|
+
const top =
|
|
2455
|
+
line.offsetTop -
|
|
2456
|
+
pre.scrollTop +
|
|
2457
|
+
(line.offsetHeight - badge.offsetHeight) / 2;
|
|
2458
|
+
badge.style.top = `${Math.max(top, 0)}px`;
|
|
2459
|
+
|
|
2460
|
+
return badge;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2385
2463
|
_tokenizedLines(source) {
|
|
2386
2464
|
// Run the language tokenizer, then split the flat token stream into
|
|
2387
2465
|
// per-line arrays of `{ spans, text }` so the existing line renderer
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { manageLabelVisibility, createElement, isSafeCssColor, contrastTextColor, parseColorString, rgbToHsv, hsvToRgb, rgbToHex, hsvToHsl, rgbaToHex, clamp, hslToHsv } from '../../modules/helpers.js';
|
|
1
|
+
import { manageLabelVisibility, createElement, isSafeCssColor, contrastTextColor, resolveThemeMountPoint, parseColorString, rgbToHsv, hsvToRgb, rgbToHex, hsvToHsl, rgbaToHex, clamp, hslToHsv } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
class YumeInput extends HTMLElement {
|
|
4
4
|
static formAssociated = true;
|
|
@@ -1616,6 +1616,20 @@ class YumeSelect extends HTMLElement {
|
|
|
1616
1616
|
|
|
1617
1617
|
const portal = document.createElement("div");
|
|
1618
1618
|
portal.className = "y-select-portal";
|
|
1619
|
+
|
|
1620
|
+
// Custom properties set inline on the host don't cascade into the
|
|
1621
|
+
// portal — it mounts under y-theme/body, not as a descendant of this
|
|
1622
|
+
// element. Forward a z-index override so a select opened inside a
|
|
1623
|
+
// higher-stacked context (e.g. a portaled popover) can lift its
|
|
1624
|
+
// dropdown above that context. The dropdown inherits the value through
|
|
1625
|
+
// the portal's shadow boundary.
|
|
1626
|
+
const zOverride = this.style
|
|
1627
|
+
.getPropertyValue("--component-select-z-index")
|
|
1628
|
+
.trim();
|
|
1629
|
+
if (zOverride) {
|
|
1630
|
+
portal.style.setProperty("--component-select-z-index", zOverride);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1619
1633
|
const shadow = portal.attachShadow({ mode: "open" });
|
|
1620
1634
|
shadow.adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets;
|
|
1621
1635
|
|
|
@@ -1640,7 +1654,7 @@ class YumeSelect extends HTMLElement {
|
|
|
1640
1654
|
}
|
|
1641
1655
|
|
|
1642
1656
|
_resolveMountPoint() {
|
|
1643
|
-
return this
|
|
1657
|
+
return resolveThemeMountPoint(this);
|
|
1644
1658
|
}
|
|
1645
1659
|
|
|
1646
1660
|
_openDropdown() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { manageLabelVisibility, createElement, isSafeCssColor, contrastTextColor, parseColorString, rgbToHsv, hsvToRgb, rgbToHex, hsvToHsl, rgbaToHex, clamp, hslToHsv } from '../../modules/helpers.js';
|
|
1
|
+
import { manageLabelVisibility, createElement, isSafeCssColor, contrastTextColor, resolveThemeMountPoint, parseColorString, rgbToHsv, hsvToRgb, rgbToHex, hsvToHsl, rgbaToHex, clamp, hslToHsv } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
class YumeInput extends HTMLElement {
|
|
4
4
|
static formAssociated = true;
|
|
@@ -1616,6 +1616,20 @@ class YumeSelect extends HTMLElement {
|
|
|
1616
1616
|
|
|
1617
1617
|
const portal = document.createElement("div");
|
|
1618
1618
|
portal.className = "y-select-portal";
|
|
1619
|
+
|
|
1620
|
+
// Custom properties set inline on the host don't cascade into the
|
|
1621
|
+
// portal — it mounts under y-theme/body, not as a descendant of this
|
|
1622
|
+
// element. Forward a z-index override so a select opened inside a
|
|
1623
|
+
// higher-stacked context (e.g. a portaled popover) can lift its
|
|
1624
|
+
// dropdown above that context. The dropdown inherits the value through
|
|
1625
|
+
// the portal's shadow boundary.
|
|
1626
|
+
const zOverride = this.style
|
|
1627
|
+
.getPropertyValue("--component-select-z-index")
|
|
1628
|
+
.trim();
|
|
1629
|
+
if (zOverride) {
|
|
1630
|
+
portal.style.setProperty("--component-select-z-index", zOverride);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1619
1633
|
const shadow = portal.attachShadow({ mode: "open" });
|
|
1620
1634
|
shadow.adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets;
|
|
1621
1635
|
|
|
@@ -1640,7 +1654,7 @@ class YumeSelect extends HTMLElement {
|
|
|
1640
1654
|
}
|
|
1641
1655
|
|
|
1642
1656
|
_resolveMountPoint() {
|
|
1643
|
-
return this
|
|
1657
|
+
return resolveThemeMountPoint(this);
|
|
1644
1658
|
}
|
|
1645
1659
|
|
|
1646
1660
|
_openDropdown() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement, isSafeCssColor, manageLabelVisibility, contrastTextColor, getColorVarPair, clamp, measureCSSLength, GAP_TOKEN_MAP } from '../../modules/helpers.js';
|
|
1
|
+
import { createElement, isSafeCssColor, manageLabelVisibility, contrastTextColor, resolveThemeMountPoint, getColorVarPair, clamp, measureCSSLength, GAP_TOKEN_MAP } from '../../modules/helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Icon registry — a runtime map of icon names to SVG markup strings.
|
|
@@ -1875,6 +1875,20 @@ class YumeSelect extends HTMLElement {
|
|
|
1875
1875
|
|
|
1876
1876
|
const portal = document.createElement("div");
|
|
1877
1877
|
portal.className = "y-select-portal";
|
|
1878
|
+
|
|
1879
|
+
// Custom properties set inline on the host don't cascade into the
|
|
1880
|
+
// portal — it mounts under y-theme/body, not as a descendant of this
|
|
1881
|
+
// element. Forward a z-index override so a select opened inside a
|
|
1882
|
+
// higher-stacked context (e.g. a portaled popover) can lift its
|
|
1883
|
+
// dropdown above that context. The dropdown inherits the value through
|
|
1884
|
+
// the portal's shadow boundary.
|
|
1885
|
+
const zOverride = this.style
|
|
1886
|
+
.getPropertyValue("--component-select-z-index")
|
|
1887
|
+
.trim();
|
|
1888
|
+
if (zOverride) {
|
|
1889
|
+
portal.style.setProperty("--component-select-z-index", zOverride);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1878
1892
|
const shadow = portal.attachShadow({ mode: "open" });
|
|
1879
1893
|
shadow.adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets;
|
|
1880
1894
|
|
|
@@ -1899,7 +1913,7 @@ class YumeSelect extends HTMLElement {
|
|
|
1899
1913
|
}
|
|
1900
1914
|
|
|
1901
1915
|
_resolveMountPoint() {
|
|
1902
|
-
return this
|
|
1916
|
+
return resolveThemeMountPoint(this);
|
|
1903
1917
|
}
|
|
1904
1918
|
|
|
1905
1919
|
_openDropdown() {
|
|
@@ -2992,6 +3006,8 @@ class YumeDatepicker extends HTMLElement {
|
|
|
2992
3006
|
this._awaitingEnd = false;
|
|
2993
3007
|
this._startTime = { h: 0, m: 0, s: 0 };
|
|
2994
3008
|
this._endTime = { h: 0, m: 0, s: 0 };
|
|
3009
|
+
this._yearRangeStart = null;
|
|
3010
|
+
this._yearRangeEnd = null;
|
|
2995
3011
|
this._mql = null;
|
|
2996
3012
|
this._isMobile = false;
|
|
2997
3013
|
this._onMediaChange = this._onMediaChange.bind(this);
|
|
@@ -3299,17 +3315,33 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3299
3315
|
});
|
|
3300
3316
|
});
|
|
3301
3317
|
|
|
3318
|
+
root.querySelectorAll(".m-year-sel").forEach((sel) => {
|
|
3319
|
+
sel.addEventListener("change", () => {
|
|
3320
|
+
this._viewDate.setFullYear(parseInt(sel.value));
|
|
3321
|
+
this.render();
|
|
3322
|
+
});
|
|
3323
|
+
});
|
|
3324
|
+
|
|
3302
3325
|
root.querySelectorAll(".month-btn").forEach((btn) => {
|
|
3303
3326
|
btn.addEventListener("click", () => {
|
|
3304
|
-
this.
|
|
3305
|
-
this.
|
|
3327
|
+
const vd = this._viewDateForSide(btn.dataset.side);
|
|
3328
|
+
this._selectMonth(vd.getFullYear(), parseInt(btn.dataset.month));
|
|
3306
3329
|
});
|
|
3307
3330
|
});
|
|
3308
3331
|
|
|
3309
3332
|
root.querySelectorAll(".year-btn").forEach((btn) => {
|
|
3310
|
-
btn.addEventListener("click", () =>
|
|
3311
|
-
this.
|
|
3312
|
-
|
|
3333
|
+
btn.addEventListener("click", () =>
|
|
3334
|
+
this._selectYear(parseInt(btn.dataset.year)),
|
|
3335
|
+
);
|
|
3336
|
+
});
|
|
3337
|
+
|
|
3338
|
+
root.querySelectorAll(".year-range-input").forEach((input) => {
|
|
3339
|
+
input.addEventListener("input", () => {
|
|
3340
|
+
const val = parseInt(input.value, 10);
|
|
3341
|
+
if (isNaN(val)) return;
|
|
3342
|
+
if (input.dataset.bound === "start") this._yearRangeStart = val;
|
|
3343
|
+
else this._yearRangeEnd = val;
|
|
3344
|
+
this._refreshYearGrid();
|
|
3313
3345
|
});
|
|
3314
3346
|
});
|
|
3315
3347
|
|
|
@@ -3406,23 +3438,14 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3406
3438
|
const year = vd.getFullYear();
|
|
3407
3439
|
const month = vd.getMonth();
|
|
3408
3440
|
|
|
3409
|
-
// Month
|
|
3410
|
-
if (!this.showDays) {
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
label = `${minY} – ${maxY}`;
|
|
3418
|
-
}
|
|
3419
|
-
return `
|
|
3420
|
-
<div class="cal-header">
|
|
3421
|
-
<div class="header-selects">
|
|
3422
|
-
<span class="header-label">${label}</span>
|
|
3423
|
-
</div>
|
|
3424
|
-
</div>
|
|
3425
|
-
`;
|
|
3441
|
+
// Month picker: 12-month grid with an optional year dropdown.
|
|
3442
|
+
if (!this.showDays && this.showMonths) {
|
|
3443
|
+
return this._buildMonthPickerHeader(year);
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
// Year picker: start/end year inputs bounding the scrollable grid.
|
|
3447
|
+
if (!this.showDays && !this.showMonths) {
|
|
3448
|
+
return this._buildYearPickerHeader();
|
|
3426
3449
|
}
|
|
3427
3450
|
|
|
3428
3451
|
const showPrev = !isRange || side === "left";
|
|
@@ -3485,6 +3508,27 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3485
3508
|
`;
|
|
3486
3509
|
}
|
|
3487
3510
|
|
|
3511
|
+
_buildMonthPickerHeader(year) {
|
|
3512
|
+
if (!this.showYears) return "";
|
|
3513
|
+
|
|
3514
|
+
const minYear = this._minDate()?.getFullYear() ?? year - 50;
|
|
3515
|
+
const maxYear = this._maxDate()?.getFullYear() ?? year + 50;
|
|
3516
|
+
const yearOptions = JSON.stringify(
|
|
3517
|
+
Array.from({ length: maxYear - minYear + 1 }, (_, i) => ({
|
|
3518
|
+
value: String(minYear + i),
|
|
3519
|
+
label: String(minYear + i),
|
|
3520
|
+
})),
|
|
3521
|
+
);
|
|
3522
|
+
|
|
3523
|
+
return `
|
|
3524
|
+
<div class="cal-header">
|
|
3525
|
+
<div class="header-selects">
|
|
3526
|
+
<y-select class="m-year-sel" size="small" value="${year}" options='${yearOptions}'></y-select>
|
|
3527
|
+
</div>
|
|
3528
|
+
</div>
|
|
3529
|
+
`;
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3488
3532
|
_buildPanel(side, showHours) {
|
|
3489
3533
|
const vd = this._viewDateForSide(side);
|
|
3490
3534
|
const isRange = this.mode === "range";
|
|
@@ -3574,10 +3618,20 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3574
3618
|
|
|
3575
3619
|
.month-sel { min-width: 120px; }
|
|
3576
3620
|
.year-sel { min-width: 80px; }
|
|
3621
|
+
.m-year-sel { min-width: 90px; }
|
|
3577
3622
|
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3623
|
+
/* ---- Year-picker range inputs ---- */
|
|
3624
|
+
|
|
3625
|
+
.cal-header.year-range {
|
|
3626
|
+
justify-content: center;
|
|
3627
|
+
gap: var(--spacing-x-small, 6px);
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
.year-range-input { width: 5.5em; }
|
|
3631
|
+
|
|
3632
|
+
.year-range-sep {
|
|
3633
|
+
color: var(--component-datepicker-header-color);
|
|
3634
|
+
flex-shrink: 0;
|
|
3581
3635
|
}
|
|
3582
3636
|
|
|
3583
3637
|
/* ---- Day grid ---- */
|
|
@@ -3777,31 +3831,57 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3777
3831
|
}
|
|
3778
3832
|
|
|
3779
3833
|
_buildYearGrid(vd) {
|
|
3834
|
+
this._ensureYearRange(vd);
|
|
3780
3835
|
const selected = this._startDate?.getFullYear();
|
|
3781
|
-
const
|
|
3782
|
-
const
|
|
3783
|
-
const years = Array.from(
|
|
3784
|
-
{ length: maxY - minY + 1 },
|
|
3785
|
-
(_, i) => minY + i,
|
|
3786
|
-
);
|
|
3836
|
+
const lo = Math.min(this._yearRangeStart, this._yearRangeEnd);
|
|
3837
|
+
const hi = Math.max(this._yearRangeStart, this._yearRangeEnd);
|
|
3838
|
+
const years = Array.from({ length: hi - lo + 1 }, (_, i) => lo + i);
|
|
3787
3839
|
return `
|
|
3788
3840
|
<div class="year-grid">
|
|
3789
3841
|
${years
|
|
3790
|
-
.map(
|
|
3791
|
-
|
|
3842
|
+
.map((y) => {
|
|
3843
|
+
const isSelected = y === selected;
|
|
3844
|
+
const disabled = this._isYearDisabled(y);
|
|
3845
|
+
return `<y-button
|
|
3792
3846
|
class="year-btn"
|
|
3793
|
-
style-type="${
|
|
3794
|
-
color="${
|
|
3847
|
+
style-type="${isSelected ? "filled" : "flat"}"
|
|
3848
|
+
color="${isSelected ? this.color : "base"}"
|
|
3795
3849
|
size="small"
|
|
3796
3850
|
padding-mode="square"
|
|
3797
3851
|
data-year="${y}"
|
|
3798
|
-
|
|
3799
|
-
|
|
3852
|
+
${disabled ? "disabled" : ""}
|
|
3853
|
+
>${y}</y-button>`;
|
|
3854
|
+
})
|
|
3800
3855
|
.join("")}
|
|
3801
3856
|
</div>
|
|
3802
3857
|
`;
|
|
3803
3858
|
}
|
|
3804
3859
|
|
|
3860
|
+
_buildYearPickerHeader() {
|
|
3861
|
+
this._ensureYearRange(this._viewDate);
|
|
3862
|
+
return `
|
|
3863
|
+
<div class="cal-header year-range">
|
|
3864
|
+
<y-input
|
|
3865
|
+
class="year-range-input"
|
|
3866
|
+
data-bound="start"
|
|
3867
|
+
type="number"
|
|
3868
|
+
size="small"
|
|
3869
|
+
value="${this._yearRangeStart}"
|
|
3870
|
+
aria-label="Start year"
|
|
3871
|
+
></y-input>
|
|
3872
|
+
<span class="year-range-sep">–</span>
|
|
3873
|
+
<y-input
|
|
3874
|
+
class="year-range-input"
|
|
3875
|
+
data-bound="end"
|
|
3876
|
+
type="number"
|
|
3877
|
+
size="small"
|
|
3878
|
+
value="${this._yearRangeEnd}"
|
|
3879
|
+
aria-label="End year"
|
|
3880
|
+
></y-input>
|
|
3881
|
+
</div>
|
|
3882
|
+
`;
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3805
3885
|
_emitChange(source) {
|
|
3806
3886
|
this._suppressParse = true;
|
|
3807
3887
|
const value = this._buildValueString();
|
|
@@ -3829,6 +3909,16 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3829
3909
|
);
|
|
3830
3910
|
}
|
|
3831
3911
|
|
|
3912
|
+
_ensureYearRange(vd) {
|
|
3913
|
+
if (this._yearRangeStart != null && this._yearRangeEnd != null) return;
|
|
3914
|
+
const base =
|
|
3915
|
+
this._startDate && !isNaN(this._startDate)
|
|
3916
|
+
? this._startDate.getFullYear()
|
|
3917
|
+
: vd.getFullYear();
|
|
3918
|
+
this._yearRangeStart = this._minDate()?.getFullYear() ?? base - 10;
|
|
3919
|
+
this._yearRangeEnd = this._maxDate()?.getFullYear() ?? base + 10;
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3832
3922
|
_formatDate(date) {
|
|
3833
3923
|
if (!date || isNaN(date)) return "";
|
|
3834
3924
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -3921,6 +4011,14 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3921
4011
|
);
|
|
3922
4012
|
}
|
|
3923
4013
|
|
|
4014
|
+
_isYearDisabled(year) {
|
|
4015
|
+
const min = this._minDate();
|
|
4016
|
+
const max = this._maxDate();
|
|
4017
|
+
if (min && year < min.getFullYear()) return true;
|
|
4018
|
+
if (max && year > max.getFullYear()) return true;
|
|
4019
|
+
return false;
|
|
4020
|
+
}
|
|
4021
|
+
|
|
3924
4022
|
_maxDate() {
|
|
3925
4023
|
return this.max ? new Date(this.max) : null;
|
|
3926
4024
|
}
|
|
@@ -3984,6 +4082,23 @@ class YumeDatepicker extends HTMLElement {
|
|
|
3984
4082
|
}
|
|
3985
4083
|
}
|
|
3986
4084
|
|
|
4085
|
+
_refreshYearGrid() {
|
|
4086
|
+
const grid = this.shadowRoot.querySelector(".year-grid");
|
|
4087
|
+
if (!grid) return;
|
|
4088
|
+
|
|
4089
|
+
const lo = Math.min(this._yearRangeStart, this._yearRangeEnd);
|
|
4090
|
+
const hi = Math.max(this._yearRangeStart, this._yearRangeEnd);
|
|
4091
|
+
// Skip runaway ranges while the user is still typing a bound.
|
|
4092
|
+
if (hi - lo > 500) return;
|
|
4093
|
+
|
|
4094
|
+
grid.outerHTML = this._buildYearGrid(this._viewDate);
|
|
4095
|
+
this.shadowRoot.querySelectorAll(".year-btn").forEach((btn) => {
|
|
4096
|
+
btn.addEventListener("click", () =>
|
|
4097
|
+
this._selectYear(parseInt(btn.dataset.year)),
|
|
4098
|
+
);
|
|
4099
|
+
});
|
|
4100
|
+
}
|
|
4101
|
+
|
|
3987
4102
|
_sameDay(a, b) {
|
|
3988
4103
|
if (!a || !b) return false;
|
|
3989
4104
|
return (
|
|
@@ -4002,6 +4117,33 @@ class YumeDatepicker extends HTMLElement {
|
|
|
4002
4117
|
});
|
|
4003
4118
|
}
|
|
4004
4119
|
|
|
4120
|
+
_selectMonth(year, month) {
|
|
4121
|
+
const src =
|
|
4122
|
+
this._startDate && !isNaN(this._startDate) ? this._startDate : null;
|
|
4123
|
+
const day = src ? src.getDate() : 1;
|
|
4124
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
4125
|
+
|
|
4126
|
+
this._startDate = new Date(year, month, Math.min(day, daysInMonth));
|
|
4127
|
+
this._viewDate = new Date(year, month, 1);
|
|
4128
|
+
this._applyTimesToDates();
|
|
4129
|
+
this._emitChange("month");
|
|
4130
|
+
this.render();
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
_selectYear(year) {
|
|
4134
|
+
const src =
|
|
4135
|
+
this._startDate && !isNaN(this._startDate) ? this._startDate : null;
|
|
4136
|
+
const month = src ? src.getMonth() : 0;
|
|
4137
|
+
const day = src ? src.getDate() : 1;
|
|
4138
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
4139
|
+
|
|
4140
|
+
this._startDate = new Date(year, month, Math.min(day, daysInMonth));
|
|
4141
|
+
this._viewDate = new Date(year, month, 1);
|
|
4142
|
+
this._applyTimesToDates();
|
|
4143
|
+
this._emitChange("year");
|
|
4144
|
+
this.render();
|
|
4145
|
+
}
|
|
4146
|
+
|
|
4005
4147
|
_setupMediaQuery() {
|
|
4006
4148
|
this._teardownMediaQuery();
|
|
4007
4149
|
const bp = this._getBreakpointPx();
|
|
@@ -8000,15 +8142,7 @@ class YumePopover extends HTMLElement {
|
|
|
8000
8142
|
}
|
|
8001
8143
|
|
|
8002
8144
|
_resolveMountPoint() {
|
|
8003
|
-
|
|
8004
|
-
// delivers a theme as CSS custom properties scoped to the <y-theme>
|
|
8005
|
-
// subtree (set inline on that element). Appending to document.body
|
|
8006
|
-
// would drop out of that scope, so every --base-* / --component-* /
|
|
8007
|
-
// --primary-* lookup falls back to the un-themed default literals.
|
|
8008
|
-
// Mount into the nearest enclosing <y-theme> instead so the portaled
|
|
8009
|
-
// surface inherits the active theme; fall back to <body> when there
|
|
8010
|
-
// is no y-theme ancestor (or it lives across a shadow boundary).
|
|
8011
|
-
return this.closest("y-theme") || document.body;
|
|
8145
|
+
return resolveThemeMountPoint(this);
|
|
8012
8146
|
}
|
|
8013
8147
|
|
|
8014
8148
|
_restoreFocus() {
|
|
@@ -10644,6 +10778,10 @@ class YumeDataGrid extends HTMLElement {
|
|
|
10644
10778
|
value: currentOp,
|
|
10645
10779
|
portal: "",
|
|
10646
10780
|
});
|
|
10781
|
+
opSelect.style.setProperty(
|
|
10782
|
+
"--component-select-z-index",
|
|
10783
|
+
"calc(var(--component-popover-z-index, 7500) + 1)",
|
|
10784
|
+
);
|
|
10647
10785
|
const valInput = createElement("y-input", {
|
|
10648
10786
|
size: "small",
|
|
10649
10787
|
type,
|