@ethlete/components 1.0.0-next.77 → 1.0.0-next.79

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 (318) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/design/accordion.ts +73 -0
  3. package/design/avatar.ts +90 -0
  4. package/design/badge.ts +48 -0
  5. package/design/banner.ts +77 -0
  6. package/design/bracket.ts +457 -0
  7. package/design/breadcrumb.ts +145 -0
  8. package/design/button.ts +49 -0
  9. package/design/calendar.ts +306 -0
  10. package/design/card.ts +92 -0
  11. package/design/carousel.ts +240 -0
  12. package/design/chart.ts +512 -0
  13. package/design/checkbox.ts +275 -0
  14. package/design/chip.ts +64 -0
  15. package/design/config.json +22 -0
  16. package/design/control.ts +19 -0
  17. package/design/copy-button.ts +59 -0
  18. package/design/description-list.ts +41 -0
  19. package/design/dialog.ts +214 -0
  20. package/design/divider.ts +27 -0
  21. package/design/empty-state.ts +45 -0
  22. package/design/floating-action.ts +55 -0
  23. package/design/form-field.ts +40 -1
  24. package/design/grid.ts +148 -0
  25. package/design/icon-set.ts +19 -0
  26. package/design/icon.ts +9 -0
  27. package/design/input.ts +86 -0
  28. package/design/kbd.ts +19 -0
  29. package/design/loader.ts +152 -0
  30. package/design/masonry.ts +115 -0
  31. package/design/match.ts +131 -0
  32. package/design/menu.ts +143 -0
  33. package/design/notification.ts +129 -0
  34. package/design/otp-input.ts +107 -0
  35. package/design/pagination.ts +120 -0
  36. package/design/picture.ts +80 -0
  37. package/design/progress-steps.ts +79 -0
  38. package/design/query-error.ts +60 -0
  39. package/design/radio-group.ts +132 -0
  40. package/design/scheduler.ts +369 -0
  41. package/design/scrollable.ts +218 -0
  42. package/design/scrollbar.ts +47 -0
  43. package/design/sdk/assets/fallback.svg +1 -0
  44. package/design/sdk/assets/tall.svg +1 -0
  45. package/design/sdk/assets/wide.svg +1 -0
  46. package/design/sdk/components/accent-on-light/accent-on-light.css +35 -0
  47. package/design/sdk/components/accent-on-light/accent-on-light.ts +10 -0
  48. package/design/sdk/components/accent-on-light/shared/controls.ts +46 -0
  49. package/design/sdk/components/accordion/accordion.css +17 -0
  50. package/design/sdk/components/accordion/accordion.ts +64 -0
  51. package/design/sdk/components/avatar/avatar.css +14 -0
  52. package/design/sdk/components/avatar/avatar.ts +78 -0
  53. package/design/sdk/components/badge/badge.css +13 -0
  54. package/design/sdk/components/badge/badge.ts +52 -0
  55. package/design/sdk/components/banner/banner.css +18 -0
  56. package/design/sdk/components/banner/banner.ts +87 -0
  57. package/design/sdk/components/bracket/bracket.css +59 -0
  58. package/design/sdk/components/bracket/bracket.ts +143 -0
  59. package/design/sdk/components/breadcrumb/breadcrumb.css +25 -0
  60. package/design/sdk/components/breadcrumb/breadcrumb.ts +81 -0
  61. package/design/sdk/components/button/button.css +33 -0
  62. package/design/sdk/components/button/button.ts +82 -0
  63. package/design/sdk/components/calendar/calendar.css +33 -0
  64. package/design/sdk/components/calendar/calendar.ts +96 -0
  65. package/design/sdk/components/card/card.css +19 -0
  66. package/design/sdk/components/card/card.ts +61 -0
  67. package/design/sdk/components/carousel/carousel.css +44 -0
  68. package/design/sdk/components/carousel/carousel.ts +74 -0
  69. package/design/sdk/components/chart/chart.css +56 -0
  70. package/design/sdk/components/chart/chart.ts +98 -0
  71. package/design/sdk/components/chip/chip.css +18 -0
  72. package/design/sdk/components/chip/chip.ts +76 -0
  73. package/design/sdk/components/copy-button/copy-button.css +14 -0
  74. package/design/sdk/components/copy-button/copy-button.ts +59 -0
  75. package/design/sdk/components/description-list/description-list.css +7 -0
  76. package/design/sdk/components/description-list/description-list.ts +53 -0
  77. package/design/sdk/components/dialog/dialog.css +29 -0
  78. package/design/sdk/components/dialog/dialog.ts +78 -0
  79. package/design/sdk/components/divider/divider.css +26 -0
  80. package/design/sdk/components/divider/divider.ts +43 -0
  81. package/design/sdk/components/empty-state/empty-state.css +15 -0
  82. package/design/sdk/components/empty-state/empty-state.ts +56 -0
  83. package/design/sdk/components/floating-action/floating-action.css +51 -0
  84. package/design/sdk/components/floating-action/floating-action.ts +63 -0
  85. package/design/sdk/components/form-field/form-field.ts +118 -0
  86. package/design/sdk/components/grid/grid.css +17 -0
  87. package/design/sdk/components/grid/grid.ts +80 -0
  88. package/design/sdk/components/input/input.css +75 -0
  89. package/design/sdk/components/input/input.ts +79 -0
  90. package/design/sdk/components/kbd/kbd.css +25 -0
  91. package/design/sdk/components/kbd/kbd.ts +57 -0
  92. package/design/sdk/components/loader/loader.css +37 -0
  93. package/design/sdk/components/loader/loader.ts +68 -0
  94. package/design/sdk/components/masonry/masonry.css +7 -0
  95. package/design/sdk/components/masonry/masonry.ts +42 -0
  96. package/design/sdk/components/match/match.css +25 -0
  97. package/design/sdk/components/match/match.ts +76 -0
  98. package/design/sdk/components/menu/menu.css +22 -0
  99. package/design/sdk/components/menu/menu.ts +91 -0
  100. package/design/sdk/components/notification/notification.css +32 -0
  101. package/design/sdk/components/notification/notification.ts +106 -0
  102. package/design/sdk/components/otp-input/otp-input.css +35 -0
  103. package/design/sdk/components/otp-input/otp-input.ts +64 -0
  104. package/design/sdk/components/pagination/pagination.css +14 -0
  105. package/design/sdk/components/pagination/pagination.ts +75 -0
  106. package/design/sdk/components/picture/picture.css +37 -0
  107. package/design/sdk/components/picture/picture.ts +96 -0
  108. package/design/sdk/components/progress-steps/progress-steps.css +37 -0
  109. package/design/sdk/components/progress-steps/progress-steps.ts +91 -0
  110. package/design/sdk/components/query-error/query-error.css +33 -0
  111. package/design/sdk/components/query-error/query-error.ts +120 -0
  112. package/design/sdk/components/scheduler/scheduler.css +55 -0
  113. package/design/sdk/components/scheduler/scheduler.ts +239 -0
  114. package/design/sdk/components/scrollable/scrollable.css +58 -0
  115. package/design/sdk/components/scrollable/scrollable.ts +133 -0
  116. package/design/sdk/components/scrollbar/scrollbar.css +78 -0
  117. package/design/sdk/components/scrollbar/scrollbar.ts +123 -0
  118. package/design/sdk/components/select/select.css +5 -0
  119. package/design/sdk/components/select/select.ts +16 -0
  120. package/design/sdk/components/select/shared/select-part.ts +75 -0
  121. package/design/sdk/components/select-panel/select-panel.css +3 -0
  122. package/design/sdk/components/select-panel/select-panel.ts +124 -0
  123. package/design/sdk/components/skeleton/skeleton.css +22 -0
  124. package/design/sdk/components/skeleton/skeleton.ts +50 -0
  125. package/design/sdk/components/slider/slider.css +52 -0
  126. package/design/sdk/components/slider/slider.ts +86 -0
  127. package/design/sdk/components/standings/standings.css +33 -0
  128. package/design/sdk/components/standings/standings.ts +130 -0
  129. package/design/sdk/components/stat-tile/stat-tile.css +15 -0
  130. package/design/sdk/components/stat-tile/stat-tile.ts +84 -0
  131. package/design/sdk/components/stream/stream.css +48 -0
  132. package/design/sdk/components/stream/stream.ts +38 -0
  133. package/design/sdk/components/switch/switch.css +35 -0
  134. package/design/sdk/components/switch/switch.ts +63 -0
  135. package/design/sdk/components/table/table.css +67 -0
  136. package/design/sdk/components/table/table.ts +160 -0
  137. package/design/sdk/components/tabs/tabs.css +13 -0
  138. package/design/sdk/components/tabs/tabs.ts +74 -0
  139. package/design/sdk/components/textarea/textarea.css +116 -0
  140. package/design/sdk/components/textarea/textarea.ts +64 -0
  141. package/design/sdk/components/timeline/timeline.css +30 -0
  142. package/design/sdk/components/timeline/timeline.ts +87 -0
  143. package/design/sdk/components/toolbar/toolbar.css +24 -0
  144. package/design/sdk/components/toolbar/toolbar.ts +87 -0
  145. package/design/sdk/components/tooltip/tooltip.css +15 -0
  146. package/design/sdk/components/tooltip/tooltip.ts +28 -0
  147. package/design/sdk/components/tree/selected-tint.css +15 -0
  148. package/design/sdk/components/tree/tree.ts +70 -0
  149. package/design/sdk/fixtures.ts +0 -0
  150. package/design/sdk/foundations/colors/colors.css +632 -0
  151. package/design/sdk/foundations/colors/colors.ts +188 -0
  152. package/design/sdk/foundations/colors/overrides.json +19 -0
  153. package/design/sdk/foundations/colors/shared/contrast.ts +120 -0
  154. package/design/sdk/foundations/elevation/elevation.css +6 -0
  155. package/design/sdk/foundations/elevation/elevation.ts +11 -0
  156. package/design/sdk/foundations/elevation/shared/elevation/scene.css +101 -0
  157. package/design/sdk/foundations/elevation/shared/elevation/scene.ts +74 -0
  158. package/design/sdk/foundations/icons/icons.ts +119 -0
  159. package/design/select.ts +113 -28
  160. package/design/skeleton.ts +67 -0
  161. package/design/slider.ts +155 -0
  162. package/design/standings.ts +161 -0
  163. package/design/stat-tile.ts +109 -0
  164. package/design/stream.ts +92 -0
  165. package/design/switch.ts +83 -0
  166. package/design/table.ts +302 -0
  167. package/design/tabs.ts +75 -0
  168. package/design/textarea.ts +92 -0
  169. package/design/timeline.ts +52 -0
  170. package/design/toolbar.ts +95 -0
  171. package/design/tooltip.ts +87 -0
  172. package/design/tree.ts +106 -0
  173. package/fesm2022/ethlete-components.mjs +240 -199
  174. package/fesm2022/ethlete-components.mjs.map +1 -1
  175. package/package.json +1 -1
  176. package/src/lib/accordion/accordion.component.css +2 -1
  177. package/src/lib/avatar/avatar.component.css +10 -0
  178. package/src/lib/badge/badge.component.css +2 -2
  179. package/src/lib/banner/banner.component.css +3 -1
  180. package/src/lib/breadcrumb/breadcrumb-outlet.component.css +5 -0
  181. package/src/lib/breadcrumb/breadcrumb.component.css +5 -1
  182. package/src/lib/button/button.component.css +19 -0
  183. package/src/lib/button/icon-button.component.css +19 -20
  184. package/src/lib/button/split-button.component.css +5 -1
  185. package/src/lib/calendar/calendar.component.css +13 -3
  186. package/src/lib/card/card.component.css +4 -0
  187. package/src/lib/carousel/carousel-autoplay-styles.component.css +1 -1
  188. package/src/lib/carousel/carousel.component.css +2 -2
  189. package/src/lib/chart/chart-axis.component.css +3 -3
  190. package/src/lib/chip/chip.component.css +8 -2
  191. package/src/lib/description-list/description-list.component.css +2 -1
  192. package/src/lib/empty-state/empty-state.component.css +4 -0
  193. package/src/lib/floating-action/floating-action-styles.component.css +27 -0
  194. package/src/lib/focus-ring/focus-ring-styles.component.css +4 -1
  195. package/src/lib/forms/checkbox/checkbox.component.css +31 -5
  196. package/src/lib/forms/color-input/color-input.component.css +3 -1
  197. package/src/lib/forms/color-input/color-picker-panel.component.css +6 -2
  198. package/src/lib/forms/date-time/date-input/date-input.component.css +1 -1
  199. package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +1 -1
  200. package/src/lib/forms/date-time/duration-input/duration-input.component.css +1 -1
  201. package/src/lib/forms/date-time/range-input-shell-styles.component.css +1 -1
  202. package/src/lib/forms/date-time/time-input/time-input.component.css +1 -1
  203. package/src/lib/forms/description/description.component.css +7 -0
  204. package/src/lib/forms/dropzone/dropzone.component.css +26 -0
  205. package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +4 -2
  206. package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +74 -17
  207. package/src/lib/forms/form-field/form-field-textarea-styles.component.css +62 -0
  208. package/src/lib/forms/form-field/form-field.component.css +6 -0
  209. package/src/lib/forms/form-field/headless/label.directive.css +1 -1
  210. package/src/lib/forms/input/input.component.css +6 -2
  211. package/src/lib/forms/input/number-input.component.css +2 -2
  212. package/src/lib/forms/input/password-input.component.css +4 -3
  213. package/src/lib/forms/otp-input/otp-input.component.css +23 -4
  214. package/src/lib/forms/phone-input/phone-input.component.css +4 -3
  215. package/src/lib/forms/rating/rating.component.css +2 -1
  216. package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +1 -1
  217. package/src/lib/forms/select/select-option-styles.component.css +3 -2
  218. package/src/lib/forms/selection-card-styles.component.css +3 -2
  219. package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +4 -2
  220. package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +4 -2
  221. package/src/lib/forms/selection-list/radio-group/radio.component.css +5 -2
  222. package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +2 -1
  223. package/src/lib/forms/slider/range-slider.component.css +98 -6
  224. package/src/lib/forms/slider/slider.component.css +96 -4
  225. package/src/lib/forms/switch/switch.component.css +47 -4
  226. package/src/lib/forms/tag-input/tag-input.component.css +2 -2
  227. package/src/lib/forms/textarea/textarea-autosize-styles.component.css +2 -3
  228. package/src/lib/forms/textarea/textarea.component.css +7 -2
  229. package/src/lib/grid/grid-item-toolbar.component.css +10 -0
  230. package/src/lib/grid/grid-item.component.css +6 -1
  231. package/src/lib/icon/headless/align-icons.ts +30 -0
  232. package/src/lib/icon/headless/arrow-out-up-right-icon.ts +17 -0
  233. package/src/lib/icon/headless/arrow-right-icon.ts +23 -0
  234. package/src/lib/icon/headless/arrow-up-icon.ts +30 -0
  235. package/src/lib/icon/headless/arrows-left-right-icon.ts +31 -0
  236. package/src/lib/icon/headless/bold-icon.ts +11 -0
  237. package/src/lib/icon/headless/calendar-icon.ts +21 -0
  238. package/src/lib/icon/headless/check-icon.ts +14 -0
  239. package/src/lib/icon/headless/chevron-icon.ts +14 -0
  240. package/src/lib/icon/headless/circle-check-icon.ts +14 -0
  241. package/src/lib/icon/headless/circle-info-icon.ts +14 -0
  242. package/src/lib/icon/headless/clipboard-check-icon.ts +28 -0
  243. package/src/lib/icon/headless/clock-icon.ts +21 -0
  244. package/src/lib/icon/headless/code-block-icon.ts +15 -0
  245. package/src/lib/icon/headless/code-icon.ts +14 -0
  246. package/src/lib/icon/headless/ellipsis-icon.ts +13 -0
  247. package/src/lib/icon/headless/ellipsis-vertical-icon.ts +13 -0
  248. package/src/lib/icon/headless/eye-icons.ts +27 -0
  249. package/src/lib/icon/headless/eyedropper-icon.ts +22 -0
  250. package/src/lib/icon/headless/file-icon.ts +16 -0
  251. package/src/lib/icon/headless/filter-icon.ts +11 -0
  252. package/src/lib/icon/headless/floppy-disk-icon.ts +27 -0
  253. package/src/lib/icon/headless/focus-frame-icon.ts +16 -0
  254. package/src/lib/icon/headless/grid-2x2-icon.ts +17 -0
  255. package/src/lib/icon/headless/heading-1-icon.ts +11 -0
  256. package/src/lib/icon/headless/heading-2-icon.ts +11 -0
  257. package/src/lib/icon/headless/heading-3-icon.ts +11 -0
  258. package/src/lib/icon/headless/icon-definition.ts +14 -0
  259. package/src/lib/icon/headless/image-icon.ts +14 -0
  260. package/src/lib/icon/headless/italic-icon.ts +19 -0
  261. package/src/lib/icon/headless/link-icon.ts +17 -0
  262. package/src/lib/icon/headless/list-bulleted-icon.ts +14 -0
  263. package/src/lib/icon/headless/list-numbered-icon.ts +14 -0
  264. package/src/lib/icon/headless/location-icon.ts +21 -0
  265. package/src/lib/icon/headless/lock-icon.ts +14 -0
  266. package/src/lib/icon/headless/minus-icon.ts +14 -0
  267. package/src/lib/icon/headless/paragraph-icon.ts +11 -0
  268. package/src/lib/icon/headless/pause-icon.ts +11 -0
  269. package/src/lib/icon/headless/pencil-icon.ts +21 -0
  270. package/src/lib/icon/headless/play-icon.ts +10 -0
  271. package/src/lib/icon/headless/plus-icon.ts +21 -0
  272. package/src/lib/icon/headless/quote-icon.ts +15 -0
  273. package/src/lib/icon/headless/rotate-right-icon.ts +16 -0
  274. package/src/lib/icon/headless/star-icon.ts +14 -0
  275. package/src/lib/icon/headless/strikethrough-icon.ts +18 -0
  276. package/src/lib/icon/headless/table-icon.ts +15 -0
  277. package/src/lib/icon/headless/thumbtack-icon.ts +18 -0
  278. package/src/lib/icon/headless/times-icon.ts +14 -0
  279. package/src/lib/icon/headless/trash-icon.ts +24 -0
  280. package/src/lib/icon/headless/triangle-exclamation-icon.ts +14 -0
  281. package/src/lib/icon/headless/trophy-icon.ts +24 -0
  282. package/src/lib/icon/headless/underline-icon.ts +12 -0
  283. package/src/lib/icon/headless/undo-redo-icons.ts +24 -0
  284. package/src/lib/icon/headless/upload-icon.ts +16 -0
  285. package/src/lib/loader/brand-loader/brand-loader.component.css +4 -1
  286. package/src/lib/loader/progress-bar/progress-bar.component.css +1 -1
  287. package/src/lib/loader/spinner/spinner-styles.component.css +1 -1
  288. package/src/lib/match/match-card.component.css +9 -3
  289. package/src/lib/match/match-score.component.css +4 -1
  290. package/src/lib/menu/menu-selection-item.component.css +1 -1
  291. package/src/lib/menu/menu.component.css +5 -0
  292. package/src/lib/overlay/overlay-container.component.css +4 -0
  293. package/src/lib/pagination/pagination.component.css +4 -0
  294. package/src/lib/progress-steps/progress-step.component.css +4 -4
  295. package/src/lib/query-error/query-error.component.css +4 -0
  296. package/src/lib/scheduler/scheduler-appointment-styles.component.css +21 -5
  297. package/src/lib/scheduler/scheduler-month-view.component.css +4 -2
  298. package/src/lib/scheduler/scheduler-time-grid-view.component.css +8 -2
  299. package/src/lib/scrollable/headless/scrollable-navigation.component.css +1 -1
  300. package/src/lib/standings/standings-pick.component.css +1 -1
  301. package/src/lib/standings/standings.component.css +10 -10
  302. package/src/lib/stat-tile/stat-tile-sparkline.component.css +1 -1
  303. package/src/lib/stream/consent/stream-consent.component.css +99 -0
  304. package/src/lib/stream/error/stream-player-error.component.css +100 -0
  305. package/src/lib/stream/loading/stream-player-loading.component.css +16 -0
  306. package/src/lib/stream/pip/pip-slot-placeholder.component.css +68 -0
  307. package/src/lib/stream/stream-player-slot-styles.component.css +4 -0
  308. package/src/lib/table/table-resize-grip.component.css +2 -1
  309. package/src/lib/table/table-select-cell.component.css +45 -15
  310. package/src/lib/table/table.component.css +15 -5
  311. package/src/lib/tabs/tab-bar-styles.component.css +1 -1
  312. package/src/lib/tabs/tabs/tab-group.component.css +111 -0
  313. package/src/lib/time-picker/time-picker.component.css +2 -1
  314. package/src/lib/timeline/timeline-item.component.css +5 -1
  315. package/src/lib/toolbar/toolbar.component.css +4 -0
  316. package/src/lib/tooltip/tooltip.component.css +14 -0
  317. package/src/lib/tree/tree.component.css +2 -2
  318. package/types/ethlete-components.d.ts +62 -53
