@waggylabs/yumekit 0.4.1-beta.63 → 0.4.1-beta.65

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 CHANGED
@@ -35,7 +35,11 @@ Delete any empty sections before publishing.
35
35
 
36
36
  ### Added
37
37
 
38
- - Storybook integrationall 26 components now have Storybook stories. Includes a theme background selector that applies the correct design tokens for each active theme.
38
+ - New `y-date` component a form-associated date (and optional time) input field. Displays a trigger button that opens a `y-datepicker` popup. Supports `name`, `value`, `label`, `label-position`, `placeholder`, `size`, `disabled`, `required`, `invalid`, `clearable`, `show-time`, `min`, `max`, `format`, and `mode` attributes. Emits `change` and `input` events. Fully keyboard accessible with `aria-expanded` management.
39
+ - New `y-datepicker` component — a standalone calendar and optional time picker widget. Supports single date and date range selection via the `mode` attribute. Configurable with `value`, `min`, `max`, `show-time`, `show-seconds`, `format`, `first-day-of-week`, `year-range`, and `panel-count` attributes. Emits a `change` event with the selected date value or `[start, end]` range array. Public API: `clear()`, `formatDate(date)`.
40
+ - 10 new bundled icons: `fan`, `thermometer-high`, `thermometer-low`, `ban`, `bluetooth`, `unlock`, `plug`, `gasoline`, `ev-charger`, `tools`.
41
+ - `comp-date` icon added to the bundled icon registry.
42
+ - Storybook integration — all 28 components now have Storybook stories. Includes a theme background selector that applies the correct design tokens for each active theme.
39
43
  - 18 new built-in themes (9 color families × dark + light): `green`, `red`, `teal`, `yellow`, `indigo`, `purple`, `pink`, `brown`, `olive`. All are usable by name in the `theme` attribute of `<y-theme>`.
40
44
 
41
45
  ### Changed
@@ -1661,18 +1661,36 @@ if (!customElements.get("y-select")) {
1661
1661
  }
1662
1662
 
1663
1663
  const MONTHS = [
1664
- "January", "February", "March", "April", "May", "June",
1665
- "July", "August", "September", "October", "November", "December",
1664
+ "January",
1665
+ "February",
1666
+ "March",
1667
+ "April",
1668
+ "May",
1669
+ "June",
1670
+ "July",
1671
+ "August",
1672
+ "September",
1673
+ "October",
1674
+ "November",
1675
+ "December",
1666
1676
  ];
1667
1677
  const DAYS_SHORT = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
1668
1678
 
