tyrell-components 1.0.0-TC54 → 1.0.0-TC56

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 (239) hide show
  1. package/README.md +53 -53
  2. package/css/tyrell-theme.css +7 -2
  3. package/css/tyrell.css +6 -15
  4. package/dist/tyrell-theme.css +1 -1
  5. package/dist/tyrell.css +1 -1
  6. package/dist/tyrell.js +1 -1
  7. package/lib/base/ty-component.d.ts +14 -0
  8. package/lib/base/ty-component.d.ts.map +1 -0
  9. package/lib/base/ty-component.js +27 -0
  10. package/lib/base/ty-component.js.map +1 -0
  11. package/lib/components/button.d.ts.map +1 -0
  12. package/lib/components/button.js +7 -0
  13. package/lib/components/button.js.map +1 -0
  14. package/lib/components/calendar-month.d.ts.map +1 -0
  15. package/lib/components/calendar-month.js +20 -6
  16. package/lib/components/calendar-month.js.map +1 -0
  17. package/lib/components/calendar-navigation.d.ts +11 -0
  18. package/lib/components/calendar-navigation.d.ts.map +1 -0
  19. package/lib/components/calendar-navigation.js +51 -40
  20. package/lib/components/calendar-navigation.js.map +1 -0
  21. package/lib/components/calendar.d.ts +31 -3
  22. package/lib/components/calendar.d.ts.map +1 -0
  23. package/lib/components/calendar.js +197 -18
  24. package/lib/components/calendar.js.map +1 -0
  25. package/lib/components/checkbox.d.ts.map +1 -0
  26. package/lib/components/checkbox.js +3 -3
  27. package/lib/components/checkbox.js.map +1 -0
  28. package/lib/components/copy.d.ts.map +1 -0
  29. package/lib/components/copy.js +14 -6
  30. package/lib/components/copy.js.map +1 -0
  31. package/lib/components/date-picker.d.ts +15 -9
  32. package/lib/components/date-picker.d.ts.map +1 -0
  33. package/lib/components/date-picker.js +187 -236
  34. package/lib/components/date-picker.js.map +1 -0
  35. package/lib/components/dropdown.d.ts +403 -0
  36. package/lib/components/dropdown.d.ts.map +1 -0
  37. package/lib/components/dropdown.js +1315 -0
  38. package/lib/components/dropdown.js.map +1 -0
  39. package/lib/components/file-upload.d.ts +4 -0
  40. package/lib/components/file-upload.js +14 -0
  41. package/lib/components/icon.d.ts.map +1 -0
  42. package/lib/components/icon.js.map +1 -0
  43. package/lib/components/input.d.ts.map +1 -0
  44. package/lib/components/input.js +3 -3
  45. package/lib/components/input.js.map +1 -0
  46. package/lib/components/modal.d.ts +2 -8
  47. package/lib/components/modal.d.ts.map +1 -0
  48. package/lib/components/modal.js +8 -18
  49. package/lib/components/modal.js.map +1 -0
  50. package/lib/components/multiselect.d.ts +389 -0
  51. package/lib/components/multiselect.d.ts.map +1 -0
  52. package/lib/components/multiselect.js +1301 -0
  53. package/lib/components/multiselect.js.map +1 -0
  54. package/lib/components/option.d.ts.map +1 -0
  55. package/lib/components/option.js +1 -1
  56. package/lib/components/option.js.map +1 -0
  57. package/lib/components/popup.d.ts.map +1 -0
  58. package/lib/components/popup.js +4 -1
  59. package/lib/components/popup.js.map +1 -0
  60. package/lib/components/radio.d.ts +15 -0
  61. package/lib/components/radio.js +34 -4
  62. package/lib/components/resize-observer.d.ts.map +1 -0
  63. package/lib/components/resize-observer.js.map +1 -0
  64. package/lib/components/scroll-container.d.ts +3 -2
  65. package/lib/components/scroll-container.d.ts.map +1 -0
  66. package/lib/components/scroll-container.js +14 -19
  67. package/lib/components/scroll-container.js.map +1 -0
  68. package/lib/components/select.d.ts +52 -16
  69. package/lib/components/select.js +170 -67
  70. package/lib/components/step.d.ts.map +1 -0
  71. package/lib/components/step.js.map +1 -0
  72. package/lib/components/switch.js +3 -3
  73. package/lib/components/tab.d.ts +49 -1
  74. package/lib/components/tab.d.ts.map +1 -0
  75. package/lib/components/tab.js +151 -10
  76. package/lib/components/tab.js.map +1 -0
  77. package/lib/components/tabs.d.ts +14 -2
  78. package/lib/components/tabs.d.ts.map +1 -0
  79. package/lib/components/tabs.js +186 -130
  80. package/lib/components/tabs.js.map +1 -0
  81. package/lib/components/tag.d.ts +4 -7
  82. package/lib/components/tag.d.ts.map +1 -0
  83. package/lib/components/tag.js +6 -7
  84. package/lib/components/tag.js.map +1 -0
  85. package/lib/components/textarea.d.ts +4 -3
  86. package/lib/components/textarea.d.ts.map +1 -0
  87. package/lib/components/textarea.js +16 -15
  88. package/lib/components/textarea.js.map +1 -0
  89. package/lib/components/tooltip.d.ts +4 -3
  90. package/lib/components/tooltip.d.ts.map +1 -0
  91. package/lib/components/tooltip.js +34 -24
  92. package/lib/components/tooltip.js.map +1 -0
  93. package/lib/components/wizard.d.ts +5 -4
  94. package/lib/components/wizard.d.ts.map +1 -0
  95. package/lib/components/wizard.js +120 -156
  96. package/lib/components/wizard.js.map +1 -0
  97. package/lib/icons/fontawesome/brands.d.ts.map +1 -0
  98. package/lib/icons/fontawesome/brands.js.map +1 -0
  99. package/lib/icons/fontawesome/regular.d.ts.map +1 -0
  100. package/lib/icons/fontawesome/regular.js.map +1 -0
  101. package/lib/icons/fontawesome/solid.d.ts.map +1 -0
  102. package/lib/icons/fontawesome/solid.js.map +1 -0
  103. package/lib/icons/heroicons/micro.d.ts.map +1 -0
  104. package/lib/icons/heroicons/micro.js.map +1 -0
  105. package/lib/icons/heroicons/mini.d.ts.map +1 -0
  106. package/lib/icons/heroicons/mini.js.map +1 -0
  107. package/lib/icons/heroicons/outline.d.ts.map +1 -0
  108. package/lib/icons/heroicons/outline.js.map +1 -0
  109. package/lib/icons/heroicons/solid.d.ts.map +1 -0
  110. package/lib/icons/heroicons/solid.js.map +1 -0
  111. package/lib/icons/lucide.d.ts.map +1 -0
  112. package/lib/icons/lucide.js.map +1 -0
  113. package/lib/icons/material/filled.d.ts.map +1 -0
  114. package/lib/icons/material/filled.js.map +1 -0
  115. package/lib/icons/material/outlined.d.ts.map +1 -0
  116. package/lib/icons/material/outlined.js.map +1 -0
  117. package/lib/icons/material/round.d.ts.map +1 -0
  118. package/lib/icons/material/round.js.map +1 -0
  119. package/lib/icons/material/sharp.d.ts.map +1 -0
  120. package/lib/icons/material/sharp.js.map +1 -0
  121. package/lib/icons/material/two-tone.d.ts.map +1 -0
  122. package/lib/icons/material/two-tone.js.map +1 -0
  123. package/lib/index.d.ts +1 -1
  124. package/lib/index.d.ts.map +1 -0
  125. package/lib/index.js.map +1 -0
  126. package/lib/styles/button.d.ts.map +1 -0
  127. package/lib/styles/button.js +68 -58
  128. package/lib/styles/button.js.map +1 -0
  129. package/lib/styles/calendar-month.d.ts +3 -0
  130. package/lib/styles/calendar-month.d.ts.map +1 -0
  131. package/lib/styles/calendar-month.js +47 -31
  132. package/lib/styles/calendar-month.js.map +1 -0
  133. package/lib/styles/calendar-navigation.d.ts +1 -1
  134. package/lib/styles/calendar-navigation.d.ts.map +1 -0
  135. package/lib/styles/calendar-navigation.js +55 -22
  136. package/lib/styles/calendar-navigation.js.map +1 -0
  137. package/lib/styles/calendar.d.ts +3 -2
  138. package/lib/styles/calendar.d.ts.map +1 -0
  139. package/lib/styles/calendar.js +75 -0
  140. package/lib/styles/calendar.js.map +1 -0
  141. package/lib/styles/checkbox.js +22 -10
  142. package/lib/styles/copy.d.ts.map +1 -0
  143. package/lib/styles/copy.js +5 -1
  144. package/lib/styles/copy.js.map +1 -0
  145. package/lib/styles/date-picker.d.ts.map +1 -0
  146. package/lib/styles/date-picker.js +52 -22
  147. package/lib/styles/date-picker.js.map +1 -0
  148. package/lib/styles/dropdown.d.ts +12 -0
  149. package/lib/styles/dropdown.d.ts.map +1 -0
  150. package/lib/styles/dropdown.js +725 -0
  151. package/lib/styles/dropdown.js.map +1 -0
  152. package/lib/styles/icon.d.ts.map +1 -0
  153. package/lib/styles/icon.js.map +1 -0
  154. package/lib/styles/input.d.ts.map +1 -0
  155. package/lib/styles/input.js.map +1 -0
  156. package/lib/styles/modal.d.ts.map +1 -0
  157. package/lib/styles/modal.js.map +1 -0
  158. package/lib/styles/multiselect.d.ts +6 -0
  159. package/lib/styles/multiselect.d.ts.map +1 -0
  160. package/lib/styles/multiselect.js +568 -0
  161. package/lib/styles/multiselect.js.map +1 -0
  162. package/lib/styles/option.d.ts +1 -1
  163. package/lib/styles/option.d.ts.map +1 -0
  164. package/lib/styles/option.js +3 -3
  165. package/lib/styles/option.js.map +1 -0
  166. package/lib/styles/popup.d.ts +1 -1
  167. package/lib/styles/popup.d.ts.map +1 -0
  168. package/lib/styles/popup.js +8 -5
  169. package/lib/styles/popup.js.map +1 -0
  170. package/lib/styles/radio.js +8 -2
  171. package/lib/styles/resize-observer.d.ts.map +1 -0
  172. package/lib/styles/resize-observer.js.map +1 -0
  173. package/lib/styles/scroll-container.d.ts +1 -1
  174. package/lib/styles/scroll-container.d.ts.map +1 -0
  175. package/lib/styles/scroll-container.js +1 -1
  176. package/lib/styles/scroll-container.js.map +1 -0
  177. package/lib/styles/select-base.js +81 -14
  178. package/lib/styles/select.d.ts +1 -1
  179. package/lib/styles/select.js +6 -0
  180. package/lib/styles/step.d.ts +1 -1
  181. package/lib/styles/step.d.ts.map +1 -0
  182. package/lib/styles/step.js +1 -9
  183. package/lib/styles/step.js.map +1 -0
  184. package/lib/styles/switch.js +6 -3
  185. package/lib/styles/tab.d.ts +1 -1
  186. package/lib/styles/tab.d.ts.map +1 -0
  187. package/lib/styles/tab.js +67 -3
  188. package/lib/styles/tab.js.map +1 -0
  189. package/lib/styles/tabs.d.ts +1 -1
  190. package/lib/styles/tabs.d.ts.map +1 -0
  191. package/lib/styles/tabs.js +7 -3
  192. package/lib/styles/tabs.js.map +1 -0
  193. package/lib/styles/tag.d.ts.map +1 -0
  194. package/lib/styles/tag.js +1 -2
  195. package/lib/styles/tag.js.map +1 -0
  196. package/lib/styles/textarea.d.ts.map +1 -0
  197. package/lib/styles/textarea.js.map +1 -0
  198. package/lib/styles/tooltip.d.ts.map +1 -0
  199. package/lib/styles/tooltip.js.map +1 -0
  200. package/lib/styles/wizard.d.ts +9 -5
  201. package/lib/styles/wizard.d.ts.map +1 -0
  202. package/lib/styles/wizard.js +211 -99
  203. package/lib/styles/wizard.js.map +1 -0
  204. package/lib/types/common.d.ts +2 -2
  205. package/lib/types/common.d.ts.map +1 -0
  206. package/lib/types/common.js.map +1 -0
  207. package/lib/utils/calendar-utils.d.ts.map +1 -0
  208. package/lib/utils/calendar-utils.js.map +1 -0
  209. package/lib/utils/icon-registry.d.ts.map +1 -0
  210. package/lib/utils/icon-registry.js.map +1 -0
  211. package/lib/utils/locale.d.ts.map +1 -0
  212. package/lib/utils/locale.js.map +1 -0
  213. package/lib/utils/number-format.d.ts.map +1 -0
  214. package/lib/utils/number-format.js.map +1 -0
  215. package/lib/utils/parse-boolean.d.ts +39 -0
  216. package/lib/utils/parse-boolean.d.ts.map +1 -0
  217. package/lib/utils/parse-boolean.js +23 -0
  218. package/lib/utils/parse-boolean.js.map +1 -0
  219. package/lib/utils/positioning.d.ts.map +1 -0
  220. package/lib/utils/positioning.js.map +1 -0
  221. package/lib/utils/property-capture.d.ts.map +1 -0
  222. package/lib/utils/property-capture.js.map +1 -0
  223. package/lib/utils/property-manager.d.ts +1 -1
  224. package/lib/utils/property-manager.d.ts.map +1 -0
  225. package/lib/utils/property-manager.js +2 -0
  226. package/lib/utils/property-manager.js.map +1 -0
  227. package/lib/utils/resize-observer.d.ts.map +1 -0
  228. package/lib/utils/resize-observer.js.map +1 -0
  229. package/lib/utils/scroll-lock.d.ts.map +1 -0
  230. package/lib/utils/scroll-lock.js.map +1 -0
  231. package/lib/utils/styles.d.ts.map +1 -0
  232. package/lib/utils/styles.js.map +1 -0
  233. package/lib/utils/validity.d.ts +26 -0
  234. package/lib/utils/validity.js +53 -0
  235. package/lib/version.d.ts +1 -1
  236. package/lib/version.d.ts.map +1 -0
  237. package/lib/version.js +1 -1
  238. package/lib/version.js.map +1 -0
  239. package/package.json +25 -4