package/design/select.ts CHANGED
@@ -1,16 +1,46 @@
1
1
  import controlSuffix from '../src/lib/forms/form-field/form-field-control-suffix-styles.component.css?inline';
2
+ import selectAllOptionStyles from '../src/lib/forms/select/select-all-option.component.css?inline';
2
3
  import selectBase from '../src/lib/forms/select/select.component.css?inline';
3
- import selectOption from '../src/lib/forms/select/select-option-styles.component.css?inline';
4
- import selectPanel from '../src/lib/forms/select/select-panel.component.css?inline';
4
+ import selectOptionGroupStyles from '../src/lib/forms/select/select-option-group.component.css?inline';
5
+ import selectOptionStyles from '../src/lib/forms/select/select-option-styles.component.css?inline';
6
+ import selectPanelStyles from '../src/lib/forms/select/select-panel.component.css?inline';
7
+ import { CHECK_ICON } from '../src/lib/icon/headless/check-icon';
8
+ import { CHEVRON_ICON } from '../src/lib/icon/headless/chevron-icon';
9
+ import { TIMES_ICON } from '../src/lib/icon/headless/times-icon';
5
10
  import { FormFieldControlMock, formFieldStyles, FormFieldMockState } from './form-field';
11
+ import { iconMarkup } from './icon';
6
12
 