1669
1679
  class YumeDatepicker extends HTMLElement {
1670
1680
  static get observedAttributes() {
1671
1681
  return [
1672
- "mode", "value", "min", "max",
1673
- "show-time", "show-minutes", "show-seconds",
1674
- "show-years", "show-months", "show-days",
1675
- "format", "color",
1682
+ "mode",
1683
+ "value",
1684
+ "min",
1685
+ "max",
1686
+ "show-time",
1687
+ "show-minutes",
1688
+ "show-seconds",
1689
+ "show-years",
1690
+ "show-months",
1691
+ "show-days",
1692
+ "format",
1693
+ "color",
1676
1694
  ];
1677
1695
  }
1678
1696
 
@@ -1701,6 +1719,7 @@ class YumeDatepicker extends HTMLElement {
1701
1719
 
1702
1720
  attributeChangedCallback(name, oldVal, newVal) {
1703
1721
  if (oldVal === newVal) return;
1722
+ if (this._suppressParse) return;
1704
1723
  if (name === "value") {
1705
1724
  this._parseValue();
1706
1725
  }
@@ -1712,52 +1731,106 @@ class YumeDatepicker extends HTMLElement {
1712
1731
  // -------------------------------------------------------------------------
1713
1732
 
1714
1733
  /** @type {string} "single" | "range" (default "single") */
1715
- get mode() { return this.getAttribute("mode") || "single"; }
1716
- set mode(v) { this.setAttribute("mode", v); }
1734
+ get mode() {
1735
+ return this.getAttribute("mode") || "single";
1736
+ }
1737
+ set mode(v) {
1738
+ this.setAttribute("mode", v);
1739
+ }
1717
1740
 
1718
1741
  /** @type {string} ISO date string, or "ISO,ISO" for range */
1719
- get value() { return this.getAttribute("value") || ""; }
1720
- set value(v) { this.setAttribute("value", v); }
1742
+ get value() {
1743
+ return this.getAttribute("value") || "";
1744
+ }
1745
+ set value(v) {
1746
+ this.setAttribute("value", v);
1747
+ }
1721
1748
 
1722
1749
  /** @type {string} Minimum selectable date (ISO string) */
1723
- get min() { return this.getAttribute("min") || ""; }
1724
- set min(v) { this.setAttribute("min", v); }
1750
+ get min() {
1751
+ return this.getAttribute("min") || "";
1752
+ }
1753
+ set min(v) {
1754
+ this.setAttribute("min", v);
1755
+ }
1725
1756
 
1726
1757
  /** @type {string} Maximum selectable date (ISO string) */
1727
- get max() { return this.getAttribute("max") || ""; }
1728
- set max(v) { this.setAttribute("max", v); }
1758
+ get max() {
1759
+ return this.getAttribute("max") || "";
1760
+ }
1761
+ set max(v) {
1762
+ this.setAttribute("max", v);
1763
+ }
1729
1764
 
1730
1765
  /** @type {boolean} Show hour time picker */
1731
- get showTime() { return this.hasAttribute("show-time"); }
1732
- set showTime(v) { v ? this.setAttribute("show-time", "") : this.removeAttribute("show-time"); }
1766
+ get showTime() {
1767
+ return this.hasAttribute("show-time");
1768
+ }
1769
+ set showTime(v) {
1770
+ v
1771
+ ? this.setAttribute("show-time", "")
1772
+ : this.removeAttribute("show-time");
1773
+ }
1733
1774
 
1734
1775
  /** @type {boolean} Show minute column in time picker (implies show-time) */
1735
- get showMinutes() { return this.hasAttribute("show-minutes"); }
1736
- set showMinutes(v) { v ? this.setAttribute("show-minutes", "") : this.removeAttribute("show-minutes"); }
1776
+ get showMinutes() {
1777
+ return this.hasAttribute("show-minutes");
1778
+ }
1779
+ set showMinutes(v) {
1780
+ v
1781
+ ? this.setAttribute("show-minutes", "")
1782
+ : this.removeAttribute("show-minutes");
1783
+ }
1737
1784
 
1738
1785
  /** @type {boolean} Show second column in time picker (implies show-minutes) */
1739
- get showSeconds() { return this.hasAttribute("show-seconds"); }
1740
- set showSeconds(v) { v ? this.setAttribute("show-seconds", "") : this.removeAttribute("show-seconds"); }
1786
+ get showSeconds() {
1787
+ return this.hasAttribute("show-seconds");
1788
+ }
1789
+ set showSeconds(v) {
1790
+ v
1791
+ ? this.setAttribute("show-seconds", "")
1792
+ : this.removeAttribute("show-seconds");
1793
+ }
1741
1794
 
1742
1795
  /** @type {boolean} Show year select in header (default true) */
1743
- get showYears() { return this.getAttribute("show-years") !== "false"; }
1744
- set showYears(v) { this.setAttribute("show-years", v ? "true" : "false"); }
1796
+ get showYears() {
1797
+ return this.getAttribute("show-years") !== "false";
1798
+ }
1799
+ set showYears(v) {
1800
+ this.setAttribute("show-years", v ? "true" : "false");
1801
+ }
1745
1802
 
1746
1803
  /** @type {boolean} Show month select in header (default true) */
1747
- get showMonths() { return this.getAttribute("show-months") !== "false"; }
1748
- set showMonths(v) { this.setAttribute("show-months", v ? "true" : "false"); }
1804
+ get showMonths() {
1805
+ return this.getAttribute("show-months") !== "false";
1806
+ }
1807
+ set showMonths(v) {
1808
+ this.setAttribute("show-months", v ? "true" : "false");
1809
+ }
1749
1810
 
1750
1811
  /** @type {boolean} Show day grid (default true) */
1751
- get showDays() { return this.getAttribute("show-days") !== "false"; }
1752
- set showDays(v) { this.setAttribute("show-days", v ? "true" : "false"); }
1812
+ get showDays() {
1813
+ return this.getAttribute("show-days") !== "false";
1814
+ }
1815
+ set showDays(v) {
1816
+ this.setAttribute("show-days", v ? "true" : "false");
1817
+ }
1753
1818
 
1754
1819
  /** @type {string} Date format string (default "MM/DD/YYYY") */
1755
- get format() { return this.getAttribute("format") || "MM/DD/YYYY"; }
1756
- set format(v) { this.setAttribute("format", v); }
1820
+ get format() {
1821
+ return this.getAttribute("format") || "MM/DD/YYYY";
1822
+ }
1823
+ set format(v) {
1824
+ this.setAttribute("format", v);
1825
+ }
1757
1826
 
1758
1827
  /** @type {string} Color theme (default "primary") */
1759
- get color() { return this.getAttribute("color") || "primary"; }
1760
- set color(v) { this.setAttribute("color", v); }
1828
+ get color() {
1829
+ return this.getAttribute("color") || "primary";
1830
+ }
1831
+ set color(v) {
1832
+ this.setAttribute("color", v);
1833
+ }
1761
1834
 
1762
1835
  // -------------------------------------------------------------------------
1763
1836
  // Public
@@ -1783,7 +1856,8 @@ class YumeDatepicker extends HTMLElement {
1783
1856
  }
1784
1857
 
1785
1858
  render() {
1786
- const showTimeCols = this.showTime || this.showMinutes || this.showSeconds;
1859
+ const showTimeCols =
1860
+ this.showTime || this.showMinutes || this.showSeconds;
1787
1861
  const isRange = this.mode === "range";
1788
1862
 
1789
1863
  this.shadowRoot.innerHTML = `
@@ -1804,10 +1878,20 @@ class YumeDatepicker extends HTMLElement {
1804
1878
 
1805
1879
  _applyTimesToDates() {
1806
1880
  if (this._startDate) {
1807
- this._startDate.setHours(this._startTime.h, this._startTime.m, this._startTime.s, 0);
1881
+ this._startDate.setHours(
1882
+ this._startTime.h,
1883
+ this._startTime.m,
1884
+ this._startTime.s,
1885
+ 0,
1886
+ );
1808
1887
  }
1809
1888
  if (this._endDate) {
1810
- this._endDate.setHours(this._endTime.h, this._endTime.m, this._endTime.s, 0);
1889
+ this._endDate.setHours(
1890
+ this._endTime.h,
1891
+ this._endTime.m,
1892
+ this._endTime.s,
1893
+ 0,
1894
+ );
1811
1895
  }
1812
1896
  }
1813
1897
 
@@ -1825,20 +1909,39 @@ class YumeDatepicker extends HTMLElement {
1825
1909
 
1826
1910
  root.querySelectorAll(".month-sel").forEach((sel) => {
1827
1911
  sel.addEventListener("change", () => {
1828
- this._viewDate.setMonth(parseInt(sel.value));
1912
+ const targetMonth = parseInt(sel.value);
1913
+ if (sel.dataset.side === "right") {
1914
+ // Right panel shows _viewDate + 1 month, so back-calculate
1915
+ const d = new Date(this._viewDate);
1916
+ d.setMonth(targetMonth - 1); // JS handles month=-1 as Dec of prev year
1917
+ d.setDate(1);
1918
+ this._viewDate = d;
1919
+ } else {
1920
+ this._viewDate.setMonth(targetMonth);
1921
+ }
1829
1922
  this.render();
1830
1923
  });
1831
1924
  });
1832
1925
 
1833
1926
  root.querySelectorAll(".year-sel").forEach((sel) => {
1834
1927
  sel.addEventListener("change", () => {
1835
- this._viewDate.setFullYear(parseInt(sel.value));
1928
+ const targetYear = parseInt(sel.value);
1929
+ if (sel.dataset.side === "right") {
1930
+ // Apply the year delta relative to what the right panel currently shows
1931
+ const rightVd = this._viewDateForSide("right");
1932
+ const yearDiff = targetYear - rightVd.getFullYear();
1933
+ this._viewDate.setFullYear(this._viewDate.getFullYear() + yearDiff);
1934
+ } else {
1935
+ this._viewDate.setFullYear(targetYear);
1936
+ }
1836
1937
  this.render();
1837
1938
  });
1838
1939
  });
1839
1940
 
1840
1941
  root.querySelectorAll(".day-btn").forEach((btn) => {
1841
- btn.addEventListener("click", () => this._handleDayClick(new Date(btn.dataset.date)));
1942
+ btn.addEventListener("click", () =>
1943
+ this._handleDayClick(new Date(btn.dataset.date)),
1944
+ );
1842
1945
  btn.addEventListener("mouseenter", () => {
1843
1946
  if (this._awaitingEnd) {
1844
1947
  this._hoverDate = new Date(btn.dataset.date);
@@ -1904,7 +2007,9 @@ class YumeDatepicker extends HTMLElement {
1904
2007
  const firstDow = new Date(year, month, 1).getDay();
1905
2008
  const daysInMonth = new Date(year, month + 1, 0).getDate();
1906
2009
 
1907
- const headers = DAYS_SHORT.map((d) => `<span class="day-hdr">${d}</span>`).join("");
2010
+ const headers = DAYS_SHORT.map(
2011
+ (d) => `<span class="day-hdr">${d}</span>`,
2012
+ ).join("");
1908
2013
  const blanks = Array(firstDow).fill(`<span></span>`).join("");
1909
2014
 
1910
2015
  const cells = Array.from({ length: daysInMonth }, (_, i) => {
@@ -2202,13 +2307,15 @@ class YumeDatepicker extends HTMLElement {
2202
2307
  >${h12}:00 ${ampm}</y-button>`;
2203
2308
  }).join("");
2204
2309
 
2205
- const minutesBtns = this.showMinutes ? `
2310
+ const minutesBtns = this.showMinutes
2311
+ ? `
2206
2312
  <div class="time-col-wrap">
2207
2313
  <span class="time-col-hdr">Min</span>
2208
2314
  <div class="time-col" data-col="minutes">
2209
- ${Array.from({ length: 12 }, (_, i) => i * 5).map((m) => {
2210
- const sel = Math.floor(time.m / 5) * 5 === m;
2211
- return `<y-button
2315
+ ${Array.from({ length: 12 }, (_, i) => i * 5)
2316
+ .map((m) => {
2317
+ const sel = Math.floor(time.m / 5) * 5 === m;
2318
+ return `<y-button
2212
2319
  class="time-btn${sel ? " selected" : ""}"
2213
2320
  style-type="${sel ? "filled" : "flat"}"
2214
2321
  color="${sel ? this.color : "base"}"
@@ -2216,18 +2323,22 @@ class YumeDatepicker extends HTMLElement {
2216
2323
  data-minute="${m}"
2217
2324
  data-side="${side}"
2218
2325
  >${String(m).padStart(2, "0")}</y-button>`;
2219
- }).join("")}
2326
+ })
2327
+ .join("")}
2220
2328
  </div>
2221
2329
  </div>
2222
- ` : "";
2330
+ `
2331
+ : "";
2223
2332
 
2224
- const secondsBtns = this.showSeconds ? `
2333
+ const secondsBtns = this.showSeconds
2334
+ ? `
2225
2335
  <div class="time-col-wrap">
2226
2336
  <span class="time-col-hdr">Sec</span>
2227
2337
  <div class="time-col" data-col="seconds">
2228
- ${Array.from({ length: 12 }, (_, i) => i * 5).map((s) => {
2229
- const sel = Math.floor(time.s / 5) * 5 === s;
2230
- return `<y-button
2338
+ ${Array.from({ length: 12 }, (_, i) => i * 5)
2339
+ .map((s) => {
2340
+ const sel = Math.floor(time.s / 5) * 5 === s;
2341
+ return `<y-button
2231
2342
  class="time-btn${sel ? " selected" : ""}"
2232
2343
  style-type="${sel ? "filled" : "flat"}"
2233
2344
  color="${sel ? this.color : "base"}"
@@ -2235,10 +2346,12 @@ class YumeDatepicker extends HTMLElement {
2235
2346
  data-second="${s}"
2236
2347
  data-side="${side}"
2237
2348
  >${String(s).padStart(2, "0")}</y-button>`;
2238
- }).join("")}
2349
+ })
2350
+ .join("")}
2239
2351
  </div>
2240
2352
  </div>
2241
- ` : "";
2353
+ `
2354
+ : "";
2242
2355
 
2243
2356
  return `
2244
2357
  <div class="time-column${disabled ? " time-disabled" : ""}">
@@ -2265,37 +2378,51 @@ class YumeDatepicker extends HTMLElement {
2265
2378
  const selected = this._startDate?.getFullYear();
2266
2379
  const minY = this._minDate()?.getFullYear() ?? vd.getFullYear() - 10;
2267
2380
  const maxY = this._maxDate()?.getFullYear() ?? vd.getFullYear() + 10;
2268
- const years = Array.from({ length: maxY - minY + 1 }, (_, i) => minY + i);
2381
+ const years = Array.from(
2382
+ { length: maxY - minY + 1 },
2383
+ (_, i) => minY + i,
2384
+ );
2269
2385
  return `
2270
2386
  <div class="year-grid">
2271
- ${years.map((y) => `<y-button
2387
+ ${years
2388
+ .map(
2389
+ (y) => `<y-button
2272
2390
  class="year-btn"
2273
2391
  style-type="${y === selected ? "filled" : "flat"}"
2274
2392
  color="${y === selected ? this.color : "base"}"
2275
2393
  size="small"
2276
2394
  data-year="${y}"
2277
- >${y}</y-button>`).join("")}
2395
+ >${y}</y-button>`,
2396
+ )
2397
+ .join("")}
2278
2398
  </div>
2279
2399
  `;
2280
2400
  }
2281
2401
 
2282
2402
  _emitChange() {
2403
+ this._suppressParse = true;
2283
2404
  const value = this._buildValueString();
2284
2405
  if (value !== this.getAttribute("value")) {
2285
2406
  this.setAttribute("value", value);
2286
2407
  }
2287
- this.dispatchEvent(new CustomEvent("change", {
2288
- bubbles: true,
2289
- composed: true,
2290
- detail: {
2291
- value,
2292
- startDate: this._startDate ? new Date(this._startDate) : null,
2293
- endDate: this._endDate ? new Date(this._endDate) : null,
2294
- formatted: this.mode === "range"
2295
- ? `${this._formatDate(this._startDate)}${this._endDate ? " – " + this._formatDate(this._endDate) : ""}`
2296
- : this._formatDate(this._startDate),
2297
- },
2298
- }));
2408
+ this._suppressParse = false;
2409
+ this.dispatchEvent(
2410
+ new CustomEvent("change", {
2411
+ bubbles: true,
2412
+ composed: true,
2413
+ detail: {
2414
+ value,
2415
+ startDate: this._startDate
2416
+ ? new Date(this._startDate)
2417
+ : null,
2418
+ endDate: this._endDate ? new Date(this._endDate) : null,
2419
+ formatted:
2420
+ this.mode === "range"
2421
+ ? `${this._formatDate(this._startDate)}${this._endDate ? " – " + this._formatDate(this._endDate) : ""}`
2422
+ : this._formatDate(this._startDate),
2423
+ },
2424
+ }),
2425
+ );
2299
2426
  }
2300
2427
 
2301
2428
  _formatDate(date) {
@@ -2341,11 +2468,13 @@ class YumeDatepicker extends HTMLElement {
2341
2468
 
2342
2469
  _inRange(date) {
2343
2470
  if (this.mode !== "range" || !this._startDate) return false;
2344
- const end = this._endDate || (this._awaitingEnd ? this._hoverDate : null);
2471
+ const end =
2472
+ this._endDate || (this._awaitingEnd ? this._hoverDate : null);
2345
2473
  if (!end) return false;
2346
- const [lo, hi] = this._startDate <= end
2347
- ? [this._startDate, end]
2348
- : [end, this._startDate];
2474
+ const [lo, hi] =
2475
+ this._startDate <= end
2476
+ ? [this._startDate, end]
2477
+ : [end, this._startDate];
2349
2478
  return date > lo && date < hi;
2350
2479
  }
2351
2480
 
@@ -2366,12 +2495,19 @@ class YumeDatepicker extends HTMLElement {
2366
2495
  }
2367
2496
 
2368
2497
  _isRangeEdge(date) {
2369
- return this._sameDay(date, this._startDate) || this._sameDay(date, this._endDate);
2498
+ return (
2499
+ this._sameDay(date, this._startDate) ||
2500
+ this._sameDay(date, this._endDate)
2501
+ );
2370
2502
  }
2371
2503
 
2372
- _maxDate() { return this.max ? new Date(this.max) : null; }
2504
+ _maxDate() {
2505
+ return this.max ? new Date(this.max) : null;
2506
+ }
2373
2507
 
2374
- _minDate() { return this.min ? new Date(this.min) : null; }
2508
+ _minDate() {
2509
+ return this.min ? new Date(this.min) : null;
2510
+ }
2375
2511
 
2376
2512
  _navigate(months) {
2377
2513
  this._viewDate.setMonth(this._viewDate.getMonth() + months);
@@ -73,6 +73,7 @@ export class YumeDatepicker extends HTMLElement {
73
73
  _buildValueString(): any;
74
74
  _buildYearGrid(vd: any): string;
75
75
  _emitChange(): void;
76
+ _suppressParse: boolean;
76
77
  _formatDate(date: any): string;
77
78
  _handleDayClick(date: any): void;
78
79
  _inRange(date: any): boolean;