@waggylabs/yumekit 0.5.1 → 0.5.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +54 -63
  3. package/dist/ai/llm.txt +14 -11
  4. package/dist/ai/skill/examples/nav-layout.html +5 -5
  5. package/dist/ai/skill/examples/themed-app.html +6 -6
  6. package/dist/ai/skill/patterns.md +7 -7
  7. package/dist/ai/skill/reference.md +21 -18
  8. package/dist/components/y-appbar.js +45 -16
  9. package/dist/components/y-banner.js +44 -15
  10. package/dist/components/y-button/y-button.d.ts +11 -4
  11. package/dist/components/y-button.d.ts +11 -4
  12. package/dist/components/y-button.js +43 -14
  13. package/dist/components/y-checkbox.js +7 -1
  14. package/dist/components/y-code.js +9 -2
  15. package/dist/components/y-color.js +12 -2
  16. package/dist/components/y-colorpicker.js +12 -2
  17. package/dist/components/y-data-grid.js +83 -35
  18. package/dist/components/y-date.js +72 -30
  19. package/dist/components/y-datepicker.js +72 -30
  20. package/dist/components/y-dialog.js +1 -1
  21. package/dist/components/y-help.js +46 -17
  22. package/dist/components/y-input.js +11 -1
  23. package/dist/components/y-paginator.js +46 -17
  24. package/dist/components/y-select.js +1 -1
  25. package/dist/components/y-sidebar.js +45 -16
  26. package/dist/components/y-tabs/y-tabs.d.ts +17 -0
  27. package/dist/components/y-tabs.d.ts +17 -0
  28. package/dist/components/y-tabs.js +193 -8
  29. package/dist/components/y-tag/y-tag.d.ts +9 -2
  30. package/dist/components/y-tag.d.ts +9 -2
  31. package/dist/components/y-tag.js +36 -8
  32. package/dist/components/y-textarea.js +11 -1
  33. package/dist/index.js +341 -63
  34. package/dist/react.d.ts +5 -0
  35. package/dist/yumekit.min.js +1 -1
  36. package/llm.txt +14 -11
  37. package/package.json +1 -1
  38. package/scripts/install-ai-docs.js +93 -72
package/dist/index.js CHANGED
@@ -2001,6 +2001,7 @@ class YumeButton extends HTMLElement {
2001
2001
  "right-icon",
2002
2002
  "color",
2003
2003
  "size",
2004
+ "variant",
2004
2005
  "style-type",
2005
2006
  "type",
2006
2007
  "padding-mode",
@@ -2053,9 +2054,13 @@ class YumeButton extends HTMLElement {
2053
2054
  }
2054
2055
  }
2055
2056
 
2057
+ if (name === "style-type" && newValue !== null) {
2058
+ this._warnStyleTypeDeprecated();
2059
+ }
2060
+
2056
2061
  this._init();
2057
2062
 
2058
- if (["color", "size", "style-type", "disabled"].includes(name)) {
2063
+ if (["color", "size", "variant", "style-type", "disabled"].includes(name)) {
2059
2064
  this._updateStyles();
2060
2065
  }
2061
2066
  }
@@ -2137,9 +2142,12 @@ class YumeButton extends HTMLElement {
2137
2142
  this.setAttribute("size", val);
2138
2143
  }
2139
2144
 
2140
- /** Visual style: "filled" | "outlined" | "flat" (default "outlined"). */
2145
+ /**
2146
+ * Deprecated alias for `variant`. Use `variant` instead; retained for
2147
+ * backward compatibility and removed in a future major version.
2148
+ */
2141
2149
  get styleType() {
2142
- return this.getAttribute("style-type") || "outlined";
2150
+ return this.variant;
2143
2151
  }
2144
2152
  set styleType(val) {
2145
2153
  this.setAttribute("style-type", val);
@@ -2182,6 +2190,18 @@ class YumeButton extends HTMLElement {
2182
2190
  this.setAttribute("value", newVal);
2183
2191
  }
2184
2192
 
2193
+ /** Visual style: "filled" | "outlined" | "flat" (default "outlined"). */
2194
+ get variant() {
2195
+ return (
2196
+ this.getAttribute("variant") ||
2197
+ this.getAttribute("style-type") ||
2198
+ "outlined"
2199
+ );
2200
+ }
2201
+ set variant(val) {
2202
+ this.setAttribute("variant", val);
2203
+ }
2204
+
2185
2205
  // -------------------------------------------------------------------------
2186
2206
  // Public
2187
2207
  // -------------------------------------------------------------------------
@@ -2236,7 +2256,7 @@ class YumeButton extends HTMLElement {
2236
2256
  });
2237
2257
  }
2238
2258
 