@@ -20,10 +20,10 @@ elementIds.set(element, id);
20
20
  return id;
21
21
  }
22
22
  import { REQUIRED_ICON_SVG } from "../utils/icons.js";
23
+ const SEARCH_AUTO_THRESHOLD = 7;
23
24
  const CHEVRON_DOWN_SVG = `<svg viewBox="0 0 20 20" fill="currentColor">
24
25
  <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
25
26
  </svg>`;
26
- const SEARCH_AUTO_THRESHOLD = 7;
27
27
  function slugify(text) {
28
28
  return text
29
29
  .trim()
@@ -55,11 +55,12 @@ this._clearable = true;
55
55
  this._externalSearch = false;
56
56
  this._allowCreate = false;
57
57
  this._createTransform = "none";
58
- this._searchable = "auto";
58
+ this._notSearchable = false;
59
59
  this._loading = false;
60
60
  this._scrollLockId = null;
61
61
  this._size = DEFAULT_SIZE;
62
62
  this._availableLabel = "Available";
63
+ this._selectedLabel = "Selected";
63
64
  this._noOptionsMessage = "No options available";
64
65
  this._state = {
65
66
  open: false,
@@ -177,16 +178,16 @@ break;
177
178
  case "required":
178
179
  this._required = newValue;
179
180
  break;
180
- case "clearable":
181
- this._clearable = newValue;
181
+ case "noClear":
182
+ this._clearable = !newValue;
182
183
  if (this.isConnected && this.shadowRoot)
183
184
  this.updateSelectionDisplay();
184
185
  break;
185
186
  case "externalSearch":
186
187
  this._externalSearch = newValue;
187
188
  break;
188
- case "searchable":
189
- this._searchable = newValue;
189
+ case "notSearchable":
190
+ this._notSearchable = newValue;
190
191
  if (this.isConnected && this.shadowRoot)
191
192
  this.updateSearchVisibility();
192
193
  break;
@@ -207,6 +208,9 @@ break;
207
208
  case "available-label":
208
209
  this._availableLabel = newValue || "Available";
209
210
  break;
211
+ case "selected-label":
212
+ this._selectedLabel = newValue || "Selected";
213
+ break;
210
214
  case "no-options-message":
211
215
  this._noOptionsMessage = newValue || "No options available";
212
216
  break;
@@ -698,6 +702,8 @@ e.stopPropagation();
698
702
  e.preventDefault();
699
703
  }
700
704
  handleSearchInput(e) {
705
+ if (this._state.mode === "mobile")
706
+ this.expandMobileSection("available");
701
707
  const target = e.target;
702
708
  const query = target.value;
703
709
  this._state.search = query;
@@ -730,7 +736,12 @@ switch (e.key) {
730
736
  case "Escape":
731
737
  e.preventDefault();
732
738
  e.stopPropagation();
739
+ if (this._state.mode === "mobile") {
740
+ this.closeMobileModal();
741
+ }
742
+ else {
733
743
  this.closeDropdown();
744
+ }
734
745
  break;
735
746
  case "Enter":
736
747
  e.preventDefault();
@@ -746,43 +757,33 @@ break;
746
757
  case "ArrowUp":
747
758
  e.preventDefault();
748
759
  e.stopPropagation();
749
- let newIndexUp;
750
- if (tagsCount === 0) {
751
- newIndexUp = -1;
752
- }
753
- else if (currentHighlightedIndex === -1) {
754
- newIndexUp = tagsCount - 1;
755
- }
756
- else if (currentHighlightedIndex === 0) {
757
- newIndexUp = tagsCount - 1;
758
- }
759
- else {
760
- newIndexUp = currentHighlightedIndex - 1;
761
- }
760
+ const newIndexUp = this.nextEnabledTagIndex(filteredTags, currentHighlightedIndex, -1);
762
761
  this._state.highlightedIndex = newIndexUp;
763
762
  this.highlightTag(filteredTags, newIndexUp);
764
763
  break;
765
764
  case "ArrowDown":
766
765
  e.preventDefault();
767
766
  e.stopPropagation();
768
- let newIndexDown;
769
- if (tagsCount === 0) {
770
- newIndexDown = -1;
771
- }
772
- else if (currentHighlightedIndex === -1) {
773
- newIndexDown = 0;
774
- }
775
- else if (currentHighlightedIndex === tagsCount - 1) {
776
- newIndexDown = 0;
777
- }
778
- else {
779
- newIndexDown = currentHighlightedIndex + 1;
780
- }
767
+ const newIndexDown = this.nextEnabledTagIndex(filteredTags, currentHighlightedIndex, 1);
781
768
  this._state.highlightedIndex = newIndexDown;
782
769
  this.highlightTag(filteredTags, newIndexDown);
783
770
  break;
784
771
  }
785
772
  }
773
+ nextEnabledTagIndex(tags, fromIndex, direction) {
774
+ const count = tags.length;
775
+ if (count === 0)
776
+ return -1;
777
+ let index = fromIndex === -1
778
+ ? (direction === 1 ? 0 : count - 1)
779
+ : (fromIndex + direction + count) % count;
780
+ for (let i = 0; i < count; i++) {
781
+ if (!tags[index].element.hasAttribute("disabled"))
782
+ return index;
783
+ index = (index + direction + count) % count;
784
+ }
785
+ return -1;
786
+ }
786
787
  filterTags(tags, query) {
787
788
  if (!query || query.trim() === "") {
788
789
  return tags;
@@ -880,19 +881,58 @@ this.renderDesktop();
880
881
  }
881
882
  this.updateSearchVisibility();
882
883
  this.applyLoadingState();
884
+ this.syncFieldChrome();
883
885
  this.updateValidity();
884
886
  }
887
+ syncFieldChrome() {
888
+ const shadow = this.shadowRoot;
889
+ const container = shadow?.querySelector(".select-container");
890
+ if (!shadow || !container)
891
+ return;
892
+ const labelId = `select-label-${getElementHash(this)}`;
893
+ let label = container.querySelector(":scope > .ty-field-label");
894
+ if (this._label) {
895
+ if (!label) {
896
+ label = document.createElement("label");
897
+ label.className = "ty-field-label";
898
+ label.id = labelId;
899
+ container.prepend(label);
900
+ }
901
+ label.innerHTML = `${this._label}${this._required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ""}`;
902
+ }
903
+ else {
904
+ label?.remove();
905
+ }
906
+ const stub = shadow.querySelector(".select-stub");
907
+ if (stub) {
908
+ stub.classList.toggle("disabled", this._disabled);
909
+ stub.classList.toggle("compact", this._compact);
910
+ stub.tabIndex = this._disabled ? -1 : 0;
911
+ stub.toggleAttribute("disabled", this._disabled);
912
+ if (this._disabled)
913
+ stub.setAttribute("aria-disabled", "true");
914
+ else
915
+ stub.removeAttribute("aria-disabled");
916
+ if (this._label)
917
+ stub.setAttribute("aria-labelledby", labelId);
918
+ else
919
+ stub.removeAttribute("aria-labelledby");
920
+ }
921
+ shadow
922
+ .querySelectorAll(".dropdown-search-input, .mobile-search-input")
923
+ .forEach((input) => (input.disabled = this._disabled));
924
+ }
885
925
  updateValidity() {
886
926
  if (this.disabled) {
887
- this._internals.setValidity({});
927
+ this.applyValidity({});
888
928
  return;
889
929
  }
890
930
  if (this.required && this.getSelectedValues().length === 0) {
891
931
  const stub = this.shadowRoot?.querySelector(".select-stub");
892
- this._internals.setValidity({ valueMissing: true }, "Please select a value", stub ?? undefined);
932
+ this.applyValidity({ valueMissing: true }, "Please select a value", stub ?? undefined);
893
933
  }
894
934
  else {
895
- this._internals.setValidity({});
935
+ this.applyValidity({});
896
936
  }
897
937
  }
898
938
  setupTriggerSlot() {
@@ -1098,12 +1138,21 @@ ${CHEVRON_DOWN_SVG}
1098
1138
  <!-- HEADER (matches dropdown.ts) -->
1099
1139
  ${searchHeaderHtml}
1100
1140
  <!-- BODY: options list with toggleable selected state -->
1101
- <div class="mobile-body">
1141
+ <div class="mobile-body" data-expanded="available">
1142
+ <!-- SELECTED STRIP (pinned above the list, collapses when empty) -->
1143
+ <div class="mobile-selected-section" data-empty="true">
1144
+ <button class="section-header" type="button" data-section="selected" aria-expanded="false">
1145
+ <span class="section-title">${this._selectedLabel} <span class="section-count">(0)</span></span>
1146
+ ${CHEVRON_DOWN_SVG}
1147
+ </button>
1148
+ <div class="section-content mobile-selected-chips"></div>
1149
+ </div>
1102
1150
  <!-- AVAILABLE LIST (always visible, takes remaining space) -->
1103
1151
  <div class="mobile-available-section" data-empty="false">
1104
- <div class="section-header">
1105
- <span class="section-title">${this._availableLabel}</span>
1106
- </div>
1152
+ <button class="section-header" type="button" data-section="available" aria-expanded="true">
1153
+ <span class="section-title">${this._availableLabel} <span class="section-count"></span></span>
1154
+ ${CHEVRON_DOWN_SVG}
1155
+ </button>
1107
1156
  <div class="section-content dropdown-options-wrapper" id="${listboxId}" role="listbox" ${this._multiple ? 'aria-multiselectable="true"' : ""}>
1108
1157
  <slot id="options-slot"></slot>
1109
1158
  <div class="empty-state">${this._noOptionsMessage}</div>
@@ -1126,6 +1175,10 @@ this.setupMobileEventListeners();
1126
1175
  this.updateSelectionDisplay();
1127
1176
  }
1128
1177
  setupMobileEventListeners() {
1178
+ if (!this._keyboardHandler) {
1179
+ this._keyboardHandler = this.handleKeyboard.bind(this);
1180
+ document.addEventListener("keydown", this._keyboardHandler);
1181
+ }
1129
1182
  const shadow = this.shadowRoot;
1130
1183
  const stub = shadow.querySelector(".select-stub");
1131
1184
  const optionsSlot = shadow.querySelector("#options-slot");
@@ -1145,6 +1198,9 @@ optionsSlot.addEventListener("click", (e) => this.handleMobileTagClick(e));
1145
1198
  if (searchInput) {
1146
1199
  searchInput.addEventListener("input", (e) => this.handleSearchInput(e));
1147
1200
  }
1201
+ shadow.querySelectorAll(".mobile-body .section-header").forEach((header) => {
1202
+ header.addEventListener("click", () => this.expandMobileSection(header.dataset.section === "selected" ? "selected" : "available"));
1203
+ });
1148
1204
  if (closeButton) {
1149
1205
  closeButton.addEventListener("click", () => this.closeMobileModal());
1150
1206
  }
@@ -1183,24 +1239,74 @@ updateMobileSelectedState() {
1183
1239
  if (this._state.mode !== "mobile")
1184
1240
  return;
1185
1241
  const shadow = this.shadowRoot;
1242
+ this.renderMobileSelectedChips();
1186
1243
  const availableSection = shadow.querySelector(".mobile-available-section");
1187
1244
  if (availableSection) {
1188
- const visibleAvailable = this.getTagElements().filter((tag) => !tag.hasAttribute("hidden")).length;
1245
+ const visibleAvailable = this.getTagElements().filter((tag) => !tag.hasAttribute("hidden") && !tag.hasAttribute("selected")).length;
1189
1246
  availableSection.setAttribute("data-empty", String(visibleAvailable === 0));
1190
- const availableTitleSpan = shadow.querySelector(".mobile-available-section .section-title");
1191
- if (availableTitleSpan) {
1192
- availableTitleSpan.textContent = `${this._availableLabel} (${visibleAvailable})`;
1247
+ this.setMobileSectionTitle(availableSection, this._availableLabel, visibleAvailable);
1193
1248
  }
1194
1249
  }
1250
+ setMobileSectionTitle(section, label, n) {
1251
+ const title = section.querySelector(".section-title");
1252
+ if (!title)
1253
+ return;
1254
+ const count = document.createElement("span");
1255
+ count.className = "section-count";
1256
+ count.textContent = `(${n})`;
1257
+ title.replaceChildren(`${label} `, count);
1258
+ }
1259
+ expandMobileSection(section) {
1260
+ const shadow = this.shadowRoot;
1261
+ const body = shadow?.querySelector(".mobile-body");
1262
+ if (!body)
1263
+ return;
1264
+ if (section === "selected" && this._state.selectedValues.length === 0)
1265
+ return;
1266
+ body.dataset.expanded = section;
1267
+ shadow.querySelectorAll(".mobile-body .section-header").forEach((header) => {
1268
+ const owns = header.dataset.section === section;
1269
+ header.setAttribute("aria-expanded", String(owns));
1270
+ });
1271
+ }
1272
+ renderMobileSelectedChips() {
1273
+ const shadow = this.shadowRoot;
1274
+ const section = shadow.querySelector(".mobile-selected-section");
1275
+ const host = shadow.querySelector(".mobile-selected-chips");
1276
+ if (!section || !host)
1277
+ return;
1278
+ const selected = this._state.selectedValues;
1279
+ section.setAttribute("data-empty", String(selected.length === 0));
1280
+ this.setMobileSectionTitle(section, this._selectedLabel, selected.length);
1281
+ if (selected.length === 0)
1282
+ this.expandMobileSection("available");
1283
+ const labelFor = (value) => {
1284
+ const option = this.getTagElements().find((tag) => this.getTagData(tag).value === value);
1285
+ return option ? this.getTagData(option).text : value;
1286
+ };
1287
+ host.textContent = "";
1288
+ for (const value of selected) {
1289
+ const chip = document.createElement("button");
1290
+ chip.type = "button";
1291
+ chip.className = "mobile-selected-chip";
1292
+ chip.dataset.value = value;
1293
+ chip.setAttribute("aria-label", `Remove ${labelFor(value)}`);
1294
+ chip.innerHTML = `<span class="chip-label"></span>${CLEAR_ICON_SVG}`;
1295
+ chip.querySelector(".chip-label").textContent = labelFor(value);
1296
+ chip.addEventListener("click", (e) => {
1297
+ e.preventDefault();
1298
+ e.stopPropagation();
1299
+ this.deselectValue(value);
1300
+ });
1301
+ host.appendChild(chip);
1302
+ }
1195
1303
  }
1196
1304
  shouldShowSearch() {
1197
1305
  if (this._externalSearch)
1198
1306
  return true;
1199
1307
  if (this._allowCreate)
1200
1308
  return true;
1201
- if (this._searchable === "always")
1202
- return true;
1203
- if (this._searchable === "never")
1309
+ if (this._notSearchable)
1204
1310
  return false;
1205
1311
  return this.getTagElements().length > SEARCH_AUTO_THRESHOLD;
1206
1312
  }
@@ -1359,11 +1465,11 @@ return this.getProperty("readonly");
1359
1465
  set readonly(value) {
1360
1466
  this.setProperty("readonly", value);
1361
1467
  }
1362
- get clearable() {
1363
- return this.getProperty("clearable");
1468
+ get noClear() {
1469
+ return this.getProperty("noClear");
1364
1470
  }
1365
- set clearable(value) {
1366
- this.setProperty("clearable", value);
1471
+ set noClear(value) {
1472
+ this.setProperty("noClear", value);
1367
1473
  }
1368
1474
  get required() {
1369
1475
  return this.getProperty("required");
@@ -1371,11 +1477,11 @@ return this.getProperty("required");
1371
1477
  set required(value) {
1372
1478
  this.setProperty("required", value);
1373
1479
  }
1374
- get searchable() {
1375
- return this.getProperty("searchable");
1480
+ get notSearchable() {
1481
+ return this.getProperty("notSearchable");
1376
1482
  }
1377
- set searchable(value) {
1378
- this.setProperty("searchable", value);
1483
+ set notSearchable(value) {
1484
+ this.setProperty("notSearchable", value);
1379
1485
  }
1380
1486
  get externalSearch() {
1381
1487
  return this.getProperty("externalSearch");
@@ -1474,11 +1580,10 @@ type: "boolean",
1474
1580
  visual: true,
1475
1581
  default: false,
1476
1582
  },
1477
- clearable: {
1583
+ noClear: {
1478
1584
  type: "boolean",
1479
1585
  visual: true,
1480
- default: true,
1481
- aliases: { "not-clearable": false },
1586
+ default: false,
1482
1587
  },
1483
1588
  externalSearch: {
1484
1589
  type: "boolean",
@@ -1499,17 +1604,10 @@ default: "none",
1499
1604
  aliases: { "create-transform": true },
1500
1605
  coerce: (v) => (v === "slug" ? "slug" : "none"),
1501
1606
  },
1502
- searchable: {
1503
- type: "string",
1607
+ notSearchable: {
1608
+ type: "boolean",
1504
1609
  visual: true,
1505
- default: "auto",
1506
- coerce: (v) => {
1507
- if (v === true || v === "" || v === "true")
1508
- return "always";
1509
- if (v === false || v === "false")
1510
- return "never";
1511
- return "auto";
1512
- },
1610
+ default: false,
1513
1611
  },
1514
1612
  align: {
1515
1613
  type: "string",
@@ -1552,6 +1650,11 @@ type: "string",
1552
1650
  visual: true,
1553
1651
  default: "Available",
1554
1652
  },
1653
+ "selected-label": {
1654
+ type: "string",
1655
+ visual: true,
1656
+ default: "Selected",
1657
+ },
1555
1658
  "no-options-message": {
1556
1659
  type: "string",
1557
1660
  visual: true,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../src/components/step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC3B;AAiED;;GAEG;AACH,qBAAa,MAAO,SAAQ,WAAW;IACrC,0BAA0B;IAC1B,MAAM,KAAK,kBAAkB,aAE5B;;IAOD,iBAAiB;IAIjB,oBAAoB;IAIpB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAG3F"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step.js","sourceRoot":"","sources":["../../src/components/step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAsB/C,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,iBAAiB,CAAC,EAAU;IACnC,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAiB,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAEhF,OAAO;QACL,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC;QACzB,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;QAC/B,WAAW,EAAE,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC;QAC3C,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;QACrC,MAAM,EAAE,UAAU,IAAI,aAAa,CAAC,QAAQ,CAAC,UAAwB,CAAC;YACpE,CAAC,CAAE,UAAyB;YAC5B,CAAC,CAAC,IAAI;KACT,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,MAAM,CAAC,EAAU;IACxB,MAAM,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;IACjC,IAAI,CAAC,UAAU;QAAE,OAAO;IAExB,MAAM,EAAE,EAAE,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAErC,2BAA2B;IAC3B,YAAY,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAE7D,mEAAmE;IACnE,4EAA4E;IAC5E,UAAU,CAAC,SAAS,GAAG;;;WAGd,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE;WAC5B,EAAE,CAAC,CAAC,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE;;;;GAIhD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,GAAW;IAC1B,6BAA6B;AAC/B,CAAC;AAED,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,OAAO,MAAO,SAAQ,WAAW;IACrC,0BAA0B;IAC1B,MAAM,KAAK,kBAAkB;QAC3B,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9D,CAAC;IAED;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,iBAAiB;QACf,MAAM,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,oBAAoB;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,wBAAwB,CAAC,KAAa,EAAE,SAAwB,EAAE,SAAwB;QACxF,MAAM,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;CACF;AAED,8BAA8B;AAC9B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;IACnC,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC"}
@@ -148,14 +148,14 @@ this.updateValidity();
148
148
  updateValidity() {
149
149
  const anchor = this.shadowRoot?.querySelector(".switch-container");
150
150
  if (this.disabled) {
151
- this._internals.setValidity({});
151
+ this.applyValidity({});
152
152
  return;
153
153
  }
154
154
  if (this.required && !this.checked) {
155
- this._internals.setValidity({ valueMissing: true }, "Please turn this on", anchor ?? undefined);
155
+ this.applyValidity({ valueMissing: true }, "Please turn this on", anchor ?? undefined);
156
156
  }
157
157
  else {
158
- this._internals.setValidity({});
158
+ this.applyValidity({});
159
159
  }
160
160
  }
161
161
  get checked() { return this.getProperty("checked"); }
@@ -3,15 +3,63 @@ export interface TabAttributes {
3
3
  id: string | null;
4
4
  label: string | null;
5
5
  disabled: boolean;
6
+ lazy: boolean;
7
+ }
8
+ /**
9
+ * `ty-tab-load`: a lazy tab with no content was shown. Fired on the ty-tab
10
+ * and doesn't bubble, so a lazy tab nested in another doesn't trigger the
11
+ * outer one's listener. Listen on the tab, or on ty-tabs with capture.
12
+ */
13
+ export interface TabLoadDetail {
14
+ id: string;
15
+ index: number;
6
16
  }
7
17
  export declare class TyTab extends HTMLElement {
8
18
  private _scrollbar;
19
+ private _panelLabel;
20
+ private _shown;
21
+ private _load;
22
+ private _contentObserver;
23
+ private _contentChanged;
9
24
  static get observedAttributes(): string[];
10
25
  constructor();
11
26
  connectedCallback(): void;
27
+ /** Accessible name. Set by ty-tabs, which also knows rich `label-{id}` slots. */
28
+ setPanelLabel(label: string): void;
29
+ /** Set by ty-tabs: a panel that isn't shown is inert. */
30
+ setShown(shown: boolean): void;
12
31
  disconnectedCallback(): void;
13
- attributeChangedCallback(_name: string, _oldValue: string | null, _newValue: string | null): void;
32
+ /**
33
+ * Called by ty-tabs when this tab becomes the shown one — never for the
34
+ * tab shown first, whose content the page is expected to carry. A lazy
35
+ * tab with no content (or whose load failed) asks for it.
36
+ */
37
+ _requestLoad(index: number): void;
38
+ /**
39
+ * Report that loading this tab's content failed: shows `slot="error"`
40
+ * (default "Couldn't load.") with a Retry button. Retry, or showing the
41
+ * tab again, fires `ty-tab-load` once more. Content arriving clears it.
42
+ */
43
+ loadFailed(): void;
44
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
14
45
  private _render;
46
+ /** Lazy tabs track whether they have content; others don't watch. */
47
+ private _watchContent;
48
+ private _hasContent;
49
+ /**
50
+ * `mutated`: content changed under a connected tab. Content that goes away
51
+ * (a morph with the server's empty markup) loads again — right away when
52
+ * this tab is the one shown, otherwise the next time it's shown.
53
+ */
54
+ private _checkContent;
55
+ private _fireLoad;
56
+ /**
57
+ * Placeholder and error, built the first time a tab loads: tabs that never
58
+ * do carry no hidden "Couldn't load / Retry" text.
59
+ */
60
+ private _ensureStatus;
61
+ private _setLoad;
62
+ private _syncPanel;
15
63
  private _setupScrollbar;
16
64
  private _destroyScrollbar;
17
65
  resetScroll(): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tab.d.ts","sourceRoot":"","sources":["../../src/components/tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CACnB;AA0DD;;GAEG;AACH,qBAAa,KAAM,SAAQ,WAAW;IACpC,0BAA0B;IAC1B,MAAM,KAAK,kBAAkB,aAE5B;;IAOD,iBAAiB;IAIjB,oBAAoB;IAIpB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAG3F"}