7
- export const CHECK_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>`;
13
+ export type SelectMockOption = { value: string; label: string; disabled?: boolean };
8
14
 
9
- export const CHEVRON_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M244.7 116.7c6.2-6.2 16.4-6.2 22.6 0l192 192c6.2 6.2 6.2 16.4 0 22.6s-16.4 6.2-22.6 0L256 150.6 75.3 331.3c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l192-192z"/></svg>`;
15
+ export type SelectMockActiveSource = 'keyboard' | 'pointer';
10
16
 
11
- export const TIMES_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M324.5 411.1c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6L214.6 256 347.1 123.5c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L192 233.4 59.6 100.9c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6L169.4 256 36.9 388.5c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L192 278.6 324.5 411.1z"/></svg>`;
17
+ export type SelectOptionMockState = {
18
+ id: string;
19
+ selected?: boolean;
20
+ /** Where the active option came from: the keyboard draws it highlighted, the pointer only under `:hover`. */
21
+ active?: SelectMockActiveSource;
22
+ };
23
+
24
+ export type SelectAllOptionMockOptions = {
25
+ id: string;
26
+ label: string;
27
+ checked: boolean | 'mixed';
28
+ active?: SelectMockActiveSource;
29
+ };
30
+
31
+ export type SelectOptionGroupMockOptions = { labelId: string; label: string; content: string };
12
32
 