2239
- _applyCustomColorStyles(color, styleType, size) {
2259
+ _applyCustomColorStyles(color, variant, size) {
2240
2260
  const text = contrastTextColor(color);
2241
2261
  const hover = `color-mix(in srgb, ${color} 85%, black)`;
2242
2262
  const active = `color-mix(in srgb, ${color} 70%, black)`;
@@ -2292,7 +2312,7 @@ class YumeButton extends HTMLElement {
2292
2312
  },
2293
2313
  };
2294
2314
 
2295
- Object.entries(styles[styleType] || styles.outlined).forEach(
2315
+ Object.entries(styles[variant] || styles.outlined).forEach(
2296
2316
  ([key, val]) => this.button.style.setProperty(key, val),
2297
2317
  );
2298
2318
 
@@ -2338,11 +2358,11 @@ class YumeButton extends HTMLElement {
2338
2358
  );
2339
2359
  }
2340
2360
 
2341
- _applyInteractionStyles(vars, styleType) {
2342
- if (styleType === "filled") {
2361
+ _applyInteractionStyles(vars, variant) {
2362
+ if (variant === "filled") {
2343
2363
  this._applyFilledInteractionStyles(vars);
2344
2364
  } else {
2345
- this._applyUnfilledInteractionStyles(vars, styleType);
2365
+ this._applyUnfilledInteractionStyles(vars, variant);
2346
2366
  }
2347
2367
  }
2348
2368
 
@@ -2467,7 +2487,7 @@ class YumeButton extends HTMLElement {
2467
2487
  this.shadowRoot.appendChild(style);
2468
2488
  }
2469
2489
 
2470
- _applyUnfilledInteractionStyles(vars, styleType) {
2490
+ _applyUnfilledInteractionStyles(vars, variant) {
2471
2491
  const borderColor = this._outlineBorderColor(
2472
2492
  `var(${vars[7]}, var(${vars[0]}, #f7f7fa))`,
2473
2493
  );
@@ -2497,7 +2517,7 @@ class YumeButton extends HTMLElement {
2497
2517
  `var(${vars[6]}, #0c0c0d)`,
2498
2518
  );
2499
2519
 
2500
- if (styleType === "outlined") {
2520
+ if (variant === "outlined") {
2501
2521
  // Outlined buttons keep their border color across all states
2502
2522
  this.button.style.setProperty("--hover-border-color", borderColor);
2503
2523
  this.button.style.setProperty("--focus-border-color", borderColor);
@@ -2785,11 +2805,11 @@ class YumeButton extends HTMLElement {
2785
2805
  }
2786
2806
 
2787
2807
  _updateStyles() {
2788
- const { color, size, styleType } = this;
2808
+ const { color, size, variant } = this;
2789
2809
  const colorVars = this._getColorVarsMap();
2790
2810
 
2791
2811
  if (!colorVars[color] && isSafeCssColor(color)) {
2792
- this._applyCustomColorStyles(color, styleType, size);
2812
+ this._applyCustomColorStyles(color, variant, size);
2793
2813
  return;
2794
2814
  }
2795
2815
 
@@ -2818,14 +2838,23 @@ class YumeButton extends HTMLElement {
2818
2838
  },
2819
2839
  };
2820
2840
 
2821
- const currentStyle = styleVars[styleType] || styleVars.outlined;
2841
+ const currentStyle = styleVars[variant] || styleVars.outlined;
2822
2842
  Object.entries(currentStyle).forEach(([key, value]) => {
2823
2843
  this.button.style.setProperty(key, value);
2824
2844
  });
2825
2845
 
2826
- this._applyInteractionStyles(vars, styleType);
2846
+ this._applyInteractionStyles(vars, variant);
2827
2847
  this._applySizeStyles(size);
2828
2848
  }
2849
+
2850
+ _warnStyleTypeDeprecated() {
2851
+ if (YumeButton._styleTypeDeprecationWarned) return;
2852
+
2853
+ YumeButton._styleTypeDeprecationWarned = true;
2854
+ console.warn(
2855
+ 'y-button: the "style-type" attribute is deprecated and will be removed in a future major version. Use "variant" instead.',
2856
+ );
2857
+ }
2829
2858
  }
2830
2859
 
2831
2860
  if (!customElements.get("y-button")) {
@@ -4143,7 +4172,7 @@ class YumeAppbar extends HTMLElement {
4143
4172
  {
4144
4173
  id,
4145
4174
  color: "base",
4146
- "style-type": "flat",
4175
+ "variant": "flat",
4147
4176
  size: cfg.buttonSize,
4148
4177
  "aria-label": "Open menu",
4149
4178
  "aria-controls": panelId,
@@ -4295,7 +4324,7 @@ class YumeAppbar extends HTMLElement {
4295
4324
  const btn = createElement("y-button", {
4296
4325
  id: btnId,
4297
4326
  color: isActive ? "primary" : "base",
4298
- "style-type": "flat",
4327
+ "variant": "flat",
4299
4328
  size: cfg.buttonSize,
4300
4329
  "aria-current": isActive ? "page" : false,
4301
4330
  });
@@ -5133,7 +5162,7 @@ class YumeSidebar extends HTMLElement {
5133
5162
  {
5134
5163
  class: "collapse-btn",
5135
5164
  color: "base",
5136
- "style-type": "flat",
5165
+ "variant": "flat",
5137
5166
  size: cfg.buttonSize,
5138
5167
  "aria-label": isCollapsed
5139
5168
  ? "Expand sidebar"
@@ -5181,7 +5210,7 @@ class YumeSidebar extends HTMLElement {
5181
5210
  const btn = createElement("y-button", {
5182
5211
  id: btnId,
5183
5212
  color: isActive ? "primary" : "base",
5184
- "style-type": "flat",
5213
+ "variant": "flat",
5185
5214
  size: cfg.buttonSize,
5186
5215
  "aria-current": isActive ? "page" : false,
5187
5216
  });
@@ -5648,7 +5677,7 @@ class YumeBanner extends HTMLElement {
5648
5677
  part: "close-btn",
5649
5678
  "aria-label": "Dismiss banner",
5650
5679
  color,
5651
- "style-type": "filled",
5680
+ "variant": "filled",
5652
5681
  size,
5653
5682
  },
5654
5683
  [createElement("y-icon", { name: "x", size: iconSize })],
@@ -7163,8 +7192,14 @@ class YumeCheckbox extends HTMLElement {
7163
7192
  // -------------------------------------------------------------------------
7164
7193
 
7165
7194
  _bindCheckboxListeners() {
7195
+ const wrapper = this.shadowRoot.querySelector(".wrapper");
7166
7196
  const box = this.shadowRoot.querySelector(".checkbox");
7167
- box.addEventListener("click", () => this.toggle());
7197
+
7198
+ wrapper.addEventListener("click", () => {
7199
+ if (this.disabled) return;
7200
+ box.focus();
7201
+ this.toggle();
7202
+ });
7168
7203
  box.addEventListener("keydown", (e) => {
7169
7204
  if (e.key === " " || e.key === "Enter") {
7170
7205
  e.preventDefault();
@@ -8864,7 +8899,8 @@ class YumeCode extends HTMLElement {
8864
8899
  _buildStyles() {
8865
8900
  return `
8866
8901
  :host {
8867
- display: block;
8902
+ display: flex;
8903
+ flex-direction: column;
8868
8904
  font-family: var(--component-code-font-family, var(--font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace));
8869
8905
  color: var(--component-code-text-color, var(--base-content--, #1a1a1a));
8870
8906
  background: var(--component-code-bg-color, var(--base-background-component, #f6f8fa));
@@ -8914,13 +8950,19 @@ class YumeCode extends HTMLElement {
8914
8950
  .pre-wrap {
8915
8951
  position: relative;
8916
8952
  overflow: hidden;
8953
+ display: flex;
8954
+ flex-direction: column;
8955
+ flex: 1 1 auto;
8956
+ min-height: 0;
8917
8957
  }
8918
8958
 
8919
8959
  pre.code {
8920
8960
  margin: 0;
8921
8961
  padding: var(--spacing-small, 8px) 0;
8962
+ flex: 1 1 auto;
8963
+ min-height: 0;
8922
8964
  overflow-x: ${this.wrap ? "hidden" : "auto"};
8923
- overflow-y: ${this.maxLines ? "auto" : "visible"};
8965
+ overflow-y: auto;
8924
8966
  font-size: var(--component-code-font-size, 0.9em);
8925
8967
  line-height: var(--component-code-line-height, 1.55);
8926
8968
  tab-size: 4;
@@ -9604,7 +9646,10 @@ class YumeInput extends HTMLElement {
9604
9646
  : "default";
9605
9647
  }
9606
9648
  set variant(val) {
9607
- this.setAttribute("variant", val === "underline" ? "underline" : "default");
9649
+ this.setAttribute(
9650
+ "variant",
9651
+ val === "underline" ? "underline" : "default",
9652
+ );
9608
9653
  }
9609
9654
 
9610
9655
  /** @type {string} Input type (default "text"). */
@@ -9672,6 +9717,13 @@ class YumeInput extends HTMLElement {
9672
9717
  // -------------------------------------------------------------------------
9673
9718
 
9674
9719
  _bindInputListeners() {
9720
+ this.inputContainer.addEventListener("mousedown", (e) => {
9721
+ if (e.target !== this.input) {
9722
+ e.preventDefault();
9723
+ this.input.focus();
9724
+ }
9725
+ });
9726
+
9675
9727
  this.input.addEventListener("input", (e) => {
9676
9728
  this.setAttribute("value", e.target.value);
9677
9729
  this.dispatchEvent(
@@ -10862,7 +10914,7 @@ class YumeSelect extends HTMLElement {
10862
10914
  tag.setAttribute("removable", "");
10863
10915
  tag.setAttribute("size", "small");
10864
10916
  tag.setAttribute("color", opt.color || "primary");
10865
- tag.setAttribute("style-type", "filled");
10917
+ tag.setAttribute("variant", "filled");
10866
10918
  tag.textContent = opt.label;
10867
10919
  tag.dataset.value = opt.value;
10868
10920
 
@@ -12988,7 +13040,10 @@ class YumeDatepicker extends HTMLElement {
12988
13040
  root.querySelectorAll(".month-btn").forEach((btn) => {
12989
13041
  btn.addEventListener("click", () => {
12990
13042
  const vd = this._viewDateForSide(btn.dataset.side);
12991
- this._selectMonth(vd.getFullYear(), parseInt(btn.dataset.month));
13043
+ this._selectMonth(
13044
+ vd.getFullYear(),
13045
+ parseInt(btn.dataset.month),
13046
+ );
12992
13047
  });
12993
13048
  });
12994
13049
 
@@ -13064,20 +13119,20 @@ class YumeDatepicker extends HTMLElement {
13064
13119
  const isSelected = this._sameDay(date, this._startDate);
13065
13120
  const inRange = this._inRange(date);
13066
13121
 
13067
- let styleType = "flat";
13122
+ let variant = "flat";
13068
13123
  let color = "base";
13069
13124
 
13070
13125
  if (isEdge || isSelected) {
13071
- styleType = "filled";
13126
+ variant = "filled";
13072
13127
  color = this.color;
13073
13128
  } else if (inRange) {
13074
- styleType = "flat";
13129
+ variant = "flat";
13075
13130
  color = this.color;
13076
13131
  }
13077
13132
 
13078
13133
  return `<y-button
13079
13134
  class="day-btn"
13080
- style-type="${styleType}"
13135
+ variant="${variant}"
13081
13136
  color="${color}"
13082
13137
  size="medium"
13083
13138
  padding-mode="square"
@@ -13131,16 +13186,16 @@ class YumeDatepicker extends HTMLElement {
13131
13186
  return `
13132
13187
  <div class="cal-header">
13133
13188
  <div class="nav-start">
13134
- ${showPrev ? `<y-button class="nav-btn" data-action="prev-year" padding-mode="square" data-side="${side}" style-type="flat" size="small" aria-label="Previous year"><y-icon name="expand-left" size="small"></y-icon></y-button>` : ""}
13135
- ${showPrev ? `<y-button class="nav-btn" data-action="prev-month" padding-mode="square"data-side="${side}" style-type="flat" size="small" aria-label="Previous month"><y-icon name="chevron-left" size="small"></y-icon></y-button>` : ""}
13189
+ ${showPrev ? `<y-button class="nav-btn" data-action="prev-year" padding-mode="square" data-side="${side}" variant="flat" size="small" aria-label="Previous year"><y-icon name="expand-left" size="small"></y-icon></y-button>` : ""}
13190
+ ${showPrev ? `<y-button class="nav-btn" data-action="prev-month" padding-mode="square" data-side="${side}" variant="flat" size="small" aria-label="Previous month"><y-icon name="chevron-left" size="small"></y-icon></y-button>` : ""}
13136
13191
  </div>
13137
13192
  <div class="header-selects">
13138
13193
  ${this.showMonths ? `<y-select class="month-sel" data-side="${side}" size="small" value="${month}" options='${monthOptions}'></y-select>` : ""}
13139
13194
  ${this.showYears ? `<y-select class="year-sel" data-side="${side}" size="small" value="${year}" options='${yearOptions}'></y-select>` : ""}
13140
13195
  </div>
13141
13196
  <div class="nav-end">
13142
- ${showNext ? `<y-button class="nav-btn" data-action="next-month" padding-mode="square" data-side="${side}" style-type="flat" size="small" aria-label="Next month"><y-icon name="chevron-right" size="small"></y-icon></y-button>` : ""}
13143
- ${showNext ? `<y-button class="nav-btn" data-action="next-year" padding-mode="square" data-side="${side}" style-type="flat" size="small" aria-label="Next year"><y-icon name="expand-right" size="small"></y-icon></y-button>` : ""}
13197
+ ${showNext ? `<y-button class="nav-btn" data-action="next-month" padding-mode="square" data-side="${side}" variant="flat" size="small" aria-label="Next month"><y-icon name="chevron-right" size="small"></y-icon></y-button>` : ""}
13198
+ ${showNext ? `<y-button class="nav-btn" data-action="next-year" padding-mode="square" data-side="${side}" variant="flat" size="small" aria-label="Next year"><y-icon name="expand-right" size="small"></y-icon></y-button>` : ""}
13144
13199
  </div>
13145
13200
  </div>
13146
13201
  `;
@@ -13158,7 +13213,7 @@ class YumeDatepicker extends HTMLElement {
13158
13213
  this._startDate.getMonth() === i;
13159
13214
  return `<y-button
13160
13215
  class="month-btn"
13161
- style-type="${isSelected ? "filled" : "flat"}"
13216
+ variant="${isSelected ? "filled" : "flat"}"
13162
13217
  color="${isSelected ? this.color : "base"}"
13163
13218
  size="small"
13164
13219
  padding-mode="square"
@@ -13405,7 +13460,7 @@ class YumeDatepicker extends HTMLElement {
13405
13460
  const sel = time.h === h;
13406
13461
  return `<y-button
13407
13462
  class="time-btn${sel ? " selected" : ""}"
13408
- style-type="${sel ? "filled" : "flat"}"
13463
+ variant="${sel ? "filled" : "flat"}"
13409
13464
  color="${sel ? this.color : "base"}"
13410
13465
  size="small"
13411
13466
  data-hour="${h}"
@@ -13431,7 +13486,7 @@ class YumeDatepicker extends HTMLElement {
13431
13486
  m;
13432
13487
  return `<y-button
13433
13488
  class="time-btn${sel ? " selected" : ""}"
13434
- style-type="${sel ? "filled" : "flat"}"
13489
+ variant="${sel ? "filled" : "flat"}"
13435
13490
  color="${sel ? this.color : "base"}"
13436
13491
  size="small"
13437
13492
  data-minute="${m}"
@@ -13459,7 +13514,7 @@ class YumeDatepicker extends HTMLElement {
13459
13514
  s;
13460
13515
  return `<y-button
13461
13516
  class="time-btn${sel ? " selected" : ""}"
13462
- style-type="${sel ? "filled" : "flat"}"
13517
+ variant="${sel ? "filled" : "flat"}"
13463
13518
  color="${sel ? this.color : "base"}"
13464
13519
  size="small"
13465
13520
  data-second="${s}"
@@ -13507,7 +13562,7 @@ class YumeDatepicker extends HTMLElement {
13507
13562
  const disabled = this._isYearDisabled(y);
13508
13563
  return `<y-button
13509
13564
  class="year-btn"
13510
- style-type="${isSelected ? "filled" : "flat"}"
13565
+ variant="${isSelected ? "filled" : "flat"}"
13511
13566
  color="${isSelected ? this.color : "base"}"
13512
13567
  size="small"
13513
13568
  padding-mode="square"
@@ -15601,7 +15656,7 @@ class YumePaginator extends HTMLElement {
15601
15656
  const attrs = {
15602
15657
  class: `button${isCurrent ? " active" : ""}`,
15603
15658
  part: parts.join(" "),
15604
- "style-type": isCurrent ? "filled" : "flat",
15659
+ "variant": isCurrent ? "filled" : "flat",
15605
15660
  color: isCurrent ? "primary" : "base",
15606
15661
  size: this.size,
15607
15662
  // Number buttons stay square/circular even in themes (e.g. Material)
@@ -15674,7 +15729,7 @@ class YumePaginator extends HTMLElement {
15674
15729
  const attrs = {
15675
15730
  class: `nav nav-${direction}`,
15676
15731
  part: `${partName}${isDisabled ? " button--disabled" : ""}`,
15677
- "style-type": "flat",
15732
+ "variant": "flat",
15678
15733
  color: "base",
15679
15734
  size: this.size,
15680
15735
  "aria-label": cfg.ariaLabel,
@@ -20458,13 +20513,13 @@ class YumeDataGrid extends HTMLElement {
20458
20513
 
20459
20514
  const actions = createElement("div", { class: "filter-actions" });
20460
20515
  const clearBtn = createElement("y-button", {
20461
- "style-type": "flat",
20516
+ "variant": "flat",
20462
20517
  size: "small",
20463
20518
  type: "button",
20464
20519
  });
20465
20520
  clearBtn.textContent = "Clear";
20466
20521
  const applyBtn = createElement("y-button", {
20467
- "style-type": "filled",
20522
+ "variant": "filled",
20468
20523
  color: "primary",
20469
20524
  size: "small",
20470
20525
  type: "button",
@@ -22860,7 +22915,7 @@ class YumeDialog extends HTMLElement {
22860
22915
  _buildCloseButton() {
22861
22916
  const btn = document.createElement("y-button");
22862
22917
  btn.setAttribute("size", "small");
22863
- btn.setAttribute("style-type", "flat");
22918
+ btn.setAttribute("variant", "flat");
22864
22919
  btn.setAttribute("aria-label", "Close");
22865
22920
  btn.textContent = "\u2715";
22866
22921
  btn.addEventListener("click", () => { this.visible = false; });
@@ -26893,7 +26948,7 @@ class YumeHelp extends HTMLElement {
26893
26948
  {
26894
26949
  class: "y-help-tooltip-close",
26895
26950
  part: "close-button",
26896
- "style-type": "flat",
26951
+ "variant": "flat",
26897
26952
  size: "small",
26898
26953
  "aria-label": this.closeLabel,
26899
26954
  },
@@ -26930,7 +26985,7 @@ class YumeHelp extends HTMLElement {
26930
26985
  {
26931
26986
  class: "y-help-tooltip-btn",
26932
26987
  part: "prev-button",
26933
- "style-type": "outlined",
26988
+ "variant": "outlined",
26934
26989
  size: "small",
26935
26990
  "aria-label": this.prevLabel,
26936
26991
  },
@@ -26942,7 +26997,7 @@ class YumeHelp extends HTMLElement {
26942
26997
  {
26943
26998
  class: "y-help-tooltip-btn",
26944
26999
  part: "next-button",
26945
- "style-type": "filled",
27000
+ "variant": "filled",
26946
27001
  color: "primary",
26947
27002
  size: "small",
26948
27003
  "aria-label": this.nextLabel,
@@ -28752,7 +28807,10 @@ class YumeTextarea extends HTMLElement {
28752
28807
  : "default";
28753
28808
  }
28754
28809
  set variant(val) {
28755
- this.setAttribute("variant", val === "underline" ? "underline" : "default");
28810
+ this.setAttribute(
28811
+ "variant",
28812
+ val === "underline" ? "underline" : "default",
28813
+ );
28756
28814
  }
28757
28815
 
28758
28816
  /** @type {string} The current textarea value. */
@@ -28809,6 +28867,13 @@ class YumeTextarea extends HTMLElement {
28809
28867
  // -------------------------------------------------------------------------
28810
28868
 
28811
28869
  _bindTextareaListeners() {
28870
+ this.inputContainer.addEventListener("mousedown", (e) => {
28871
+ if (e.target !== this.textarea) {
28872
+ e.preventDefault();
28873
+ this.textarea.focus();
28874
+ }
28875
+ });
28876
+
28812
28877
  this.textarea.addEventListener("input", (e) => {
28813
28878
  this.setAttribute("value", e.target.value);
28814
28879
  this._internals.setFormValue(
@@ -34374,7 +34439,7 @@ if (!customElements.get("y-table")) {
34374
34439
 
34375
34440
  class YumeTag extends HTMLElement {
34376
34441
  static get observedAttributes() {
34377
- return ["removable", "color", "style-type", "shape", "size"];
34442
+ return ["removable", "color", "variant", "style-type", "shape", "size"];
34378
34443
  }
34379
34444
 
34380
34445
  // -------------------------------------------------------------------------
@@ -34392,6 +34457,10 @@ class YumeTag extends HTMLElement {
34392
34457
  }
34393
34458
 
34394
34459
  attributeChangedCallback(name, oldValue, newValue) {
34460
+ if (name === "style-type" && newValue !== null) {
34461
+ this._warnStyleTypeDeprecated();
34462
+ }
34463
+
34395
34464
  if (oldValue !== newValue) this.render();
34396
34465
  }
34397
34466
 
@@ -34432,14 +34501,29 @@ class YumeTag extends HTMLElement {
34432
34501
  this.setAttribute("size", val);
34433
34502
  }
34434
34503
 
34435
- /** Visual style: "filled" | "outlined" | "flat" (default "filled"). */
34504
+ /**
34505
+ * Deprecated alias for `variant`. Use `variant` instead; retained for
34506
+ * backward compatibility and removed in a future major version.
34507
+ */
34436
34508
  get styleType() {
34437
- return this.getAttribute("style-type") || "filled";
34509
+ return this.variant;
34438
34510
  }
34439
34511
  set styleType(val) {
34440
34512
  this.setAttribute("style-type", val);
34441
34513
  }
34442
34514
 
34515
+ /** Visual style: "filled" (default) | "outlined" | "flat". */
34516
+ get variant() {
34517
+ return (
34518
+ this.getAttribute("variant") ||
34519
+ this.getAttribute("style-type") ||
34520
+ "filled"
34521
+ );
34522
+ }
34523
+ set variant(val) {
34524
+ this.setAttribute("variant", val);
34525
+ }
34526
+
34443
34527
  // -------------------------------------------------------------------------
34444
34528
  // Public
34445
34529
  // -------------------------------------------------------------------------
@@ -34474,7 +34558,7 @@ class YumeTag extends HTMLElement {
34474
34558
  });
34475
34559
  }
34476
34560
 
34477
- _getCustomColorVariant(color, styleType) {
34561
+ _getCustomColorVariant(color, variant) {
34478
34562
  const textColor = contrastTextColor(color);
34479
34563
  const variants = {
34480
34564
  filled: `
@@ -34490,11 +34574,11 @@ class YumeTag extends HTMLElement {
34490
34574
  .remove { color: ${color}; }
34491
34575
  `,
34492
34576
  };
34493
- return variants[styleType] || variants.filled;
34577
+ return variants[variant] || variants.filled;
34494
34578
  }
34495
34579
 
34496
34580
  _getStyle() {
34497
- const { color, styleType, shape, size } = this;
34581
+ const { color, variant, shape, size } = this;
34498
34582
 
34499
34583
  const vars = {
34500
34584
  primary: [
@@ -34596,7 +34680,7 @@ class YumeTag extends HTMLElement {
34596
34680
  `;
34597
34681
 
34598
34682
  if (isCustomColor)
34599
- return baseStyle + this._getCustomColorVariant(color, styleType);
34683
+ return baseStyle + this._getCustomColorVariant(color, variant);
34600
34684
 
34601
34685
  const [content, inverse, flatBackground] = varEntry || vars.base;
34602
34686
 
@@ -34615,7 +34699,16 @@ class YumeTag extends HTMLElement {
34615
34699
  `,
34616
34700
  };
34617
34701
 
34618
- return baseStyle + (styleVariants[styleType] || styleVariants.filled);
34702
+ return baseStyle + (styleVariants[variant] || styleVariants.filled);
34703
+ }
34704
+
34705
+ _warnStyleTypeDeprecated() {
34706
+ if (YumeTag._styleTypeDeprecationWarned) return;
34707
+
34708
+ YumeTag._styleTypeDeprecationWarned = true;
34709
+ console.warn(
34710
+ 'y-tag: the "style-type" attribute is deprecated and will be removed in a future major version. Use "variant" instead.',
34711
+ );
34619
34712
  }
34620
34713
  }
34621
34714
 
@@ -34625,7 +34718,7 @@ if (!customElements.get("y-tag")) {
34625
34718
 
34626
34719
  class YumeTabs extends HTMLElement {
34627
34720
  static get observedAttributes() {
34628
- return ["options", "size", "position", "variant"];
34721
+ return ["options", "size", "position", "variant", "overflow"];
34629
34722
  }
34630
34723
 
34631
34724
  // -------------------------------------------------------------------------
@@ -34637,15 +34730,23 @@ class YumeTabs extends HTMLElement {
34637
34730
  this.attachShadow({ mode: "open" });
34638
34731
  this._activeTab = "";
34639
34732
  this._warnedSlots = new Set();
34733
+ this._resizeObserver = null;
34734
+ this._onTablistScroll = this._onTablistScroll.bind(this);
34640
34735
  }
34641
34736
 
34642
34737
  connectedCallback() {
34643
34738
  if (!this.hasAttribute("size")) this.setAttribute("size", "medium");
34644
34739
  if (!this.hasAttribute("position"))
34645
34740
  this.setAttribute("position", "top");
34741
+ if (!this.hasAttribute("overflow"))
34742
+ this.setAttribute("overflow", "scroll");
34646
34743
  this.render();
34647
34744
  }
34648
34745
 
34746
+ disconnectedCallback() {
34747
+ this._teardownScroll();
34748
+ }
34749
+
34649
34750
  attributeChangedCallback(name, oldVal, newVal) {
34650
34751
  if (oldVal === newVal) return;
34651
34752
  if (name === "options") this._warnedSlots.clear();
@@ -34671,6 +34772,19 @@ class YumeTabs extends HTMLElement {
34671
34772
  this.render();
34672
34773
  }
34673
34774
 
34775
+ /**
34776
+ * @type {"scroll"|"wrap"} How a tab strip wider (or taller) than its
34777
+ * container behaves. `"scroll"` keeps tabs on a single line and reveals
34778
+ * prev/next arrow buttons when the strip overflows; `"wrap"` lets tabs flow
34779
+ * onto multiple rows (or columns, for left/right positions).
34780
+ */
34781
+ get overflow() {
34782
+ return this.getAttribute("overflow") === "wrap" ? "wrap" : "scroll";
34783
+ }
34784
+ set overflow(val) {
34785
+ this.setAttribute("overflow", val === "wrap" ? "wrap" : "scroll");
34786
+ }
34787
+
34674
34788
  /** @type {"top"|"bottom"|"left"|"right"} Which edge the tab strip is placed on. */
34675
34789
  get position() {
34676
34790
  const pos = this.getAttribute("position");
@@ -34729,6 +34843,7 @@ class YumeTabs extends HTMLElement {
34729
34843
 
34730
34844
  const activeDef = tabs.find((t) => t.id === this._activeTab);
34731
34845
 
34846
+ this._teardownScroll();
34732
34847
  this.shadowRoot.innerHTML = "";
34733
34848
 
34734
34849
  const style = document.createElement("style");
@@ -34741,11 +34856,12 @@ class YumeTabs extends HTMLElement {
34741
34856
  part: "tablist",
34742
34857
  });
34743
34858
  tabs.forEach((tab) => tablist.appendChild(this._createTabButton(tab)));
34744
- this.shadowRoot.appendChild(tablist);
34745
34859
 
34860
+ this.shadowRoot.appendChild(this._buildTabStrip(tablist));
34746
34861
  this.shadowRoot.appendChild(this._createPanel(activeDef?.slot || ""));
34747
34862
 
34748
34863
  this._setupEvents();
34864
+ this._setupScroll();
34749
34865
  }
34750
34866
 
34751
34867
  // -------------------------------------------------------------------------
@@ -34770,6 +34886,47 @@ class YumeTabs extends HTMLElement {
34770
34886
  parent.appendChild(createElement("slot", { name: slotName, class: "icon-slot" }));
34771
34887
  }
34772
34888
 
34889
+ _buildScrollButton(direction) {
34890
+ const vertical = this.position === "left" || this.position === "right";
34891
+ const icon =
34892
+ direction === "prev"
34893
+ ? vertical
34894
+ ? "chevron-up"
34895
+ : "chevron-left"
34896
+ : vertical
34897
+ ? "chevron-down"
34898
+ : "chevron-right";
34899
+ const btn = createElement(
34900
+ "button",
34901
+ {
34902
+ type: "button",
34903
+ class: `scroll-btn scroll-${direction}`,
34904
+ part: `scroll-button scroll-${direction}`,
34905
+ "aria-label":
34906
+ direction === "prev"
34907
+ ? "Scroll tabs backward"
34908
+ : "Scroll tabs forward",
34909
+ tabindex: "-1",
34910
+ hidden: "",
34911
+ },
34912
+ [createElement("y-icon", { name: icon, size: this.size, "aria-hidden": "true" })],
34913
+ );
34914
+ btn.addEventListener("click", () => this._scrollTabs(direction));
34915
+ return btn;
34916
+ }
34917
+
34918
+ _buildTabStrip(tablist) {
34919
+ const strip = createElement("div", { class: "tabstrip", part: "tabstrip" });
34920
+ if (this.overflow === "scroll") {
34921
+ strip.appendChild(this._buildScrollButton("prev"));
34922
+ strip.appendChild(tablist);
34923
+ strip.appendChild(this._buildScrollButton("next"));
34924
+ } else {
34925
+ strip.appendChild(tablist);
34926
+ }
34927
+ return strip;
34928
+ }
34929
+
34773
34930
  _createIcon(name) {
34774
34931
  return createElement("y-icon", { name, size: this.size });
34775
34932
  }
@@ -34850,16 +35007,63 @@ class YumeTabs extends HTMLElement {
34850
35007
  :host([position="left"]) { flex-direction: row; }
34851
35008
  :host([position="right"]) { flex-direction: row-reverse; }
34852
35009
 
35010
+ .tabstrip {
35011
+ display: flex;
35012
+ position: relative;
35013
+ z-index: 1;
35014
+ min-width: 0;
35015
+ min-height: 0;
35016
+ }
35017
+ :host([position="left"]) .tabstrip,
35018
+ :host([position="right"]) .tabstrip { flex-direction: column; }
35019
+ :host([position="top"]) .tabstrip { margin-bottom: -1px; }
35020
+ :host([position="bottom"]) .tabstrip { margin-top: -1px; }
35021
+ :host([position="left"]) .tabstrip { margin-right: -1px; }
35022
+ :host([position="right"]) .tabstrip { margin-left: -1px; }
35023
+
34853
35024
  .tablist {
34854
35025
  display: flex;
34855
35026
  gap: 0;
34856
35027
  position: relative;
34857
- z-index: 1;
35028
+ min-width: 0;
35029
+ min-height: 0;
35030
+ }
35031
+ :host([position="left"]) .tablist,
35032
+ :host([position="right"]) .tablist { flex-direction: column; }
35033
+
35034
+ /* Scroll mode: tabs stay on one line; arrow buttons drive scrolling,
35035
+ so the native scrollbar is hidden. */
35036
+ :host([overflow="scroll"]) .tablist {
35037
+ flex: 1 1 auto;
35038
+ overflow: auto;
35039
+ scrollbar-width: none;
35040
+ -ms-overflow-style: none;
35041
+ }
35042
+ :host([overflow="scroll"]) .tablist::-webkit-scrollbar { display: none; }
35043
+
35044
+ /* Wrap mode: tabs flow onto multiple rows (or columns). */
35045
+ :host([overflow="wrap"]) .tablist { flex-wrap: wrap; }
35046
+
35047
+ .scroll-btn {
35048
+ flex: 0 0 auto;
35049
+ display: inline-flex;
35050
+ align-items: center;
35051
+ justify-content: center;
35052
+ padding: 0 4px;
35053
+ background: var(--component-tabs-inactive-background, var(--component-tabs-border-color));
35054
+ color: var(--component-tabs-color);
35055
+ border: 1px solid var(--component-tabs-border-color);
35056
+ border-width: var(--component-tabs-border-width, var(--component-tab-border-width, 1px));
35057
+ cursor: pointer;
35058
+ font-family: inherit;
34858
35059
  }
34859
- :host([position="top"]) .tablist { margin-bottom: -1px; margin-top: 0; }
34860
- :host([position="bottom"]) .tablist { margin-top: -1px; margin-bottom: 0; }
34861
- :host([position="left"]) .tablist { flex-direction: column; margin-right: -1px; margin-left: 0; }
34862
- :host([position="right"]) .tablist { flex-direction: column; margin-left: -1px; margin-right: 0; }
35060
+ .scroll-btn[hidden] { display: none; }
35061
+ .scroll-btn:hover { background: var(--component-tabs-background); }
35062
+ .scroll-btn:focus-visible {
35063
+ outline: 2px solid var(--component-tabs-accent);
35064
+ outline-offset: -1px;
35065
+ }
35066
+ :host([variant="accent"]) .scroll-btn { background: transparent; border: none; }
34863
35067
 
34864
35068
  :host([position="top"]) .tablist button { border-bottom: none; }
34865
35069
  :host([position="bottom"]) .tablist button { border-top: none; }
@@ -34975,6 +35179,10 @@ class YumeTabs extends HTMLElement {
34975
35179
  }
34976
35180
  }
34977
35181
 
35182
+ _onTablistScroll() {
35183
+ this._updateScrollButtons();
35184
+ }
35185
+
34978
35186
  _resolveActiveTab(tabs) {
34979
35187
  const currentInvalid =
34980
35188
  !this._activeTab ||
@@ -34984,8 +35192,26 @@ class YumeTabs extends HTMLElement {
34984
35192
  }
34985
35193
  }
34986
35194
 
35195
+ _scrollTabs(direction) {
35196
+ const tablist = this.shadowRoot.querySelector(".tablist");
35197
+ if (!tablist) return;
35198
+
35199
+ const vertical = this.position === "left" || this.position === "right";
35200
+ const amount =
35201
+ (vertical ? tablist.clientHeight : tablist.clientWidth) * 0.75;
35202
+ const delta = direction === "prev" ? -amount : amount;
35203
+
35204
+ tablist.scrollBy(
35205
+ vertical
35206
+ ? { top: delta, behavior: "smooth" }
35207
+ : { left: delta, behavior: "smooth" },
35208
+ );
35209
+ }
35210
+
34987
35211
  _setupEvents() {
34988
- const buttons = Array.from(this.shadowRoot.querySelectorAll("button"));
35212
+ const buttons = Array.from(
35213
+ this.shadowRoot.querySelectorAll(".tablist button"),
35214
+ );
34989
35215
  buttons.forEach((button) => {
34990
35216
  if (button.disabled) return;
34991
35217
  button.addEventListener("click", () =>
@@ -34996,6 +35222,58 @@ class YumeTabs extends HTMLElement {
34996
35222
  );
34997
35223
  });
34998
35224
  }
35225
+
35226
+ _setupScroll() {
35227
+ if (this.overflow !== "scroll") return;
35228
+
35229
+ const tablist = this.shadowRoot.querySelector(".tablist");
35230
+ if (!tablist) return;
35231
+
35232
+ tablist.addEventListener("scroll", this._onTablistScroll, {
35233
+ passive: true,
35234
+ });
35235
+ if (typeof ResizeObserver !== "undefined") {
35236
+ this._resizeObserver = new ResizeObserver(() =>
35237
+ this._updateScrollButtons(),
35238
+ );
35239
+ this._resizeObserver.observe(tablist);
35240
+ this._resizeObserver.observe(this);
35241
+ }
35242
+
35243
+ this._updateScrollButtons();
35244
+ }
35245
+
35246
+ _teardownScroll() {
35247
+ this._resizeObserver?.disconnect();
35248
+ this._resizeObserver = null;
35249
+ const tablist = this.shadowRoot?.querySelector(".tablist");
35250
+ tablist?.removeEventListener("scroll", this._onTablistScroll);
35251
+ }
35252
+
35253
+ _updateScrollButtons() {
35254
+ const tablist = this.shadowRoot.querySelector(".tablist");
35255
+ const prev = this.shadowRoot.querySelector(".scroll-prev");
35256
+ const next = this.shadowRoot.querySelector(".scroll-next");
35257
+ if (!tablist || !prev || !next) return;
35258
+
35259
+ const vertical = this.position === "left" || this.position === "right";
35260
+ const scrollSize = vertical
35261
+ ? tablist.scrollHeight
35262
+ : tablist.scrollWidth;
35263
+ const clientSize = vertical
35264
+ ? tablist.clientHeight
35265
+ : tablist.clientWidth;
35266
+ const scrollPos = vertical ? tablist.scrollTop : tablist.scrollLeft;
35267
+
35268
+ if (scrollSize - clientSize <= 1) {
35269
+ prev.hidden = true;
35270
+ next.hidden = true;
35271
+ return;
35272
+ }
35273
+
35274
+ prev.hidden = scrollPos <= 1;
35275
+ next.hidden = scrollPos >= scrollSize - clientSize - 1;
35276
+ }
34999
35277
  }
35000
35278
 
35001
35279
  if (!customElements.get("y-tabs")) {