13
- export type SelectMockOption = { value: string; label: string };
33
+ export type SelectPanelMockOptions = {
34
+ id: string;
35
+ labelId?: string | null;
36
+ /** The panel's options, groups and select-all row as HTML. */
37
+ content: string;
38
+ multiple?: boolean;
39
+ /** The app's default color theme name: the panel provides it to the options. */
40
+ color?: string;
41
+ /** A surface theme name the app registers, for the panel. */
42
+ surface?: string;
43
+ };
14
44
 
15
45
  export type SelectMockOptions = {
16
46
  options: SelectMockOption[];
@@ -33,12 +63,70 @@ export const selectStyles = /* @__PURE__ */ [
33
63
  formFieldStyles,
34
64
  controlSuffix,
35
65
  selectBase,
36
- selectOption,
37
- selectPanel,
66
+ selectOptionStyles,
67
+ selectOptionGroupStyles,
68
+ selectAllOptionStyles,
69
+ selectPanelStyles,
38
70
  ].join('\n');
39
71
 
40
- const icon = (name: string, svg: string) =>
41
- `<i eticon="${name}" aria-hidden="true" class="et-icon et-icon--${name}">${svg}</i>`;
72
+ const activeAttributes = (active: SelectMockActiveSource | undefined) =>
73
+ active && `data-active="true" data-active-source="${active}"`;
74
+
75
+ /** Draws one `<et-select-option>` as the select panel renders it. */
76
+ export const selectOption = (option: SelectMockOption, state: SelectOptionMockState) => {
77
+ const attributes = [
78
+ 'role="option"',
79
+ `value="${option.value}"`,
80
+ 'class="et-select-option"',
81
+ `id="${state.id}"`,
82
+ `aria-selected="${!!state.selected}"`,
83
+ option.disabled && 'aria-disabled="true"',
84
+ state.selected && 'data-selected="true"',
85
+ activeAttributes(state.active),
86
+ ].filter(Boolean);
87
+
88
+ return `<et-select-option ${attributes.join(' ')}><span class="et-select-option-label">${option.label}</span><span class="et-select-option-check">${iconMarkup(CHECK_ICON)}</span></et-select-option>`;
89
+ };
90
+
91
+ /** Draws the `<et-select-all-option>` row a multi select with `selectAll` renders first in its panel. */
92
+ export const selectAllOption = (options: SelectAllOptionMockOptions) => {
93
+ const attributes = [
94
+ 'role="option"',
95
+ 'class="et-select-option et-select-all-option"',
96
+ `id="${options.id}"`,
97
+ `aria-checked="${options.checked}"`,
98
+ activeAttributes(options.active),
99
+ ].filter(Boolean);
100
+ const check =
101
+ options.checked === 'mixed'
102
+ ? '<span class="et-select-all-option-dash" aria-hidden="true"></span>'
103
+ : iconMarkup(CHECK_ICON);
104
+
105
+ return `<et-select-all-option ${attributes.join(' ')}><span class="et-select-option-label">${options.label}</span><span class="et-select-option-check">${check}</span></et-select-all-option>`;
106
+ };
107
+
108
+ /** Draws an `<et-select-option-group>` with its label around the option HTML in `content`. */
109
+ export const selectOptionGroup = (options: SelectOptionGroupMockOptions) =>
110
+ `<et-select-option-group role="group" aria-labelledby="${options.labelId}" class="et-select-option-group"><div id="${options.labelId}" class="et-select-option-group-label">${options.label}</div>${options.content}</et-select-option-group>`;
111
+
112
+ /** Draws the open `<et-select-panel>` with its listbox around the HTML in `content`. */
113
+ export const selectPanel = (options: SelectPanelMockOptions) => {
114
+ const listbox = [
115
+ 'role="listbox"',
116
+ 'class="et-select-listbox"',
117
+ `id="et-select-listbox-${options.id}"`,
118
+ options.labelId && `aria-labelledby="${options.labelId}"`,
119
+ options.multiple && 'aria-multiselectable="true"',
120
+ ].filter(Boolean);
121
+ const classes = [
122
+ 'et-select-panel',
123
+ 'et-color--inherited',
124
+ options.color && `et-color--${options.color}`,
125
+ `et-surface--${options.surface ?? 'inherited'}`,
126
+ ].filter(Boolean);
127
+
128
+ return `<et-select-panel class="${classes.join(' ')}"><div class="et-select-panel-scroller et-scrollbar-host"><div class="et-select-panel-body"><div ${listbox.join(' ')}>${options.content}</div><div class="et-select-panel-extras"></div></div></div><et-scrollbar class="et-scrollbar" data-orientation="vertical"><div class="et-scrollbar-thumb"></div></et-scrollbar></et-select-panel>`;
129
+ };
42
130
 
43
131
  /**
44
132
  * Draws `<et-select>` as a control for `formField()`. Pass `open: true` to `formField()` to draw
@@ -47,21 +135,12 @@ const icon = (name: string, svg: string) =>
47
135
  export const select = (options: SelectMockOptions): FormFieldControlMock => {
48
136
  const selected = options.options.find((option) => option.value === options.value);
49
137
 
50
- const optionHtml = (state: FormFieldMockState) => (option: SelectMockOption, index: number) => {
51
- const isSelected = option === selected;
52
- const isActive = state.open && option.value === options.active;
53
- const attributes = [
54
- 'role="option"',
55
- `value="${option.value}"`,
56
- 'class="et-select-option"',
57
- `id="et-select-option-${state.id}-${index}"`,
58
- `aria-selected="${isSelected}"`,
59
- isSelected && 'data-selected="true"',
60
- isActive && 'data-active="true" data-active-source="keyboard"',
61
- ].filter(Boolean);
62
-
63
- return `<et-select-option ${attributes.join(' ')}><span class="et-select-option-label">${option.label}</span><span class="et-select-option-check">${icon('et-check', CHECK_ICON_SVG)}</span></et-select-option>`;
64
- };
138
+ const optionHtml = (state: FormFieldMockState) => (option: SelectMockOption, index: number) =>
139
+ selectOption(option, {
140
+ id: `et-select-option-${state.id}-${index}`,
141
+ selected: option === selected,
142
+ active: state.open && option.value === options.active ? 'keyboard' : undefined,
143
+ });
65
144
 
66
145
  const optionsHtml = (state: FormFieldMockState) => options.options.map(optionHtml(state)).join('');
67
146
 
@@ -99,12 +178,18 @@ export const select = (options: SelectMockOptions): FormFieldControlMock => {
99
178
  suffix: (state) => {
100
179
  const clear =
101
180
  (options.clearable ?? true) && selected && state.focused && !state.disabled && !state.readonly
102
- ? `<button aria-label="${options.clearLabel ?? 'Clear'}" class="et-input-clear" tabindex="-1" type="button">${icon('et-times', TIMES_ICON_SVG)}</button>`
181
+ ? `<button aria-label="${options.clearLabel ?? 'Clear'}" class="et-input-clear" tabindex="-1" type="button">${iconMarkup(TIMES_ICON)}</button>`
103
182
  : '';
104
183
 
105
- return `${clear}<button aria-hidden="true" tabindex="-1" type="button" class="et-input-picker-trigger et-select-arrow"${state.disabled ? ' disabled' : ''}>${icon('et-chevron', CHEVRON_ICON_SVG)}</button>`;
184
+ return `${clear}<button aria-hidden="true" tabindex="-1" type="button" class="et-input-picker-trigger et-select-arrow"${state.disabled ? ' disabled' : ''}>${iconMarkup(CHEVRON_ICON)}</button>`;
106
185
  },
107
186
  panel: (state) =>
108
- `<et-select-panel class="et-select-panel et-color--inherited${options.panelColor ? ` et-color--${options.panelColor}` : ''} et-surface--${options.panelSurface ?? 'inherited'}"><div class="et-select-panel-scroller et-scrollbar-host"><div class="et-select-panel-body"><div role="listbox" class="et-select-listbox" id="et-select-listbox-${state.id}"${state.labelId ? ` aria-labelledby="${state.labelId}"` : ''}>${optionsHtml(state)}</div><div class="et-select-panel-extras"></div></div></div><et-scrollbar class="et-scrollbar" data-orientation="vertical"><div class="et-scrollbar-thumb"></div></et-scrollbar></et-select-panel>`,
187
+ selectPanel({
188
+ id: state.id,
189
+ labelId: state.labelId,
190
+ content: optionsHtml(state),
191
+ color: options.panelColor,
192
+ surface: options.panelSurface,
193
+ }),
109
194
  };
110
195
  };
@@ -0,0 +1,67 @@
1
+ import skeletonBase from '../src/lib/skeleton/skeleton.component.css?inline';
2
+
3
+ export type SkeletonShapeMock = 'text' | 'rect' | 'circle';
4
+
5
+ export type SkeletonItemMockOptions = {
6
+ /** Left out, `text`, as `et-skeleton-item` defaults to. */
7
+ shape?: SkeletonShapeMock;
8
+ /** Inline style on the item, e.g. `--et-skeleton-size: 40px` or `block-size: 140px`. */
9
+ style?: string;
10
+ };
11
+
12
+ export type SkeletonTextMockOptions = {
13
+ /** Left out, 3. */
14
+ lines?: number;
15
+ /** Width (%) of the last line. Left out, 60. */
16
+ lastLineWidth?: number;
17
+ };
18
+
19
+ export type SkeletonMockOptions = {
20
+ /** The projected content, built from `skeletonItem()`, `skeletonText()` and any layout markup. */
21
+ content: string;
22
+ /** Left out, `true`, as `et-skeleton` defaults to. */
23
+ animated?: boolean;
24
+ /** Where the shimmer is drawn, as a fraction of its sweep from 0 to 1. Left out, 0.5: the highlight centred on each bone. */
25
+ shimmerAt?: number;
26
+ /** Extra attributes on the skeleton, e.g. design markers. */
27
+ attributes?: string;
28
+ };
29
+
30
+ /** The stylesheet `skeleton()` draws with: the real skeleton CSS, plus the rule that holds the shimmer still. */
31
+ export const skeletonStyles = /* @__PURE__ */ [
32
+ skeletonBase,
33
+ `.et-skeleton[data-et-shimmer-at] .et-skeleton-item::after {
34
+ animation-play-state: paused;
35
+ animation-delay: calc(var(--et-skeleton-shimmer-at) * -1 * var(--et-skeleton-duration));
36
+ }`,
37
+ ].join('\n');
38
+
39
+ /** Draws one `et-skeleton-item` as the component renders it, as HTML. */
40
+ export const skeletonItem = (options: SkeletonItemMockOptions = {}) => {
41
+ const style = options.style ? ` style="${options.style}"` : '';
42
+
43
+ return `<et-skeleton-item class="et-skeleton-item" aria-hidden="true" data-shape="${options.shape ?? 'text'}"${style}></et-skeleton-item>`;
44
+ };
45
+
46
+ /** Draws an `et-skeleton-text` with its lines as the component renders it, as HTML. */
47
+ export const skeletonText = (options: SkeletonTextMockOptions = {}) => {
48
+ const lines = Math.max(1, Math.floor(options.lines ?? 3));
49
+ const items = Array.from({ length: lines }, (_, index) =>
50
+ skeletonItem({ style: `inline-size: ${index === lines - 1 ? (options.lastLineWidth ?? 60) : 100}%` }),
51
+ ).join('');
52
+
53
+ return `<et-skeleton-text class="et-skeleton-text">${items}</et-skeleton-text>`;
54
+ };
55
+
56
+ /** Draws an `et-skeleton` around `content` as the component renders it, as HTML, without its visually hidden label and with the shimmer held at one frame. */
57
+ export const skeleton = (options: SkeletonMockOptions) => {
58
+ const animated = options.animated ?? true;
59
+ const attributes = [
60
+ options.attributes,
61
+ `class="et-skeleton${animated ? ' et-skeleton--animated' : ''}"`,
62
+ 'role="status"',
63
+ animated && `data-et-shimmer-at style="--et-skeleton-shimmer-at: ${options.shimmerAt ?? 0.5}"`,
64
+ ].filter(Boolean);
65
+
66
+ return `<et-skeleton ${attributes.join(' ')}>${options.content}</et-skeleton>`;
67
+ };
@@ -0,0 +1,155 @@
1
+ import formSupport from '../src/lib/forms/form-field/form-support-styles.component.css?inline';
2
+ import label from '../src/lib/forms/form-field/headless/label.directive.css?inline';
3
+ import hint from '../src/lib/forms/form-field/hint.component.css?inline';
4
+ import rangeSliderBase from '../src/lib/forms/slider/range-slider.component.css?inline';
5
+ import sliderBase from '../src/lib/forms/slider/slider.component.css?inline';
6
+
7
+ /** An interaction a frame forces on a thumb with the `data-et-force` design marker. */
8
+ export type SliderMockForce = 'hover' | 'focus-visible' | 'active';
9
+
10
+ export type SliderMockMark = { value: number; label?: string };
11
+
12
+ export type SliderMockOptions = {
13
+ /** One number draws `et-slider`, a pair draws `et-range-slider` with two thumbs. */
14
+ value: number | [number, number];
15
+ min?: number;
16
+ max?: number;
17
+ /** `true` draws a tick at every `step`; a list draws the given ticks, with labels where set. */
18
+ marks?: boolean | SliderMockMark[];
19
+ step?: number;
20
+ /** The label, as HTML. */
21
+ label?: string;
22
+ /** The hint, as HTML. Hidden while an `error` shows. */
23
+ hint?: string;
24
+ /** The error message, as HTML. Draws the slider touched and invalid. */
25
+ error?: string;
26
+ /** The color theme name the app registers for errors, e.g. `danger` in Storybook. */
27
+ errorColor?: string;
28
+ /** A color theme name the app registers. */
29
+ color?: string;
30
+ required?: boolean;
31
+ disabled?: boolean;
32
+ /** View-only: the value shows, the thumb takes focus but does not move. */
33
+ readonly?: boolean;
34
+ /** An unresolved bulk-edit value: the thumbs park at the track start. */
35
+ mixed?: boolean;
36
+ mixedLabel?: string;
37
+ orientation?: 'horizontal' | 'vertical';
38
+ /** Draws the value bubble an `etSliderThumbLabel` template renders above each thumb. */
39
+ valueLabel?: boolean;
40
+ /** The thumb index that is being dragged. */
41
+ dragging?: number;
42
+ /** Forced interactions, per thumb index (a single slider uses index 0). */
43
+ force?: Partial<Record<number, SliderMockForce | SliderMockForce[]>>;
44
+ /** Unique per frame: the label and message ids derive from it. */
45
+ id?: string;
46
+ };
47
+
48
+ /** The stylesheets `slider()` draws with: the real `et-slider`, `et-range-slider` and form support CSS. */
49
+ export const sliderStyles = /* @__PURE__ */ [label, hint, formSupport, sliderBase, rangeSliderBase].join('\n');
50
+
51
+ const styled = (css: string) => ` style="${css}"`;
52
+
53
+ const forceList = (force: SliderMockForce | SliderMockForce[] | undefined) =>
54
+ force === undefined ? [] : Array.isArray(force) ? force : [force];
55
+
56
+ /** Draws `<et-slider>`, or `<et-range-slider>` for a value pair, as HTML, with the DOM the components render. */
57
+ export const slider = (options: SliderMockOptions) => {
58
+ const { id = 'slider', min = 0, max = 100, step = 1, orientation = 'horizontal' } = options;
59
+ const range = Array.isArray(options.value);
60
+ const prefix = range ? 'et-range-slider' : 'et-slider';
61
+ const [start, end] = Array.isArray(options.value) ? options.value : [min, options.value];
62
+ const values = range ? [start, end] : [end];
63
+ const shown = options.mixed ? values.map(() => min) : values;
64
+ const [shownStart, shownEnd] = options.mixed ? [min, min] : [start, end];
65
+ const percent = (value: number) => ((value - min) / (max - min)) * 100;
66
+ const ids = { hint: `et-form-field-hint-${id}`, error: `et-form-field-error-${id}` };
67
+ const labelId = options.label !== undefined ? `et-label-${id}` : null;
68
+ const invalid = options.error !== undefined;
69
+ const supportId = invalid ? ids.error : options.hint !== undefined ? ids.hint : null;
70
+ const colorClass = invalid && options.errorColor ? options.errorColor : (options.color ?? 'inherited');
71
+ const activeRange = options.mixed ? null : { start, end };
72
+
73
+ const markList: SliderMockMark[] =
74
+ options.marks === true
75
+ ? Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, index) => ({ value: min + index * step }))
76
+ : options.marks || [];
77
+ const hasMarkLabels = markList.some((mark) => !!mark.label);
78
+
79
+ const hostAttributes = [
80
+ `class="${prefix} et-color--${colorClass}"`,
81
+ options.mixed && 'data-mixed="true"',
82
+ `data-orientation="${orientation}"`,
83
+ invalid && 'data-error="true"',
84
+ options.disabled && 'data-disabled="true"',
85
+ options.readonly && 'data-readonly="true"',
86
+ options.dragging !== undefined && 'data-dragging="true"',
87
+ hasMarkLabels && 'data-mark-labels="true"',
88
+ options.valueLabel && 'data-thumb-value="true"',
89
+ ].filter(Boolean);
90
+
91
+ const labelHtml = labelId
92
+ ? `<et-label id="${labelId}"${options.disabled ? ' data-disabled="true"' : ''}><span class="et-label-content">${options.label}</span>${options.required ? '<span aria-hidden="true" class="et-label-required-marker">*</span>' : ''}</et-label>`
93
+ : '';
94
+
95
+ const fillStyle = range
96
+ ? `--_et-slider-fill-start: ${percent(shownStart)}; --_et-slider-fill-end: ${percent(shownEnd)};`
97
+ : `--_et-slider-fill-end: ${percent(shownEnd)};`;
98
+
99
+ const marksHtml = markList.length
100
+ ? `<div aria-hidden="true" class="${prefix}-marks">${markList
101
+ .map((mark) => {
102
+ const active = activeRange !== null && mark.value >= activeRange.start && mark.value <= activeRange.end;
103
+ const markLabel = mark.label ? `<span class="${prefix}-mark-label">${mark.label}</span>` : '';
104
+
105
+ return `<span class="${prefix}-mark"${active ? ' data-active="true"' : ''} data-et-slider-mark-value="${mark.value}"${styled(`--_et-slider-mark-position: ${percent(mark.value)};`)}>${markLabel}</span>`;
106
+ })
107
+ .join('')}</div>`
108
+ : '';
109
+
110
+ const thumbs = shown
111
+ .map((value, index) => {
112
+ const forced = forceList(options.force?.[index]);
113
+ const markLabel = markList.find((mark) => mark.value === values[index])?.label;
114
+ const valueText = options.mixed ? (options.mixedLabel ?? 'Mixed') : markLabel;
115
+ const ariaMin = range && index === 1 ? start : min;
116
+ const ariaMax = range && index === 0 ? end : max;
117
+ const attributes = [
118
+ 'role="slider"',
119
+ `class="${prefix}-thumb"`,
120
+ `aria-orientation="${orientation}"`,
121
+ `tabindex="${options.disabled ? -1 : 0}"`,
122
+ `aria-valuemin="${ariaMin}"`,
123
+ `aria-valuemax="${ariaMax}"`,
124
+ !options.mixed && `aria-valuenow="${value}"`,
125
+ valueText && `aria-valuetext="${valueText}"`,
126
+ range ? `aria-label="${index === 0 ? 'Minimum' : 'Maximum'}"` : labelId && `aria-labelledby="${labelId}"`,
127
+ supportId && `aria-describedby="${supportId}"`,
128
+ options.disabled && 'aria-disabled="true"',
129
+ options.readonly && 'aria-readonly="true"',
130
+ invalid && 'aria-invalid="true"',
131
+ options.disabled && 'data-disabled="true"',
132
+ options.readonly && 'data-readonly="true"',
133
+ options.dragging === index && 'data-dragging="true"',
134
+ forced.length > 0 && `data-et-force="${forced.join(' ')}"`,
135
+ styled(`--_et-slider-thumb-position: ${percent(value)};`).trim(),
136
+ ].filter(Boolean);
137
+ const bubble = options.valueLabel
138
+ ? `<div aria-hidden="true" class="${prefix}-thumb-value">${values[index]}</div>`
139
+ : '';
140
+
141
+ return `<div ${attributes.join(' ')}>${bubble}</div>`;
142
+ })
143
+ .join('');
144
+
145
+ const support =
146
+ options.hint === undefined && options.error === undefined
147
+ ? ''
148
+ : `<et-form-support class="et-form-support"><div class="et-form-support-stack">${
149
+ invalid
150
+ ? `<div class="et-form-support-content et-form-support-errors${options.errorColor ? ` et-color--${options.errorColor}` : ''}" id="${ids.error}" aria-atomic="true" aria-live="polite" data-active="true" data-direction="from-below" data-state="active"><et-form-error class="et-form-error">${options.error}</et-form-error></div>`
151
+ : `<div class="et-form-support-content et-form-support-hint" id="${ids.hint}" data-active="true" data-direction="from-above" data-state="active"><et-hint>${options.hint}</et-hint></div>`
152
+ }</div></et-form-support>`;
153
+
154
+ return `<${prefix} ${hostAttributes.join(' ')}>${labelHtml}<div class="${prefix}-interaction"><div aria-hidden="true" class="${prefix}-rail"></div><div aria-hidden="true" class="${prefix}-fill"${styled(fillStyle)}></div>${marksHtml}${thumbs}</div>${support}</${prefix}>`;
155
+ };
@@ -0,0 +1,161 @@
1
+ import focusRing from '../src/lib/focus-ring/focus-ring-styles.component.css?inline';
2
+ import participantBase from '../src/lib/match/match-participant.component.css?inline';
3
+ import pickBase from '../src/lib/standings/standings-pick.component.css?inline';
4
+ import standingsBase from '../src/lib/standings/standings.component.css?inline';
5
+
6
+ export type StandingsMockFormResult = 'win' | 'tie' | 'loss';
7
+
8
+ export type StandingsMockRow = {
9
+ position: number;
10
+ name: string;
11
+ played: number;
12
+ wins: number;
13
+ ties: number;
14
+ losses: number;
15
+ /** Left out, the table has no difference column unless another row has one. */
16
+ difference?: number;
17
+ points: number;
18
+ /** Oldest first. Left out on every row, the table has no form column. */
19
+ form?: readonly StandingsMockFormResult[];
20
+ };
21
+
22
+ export type StandingsMockZone = {
23
+ from: number;
24
+ to: number;
25
+ label: string;
26
+ /** A color theme name the app registers. Left out, the zone takes the color theme around the table. */
27
+ color?: string;
28
+ };
29
+
30
+ export type StandingsMockOptions = {
31
+ rows: readonly StandingsMockRow[];
32
+ zones?: readonly StandingsMockZone[];
33
+ /** The position of the highlighted (own) row. */
34
+ highlighted?: number;
35
+ showLegend?: boolean;
36
+ /** Extra attributes on the host, e.g. a style or design markers. */
37
+ attributes?: string;
38
+ };
39
+
40
+ export type StandingsPickMockRow = {
41
+ name: string;
42
+ /** The consumer's mark template content, as HTML, drawn only while locked. */
43
+ mark?: string;
44
+ /** A design marker for the reorder handle, e.g. `hover` or `focus-visible`. */
45
+ handleForce?: string;
46
+ };
47
+
48
+ export type StandingsPickMockOptions = {
49
+ rows: readonly StandingsPickMockRow[];
50
+ advancingCount: number;
51
+ locked?: boolean;
52
+ /** The index of the card being dragged, and its offset from the slot in px. */
53
+ dragging?: { index: number; offset: number };
54
+ /** Per index, the preview offset in px the other cards slide by while a drag runs. */
55
+ shifts?: readonly number[];
56
+ /** Extra attributes on the host, e.g. a style or design markers. */
57
+ attributes?: string;
58
+ };
59
+
60
+ /** The stylesheets the standings mocks draw with: the real standings, standings pick, match participant and focus ring CSS. */
61
+ export const standingsStyles = /* @__PURE__ */ [focusRing, participantBase, standingsBase, pickBase].join('\n');
62
+
63
+ const forced = (force: string | undefined) => (force ? ` data-et-force="${force}"` : '');
64
+
65
+ const colored = (color: string | undefined) => (color ? ` et-color--${color}` : '');
66
+
67
+ const participant = (name: string, className: string) =>
68
+ `<et-match-participant class="et-match-participant ${className}"><span class="et-match-participant-emblem"><span data-mark="${(Array.from(name.trim())[0] ?? '').toUpperCase()}" class="et-match-participant-emblem-mark" aria-hidden="true"></span></span><span class="et-match-participant-names"><span class="et-match-participant-name">${name}</span></span></et-match-participant>`;
69
+
70
+ const differenceText = (difference: number | undefined) =>
71
+ difference === undefined ? '' : difference > 0 ? `+${difference}` : `${difference}`;
72
+
73
+ const FORM_SHORT = { win: 'W', tie: 'D', loss: 'L' } as const;
74
+
75
+ const headCell = (column: string, label: string) =>
76
+ `<th class="et-standings-cell" data-column="${column}" scope="col">${label}</th>`;
77
+
78
+ /** Draws an `et-standings` table with its zone bands, highlighted row and legend, as HTML. */
79
+ export const standings = (options: StandingsMockOptions) => {
80
+ const zones = options.zones ?? [];
81
+ const hasDifference = options.rows.some((row) => row.difference !== undefined);
82
+ const hasForm = options.rows.some((row) => row.form);
83
+ const zoneOf = (position: number) => zones.find((zone) => position >= zone.from && position <= zone.to);
84
+
85
+ const head = [
86
+ headCell('position', '#'),
87
+ headCell('participant', 'Team'),
88
+ ...['P', 'W', 'D', 'L'].map((label) => headCell('detail', label)),
89
+ hasDifference ? headCell('detail', 'Diff') : '',
90
+ headCell('points', 'Pts'),
91
+ hasForm ? headCell('form', 'Form') : '',
92
+ ].join('');
93
+
94
+ const body = options.rows
95
+ .map((row) => {
96
+ const zone = zoneOf(row.position);
97
+ const highlighted = row.position === options.highlighted;
98
+ const attributes = [
99
+ zone ? ' data-zone=""' : '',
100
+ highlighted ? ' data-highlighted="" aria-current="true"' : '',
101
+ ].join('');
102
+ const form = row.form
103
+ ? `<span class="et-standings-form">${row.form.map((result) => `<span data-result="${result}" class="et-standings-form-result" aria-hidden="true">${FORM_SHORT[result]}</span>`).join('')}</span>`
104
+ : '';
105
+
106
+ return [
107
+ `<tr class="et-standings-row${colored(zone?.color)}"${attributes}>`,
108
+ `<th class="et-standings-cell" data-column="position" scope="row">${row.position}</th>`,
109
+ `<td class="et-standings-cell" data-column="participant">${participant(row.name, 'et-standings-participant')}</td>`,
110
+ ...[row.played, row.wins, row.ties, row.losses].map(
111
+ (value) => `<td class="et-standings-cell" data-column="detail">${value}</td>`,
112
+ ),
113
+ hasDifference
114
+ ? `<td class="et-standings-cell" data-column="detail">${differenceText(row.difference)}</td>`
115
+ : '',
116
+ `<td class="et-standings-cell" data-column="points">${row.points}</td>`,
117
+ hasForm ? `<td class="et-standings-cell" data-column="form">${form}</td>` : '',
118
+ `</tr>`,
119
+ ].join('');
120
+ })
121
+ .join('');
122
+
123
+ const legend =
124
+ options.showLegend && zones.length
125
+ ? `<ul aria-label="What the highlighted positions mean" class="et-standings-legend">${zones.map((zone) => `<li class="et-standings-legend-item"><span class="et-standings-legend-swatch${colored(zone.color)}" aria-hidden="true"></span>${zone.label}</li>`).join('')}</ul>`
126
+ : '';
127
+
128
+ return `<et-standings class="et-standings"${zones.length ? ' data-has-zones=""' : ''} ${options.attributes ?? ''}><div class="et-standings-content"><table class="et-standings-table"><thead><tr class="et-standings-head-row">${head}</tr></thead><tbody>${body}</tbody></table>${legend}</div></et-standings>`;
129
+ };
130
+
131
+ /** Draws an `et-standings-pick` list with its cut line, at rest, mid-drag or locked with marks, as HTML. */
132
+ export const standingsPick = (options: StandingsPickMockOptions) => {
133
+ const items = options.rows
134
+ .map((row, index) => {
135
+ const advancing = index < options.advancingCount;
136
+ const dragging = options.dragging?.index === index;
137
+ const offset = dragging ? options.dragging?.offset : options.shifts?.[index];
138
+ const translate = offset ? ` style="translate: 0 ${offset}px"` : '';
139
+ const mark = options.locked && row.mark ? `<span class="et-standings-pick-mark">${row.mark}</span>` : '';
140
+ const handle = options.locked
141
+ ? ''
142
+ : `<button aria-label="Move ${row.name}, position ${index + 1}" class="et-standings-pick-handle et-focus-ring" type="button"${forced(row.handleForce)}><span class="et-standings-pick-grip" aria-hidden="true"></span></button>`;
143
+ const cut =
144
+ index === options.advancingCount - 1
145
+ ? `<p class="et-standings-pick-cut" aria-hidden="true"><span class="et-standings-pick-cut-line"></span><span class="et-standings-pick-cut-label">Advancing</span><span class="et-standings-pick-cut-line"></span></p>`
146
+ : '';
147
+
148
+ return [
149
+ `<li class="et-standings-pick-item"${advancing ? ' data-advancing=""' : ''}>`,
150
+ `<div class="et-standings-pick-row"><span class="et-standings-pick-position">${index + 1}</span>`,
151
+ `<div class="et-standings-pick-card"${dragging ? ' data-dragging=""' : ''}${translate}>`,
152
+ participant(row.name, 'et-standings-pick-participant'),
153
+ mark,
154
+ handle,
155
+ `</div></div>${cut}</li>`,
156
+ ].join('');
157
+ })
158
+ .join('');
159
+
160
+ return `<et-standings-pick class="et-standings-pick"${options.locked ? ' data-locked=""' : ''} ${options.attributes ?? ''}><ol aria-label="Your predicted order" class="et-standings-pick-list"${options.dragging ? ' data-dragging=""' : ''}>${items}</ol></et-standings-pick>`;
161
+ };