@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
@@ -0,0 +1,306 @@
1
+ import iconButtonBase from '../src/lib/button/icon-button.component.css?inline';
2
+ import coarseGridBase from '../src/lib/calendar/calendar-coarse-grid-styles.component.css?inline';
3
+ import comparisonBandBase from '../src/lib/calendar/calendar-comparison-band-styles.component.css?inline';
4
+ import weekNumbersBase from '../src/lib/calendar/calendar-week-numbers-styles.component.css?inline';
5
+ import calendarBase from '../src/lib/calendar/calendar.component.css?inline';
6
+ import { CHEVRON_ICON } from '../src/lib/icon/headless/chevron-icon';
7
+ import iconBase from '../src/lib/icon/headless/icon-styles.component.css?inline';
8
+ import { buttonStyles } from './button';
9
+ import { iconMarkup } from './icon';
10
+
11
+ export type CalendarMockRange = {
12
+ /** An ISO date, `YYYY-MM-DD`. */
13
+ start: string;
14
+ /** An ISO date, `YYYY-MM-DD`. */
15
+ end: string;
16
+ };
17
+
18
+ export type CalendarMockOptions = {
19
+ /** Left out, `month`. */
20
+ view?: 'month' | 'year' | 'multiYear';
21
+ /** The visible month, as an ISO month `YYYY-MM`. */
22
+ month: string;
23
+ /** The first year of the year grid's page of 24. Left out, the visible year rounded down to a multiple of 24. */
24
+ yearPageStart?: number;
25
+ /** Today, as an ISO date. Pass a fixed date, never the real today. */
26
+ today?: string;
27
+ /** The selected day of a single-value calendar, as an ISO date. */
28
+ value?: string;
29
+ /** The selected range of a range calendar. */
30
+ range?: CalendarMockRange;
31
+ /** The comparison band (`etCalendarComparisonBand`). */
32
+ comparison?: CalendarMockRange;
33
+ /** The earliest selectable day, as an ISO date. */
34
+ min?: string;
35
+ /** The latest selectable day, as an ISO date. */
36
+ max?: string;
37
+ /** Days the date filter turns down, as ISO dates. */
38
+ disabled?: string[];
39
+ weekNumbers?: boolean;
40
+ /** Left out, `1` (Monday), as `et-calendar` defaults to without a locale. */
41
+ weekStartsOn?: number;
42
+ /** Replaces the header label, e.g. with a localized one. */
43
+ headerLabel?: string;
44
+ /** Extra attributes per cell, keyed by ISO date in the month view, `YYYY-MM` in the year view and `YYYY` in the year grid. */
45
+ cellAttributes?: Record<string, string>;
46
+ /** Extra attributes on the previous button, e.g. design markers. */
47
+ previousAttributes?: string;
48
+ /** Extra attributes on the next button, e.g. design markers. */
49
+ nextAttributes?: string;
50
+ /** Extra attributes on the header label button, e.g. design markers. */
51
+ labelAttributes?: string;
52
+ /** Extra attributes on the host, e.g. a style or design markers. */
53
+ attributes?: string;
54
+ };
55
+
56
+ type Band = 'start' | 'middle' | 'end' | 'single' | null;
57
+
58
+ type CellFlags = {
59
+ label: string;
60
+ key: string;
61
+ today: boolean;
62
+ selected: boolean;
63
+ disabled: boolean;
64
+ band: Band;
65
+ comparisonBand: Band;
66
+ outsideMonth?: boolean;
67
+ };
68
+
69
+ const DAY = 86_400_000;
70
+
71
+ const toDay = (iso: string) => {
72
+ const [year = 0, month = 1, day = 1] = iso.split('-').map(Number);
73
+
74
+ return Date.UTC(year, month - 1, day) / DAY;
75
+ };
76
+
77
+ const toIso = (day: number) => new Date(day * DAY).toISOString().slice(0, 10);
78
+
79
+ const isoWeek = (day: number) => {
80
+ const date = new Date(day * DAY);
81
+ const thursday = day + 3 - ((date.getUTCDay() + 6) % 7);
82
+ const yearStart = Date.UTC(new Date(thursday * DAY).getUTCFullYear(), 0, 1) / DAY;
83
+
84
+ return Math.floor((thursday - yearStart) / 7) + 1;
85
+ };
86
+
87
+ const monthName = (utc: number, style: 'long' | 'short') =>
88
+ new Intl.DateTimeFormat('en-US', { month: style, timeZone: 'UTC' }).format(utc);
89
+
90
+ const bandFor = (range: { from: number; to: number } | null, [from, to]: [number, number]): Band => {
91
+ if (!range || to < range.from || from > range.to) return null;
92
+ if (range.from === range.to && from <= range.from && to >= range.to) return 'single';
93
+ if (from <= range.from) return 'start';
94
+ if (to >= range.to) return 'end';
95
+
96
+ return 'middle';
97
+ };
98
+
99
+ const toRange = (range: CalendarMockRange | undefined) =>
100
+ range
101
+ ? { from: Math.min(toDay(range.start), toDay(range.end)), to: Math.max(toDay(range.start), toDay(range.end)) }
102
+ : null;
103
+
104
+ /** The stylesheets `calendar()` draws with: the real calendar, coarse grid, comparison band, week number, icon-button and icon CSS. */
105
+ export const calendarStyles = /* @__PURE__ */ [
106
+ buttonStyles,
107
+ iconBase,
108
+ iconButtonBase,
109
+ calendarBase,
110
+ coarseGridBase,
111
+ comparisonBandBase,
112
+ weekNumbersBase,
113
+ ].join('\n');
114
+
115
+ /** Draws `<et-calendar>` as HTML with its default header and the grid of one view, as a static snapshot of one state. */
116
+ export const calendar = (options: CalendarMockOptions) => {
117
+ const { view = 'month', weekStartsOn = 1, cellAttributes = {} } = options;
118
+ const [year = 0, monthNumber = 1] = options.month.split('-').map(Number);
119
+ const month = monthNumber - 1;
120
+ const monthStart = Date.UTC(year, month, 1) / DAY;
121
+ const monthEnd = Date.UTC(year, month + 1, 0) / DAY;
122
+ const min = options.min ? toDay(options.min) : -Infinity;
123
+ const max = options.max ? toDay(options.max) : Infinity;
124
+ const turnedDown = new Set((options.disabled ?? []).map(toDay));
125
+ const today = options.today ? toDay(options.today) : null;
126
+ const value = options.value ? toDay(options.value) : null;
127
+ const range = toRange(options.range);
128
+ const comparison = toRange(options.comparison);
129
+ const pageStart = options.yearPageStart ?? Math.floor(year / 24) * 24;
130
+
131
+ const isSelectable = (day: number) => day >= min && day <= max && !turnedDown.has(day);
132
+ const hasSelectableDay = (from: number, to: number) => {
133
+ for (let day = Math.max(from, min); day <= Math.min(to, max); day++) {
134
+ if (!turnedDown.has(day)) return true;
135
+ }
136
+
137
+ return false;
138
+ };
139
+
140
+ const spanFlags = (from: number, to: number) => ({
141
+ today: today !== null && today >= from && today <= to,
142
+ selected:
143
+ (value !== null && value >= from && value <= to) ||
144
+ (range !== null && ((range.from >= from && range.from <= to) || (range.to >= from && range.to <= to))),
145
+ band: bandFor(range, [from, to]),
146
+ comparisonBand: bandFor(comparison, [from, to]),
147
+ });
148
+
149
+ const cellButton = (cell: CellFlags, coarse: boolean) => {
150
+ const attributes = [
151
+ cellAttributes[cell.key],
152
+ `class="et-calendar-cell${coarse ? ' et-calendar-cell--coarse' : ''}"`,
153
+ 'type="button"',
154
+ 'tabindex="-1"',
155
+ `aria-selected="${cell.selected}"`,
156
+ cell.disabled && 'aria-disabled="true"',
157
+ cell.today && 'aria-current="date"',
158
+ cell.selected && 'data-selected=""',
159
+ cell.disabled && 'data-disabled=""',
160
+ cell.today && 'data-today=""',
161
+ cell.band && `data-band="${cell.band}"`,
162
+ cell.comparisonBand && `data-comparison-band="${cell.comparisonBand}"`,
163
+ cell.outsideMonth && 'data-outside-month=""',
164
+ ].filter(Boolean);
165
+
166
+ return `<button ${attributes.join(' ')}><span class="et-calendar-cell-content">${cell.label}</span></button>`;
167
+ };
168
+
169
+ const coarseRows = (cells: CellFlags[]) => {
170
+ const rows: string[] = [];
171
+
172
+ for (let index = 0; index < cells.length; index += 4) {
173
+ const row = cells
174
+ .slice(index, index + 4)
175
+ .map((cell) => cellButton(cell, true))
176
+ .join('');
177
+ rows.push(`<div class="et-calendar-coarse-row" role="row">${row}</div>`);
178
+ }
179
+
180
+ return rows.join('');
181
+ };
182
+
183
+ const monthGrid = () => {
184
+ const offset = (new Date(monthStart * DAY).getUTCDay() - weekStartsOn + 7) % 7;
185
+ const weeks: string[] = [];
186
+
187
+ for (let weekStart = monthStart - offset; weekStart <= monthEnd; weekStart += 7) {
188
+ const weekNumber = options.weekNumbers
189
+ ? `<span class="et-calendar-week-number" role="rowheader">${isoWeek(weekStart)}</span>`
190
+ : '';
191
+ const days = Array.from({ length: 7 }, (_, index) => {
192
+ const day = weekStart + index;
193
+
194
+ return cellButton(
195
+ {
196
+ label: `${new Date(day * DAY).getUTCDate()}`,
197
+ key: toIso(day),
198
+ disabled: !isSelectable(day),
199
+ outsideMonth: day < monthStart || day > monthEnd,
200
+ ...spanFlags(day, day),
201
+ },
202
+ false,
203
+ );
204
+ }).join('');
205
+ weeks.push(`<div class="et-calendar-week" role="row">${weekNumber}${days}</div>`);
206
+ }
207
+
208
+ return `<div class="et-calendar-months" role="presentation"><div class="et-calendar-month" role="presentation">${weeks.join('')}</div></div>`;
209
+ };
210
+
211
+ const yearGrid = () =>
212
+ coarseRows(
213
+ Array.from({ length: 12 }, (_, index) => {
214
+ const from = Date.UTC(year, index, 1) / DAY;
215
+ const to = Date.UTC(year, index + 1, 0) / DAY;
216
+
217
+ return {
218
+ label: monthName(from * DAY, 'short'),
219
+ key: `${year}-${String(index + 1).padStart(2, '0')}`,
220
+ disabled: !hasSelectableDay(from, to),
221
+ ...spanFlags(from, to),
222
+ };
223
+ }),
224
+ );
225
+
226
+ const multiYearGrid = () =>
227
+ coarseRows(
228
+ Array.from({ length: 24 }, (_, index) => {
229
+ const cellYear = pageStart + index;
230
+ const from = Date.UTC(cellYear, 0, 1) / DAY;
231
+ const to = Date.UTC(cellYear, 11, 31) / DAY;
232
+
233
+ return {
234
+ label: `${cellYear}`,
235
+ key: `${cellYear}`,
236
+ disabled: !hasSelectableDay(from, to),
237
+ ...spanFlags(from, to),
238
+ };
239
+ }),
240
+ );
241
+
242
+ const weekdays = () => {
243
+ const heading = options.weekNumbers
244
+ ? '<span class="et-calendar-week-number-heading" role="columnheader"></span>'
245
+ : '';
246
+ const names = Array.from({ length: 7 }, (_, index) => {
247
+ const name = new Intl.DateTimeFormat('en-US', { weekday: 'short', timeZone: 'UTC' })
248
+ .format(Date.UTC(2023, 0, 1 + ((weekStartsOn + index) % 7)))
249
+ .slice(0, 2);
250
+
251
+ return `<span class="et-calendar-weekday" role="columnheader">${name}</span>`;
252
+ }).join('');
253
+
254
+ return `<div class="et-calendar-weekdays" role="row">${heading}${names}</div>`;
255
+ };
256
+
257
+ const [canGoPrev, canGoNext] =
258
+ view === 'month'
259
+ ? [min < monthStart, max > monthEnd]
260
+ : view === 'year'
261
+ ? [min < Date.UTC(year, 0, 1) / DAY, max > Date.UTC(year, 11, 31) / DAY]
262
+ : [min < Date.UTC(pageStart, 0, 1) / DAY, max > Date.UTC(pageStart + 23, 11, 31) / DAY];
263
+
264
+ const headerLabel =
265
+ options.headerLabel ??
266
+ (view === 'month'
267
+ ? `${monthName(monthStart * DAY, 'long')} ${year}`
268
+ : view === 'year'
269
+ ? `${year}`
270
+ : `${pageStart} – ${pageStart + 23}`);
271
+
272
+ const navButton = (side: 'previous' | 'next', { enabled, extra }: { enabled: boolean; extra?: string }) => {
273
+ const attributes = [
274
+ extra,
275
+ `class="et-calendar-nav-button et-calendar-nav-button--${side} et-icon-button et-color-interactive et-focus-ring et-color--inherited"`,
276
+ 'type="button"',
277
+ `aria-label="${side === 'previous' ? 'Previous' : 'Next'}"`,
278
+ !enabled && 'disabled',
279
+ 'data-variant="transparent"',
280
+ 'data-size="sm"',
281
+ ].filter(Boolean);
282
+
283
+ return `<button ${attributes.join(' ')}><div class="et-icon-button-icon">${iconMarkup(CHEVRON_ICON)}</div></button>`;
284
+ };
285
+
286
+ const label = [
287
+ options.labelAttributes,
288
+ 'class="et-calendar-header-label"',
289
+ 'type="button"',
290
+ `aria-label="${view === 'multiYear' ? 'Choose date' : 'Choose year'}"`,
291
+ ].filter(Boolean);
292
+
293
+ const header = `<div class="et-calendar-header">${navButton('previous', { enabled: canGoPrev, extra: options.previousAttributes })}<button ${label.join(' ')}><span class="et-calendar-header-label-text" aria-live="polite"><span class="et-calendar-header-label-value">${headerLabel}</span></span>${iconMarkup(CHEVRON_ICON, 'et-calendar-header-label-icon')}</button>${navButton('next', { enabled: canGoNext, extra: options.nextAttributes })}</div>`;
294
+
295
+ const grid = view === 'month' ? monthGrid() : view === 'year' ? yearGrid() : multiYearGrid();
296
+ const body = `<div class="et-calendar-grid">${view === 'month' ? weekdays() : ''}<div class="et-calendar-weeks-viewport" role="presentation"><div class="et-calendar-weeks" role="rowgroup">${grid}</div></div></div>`;
297
+
298
+ const host = [
299
+ options.attributes,
300
+ 'class="et-calendar"',
301
+ `data-view="${view}"`,
302
+ options.weekNumbers && 'data-week-numbers=""',
303
+ ].filter(Boolean);
304
+
305
+ return `<div ${host.join(' ')}>${header}${body}</div>`;
306
+ };
package/design/card.ts ADDED
@@ -0,0 +1,92 @@
1
+ import cardBase from '../src/lib/card/card.component.css?inline';
2
+ import { button, ButtonMockOptions, buttonStyles } from './button';
3
+
4
+ export type CardMockOptions = {
5
+ variant?: 'elevated' | 'outlined' | 'filled';
6
+ /** A surface theme name the app registers. Left out, the card takes the surface around it. */
7
+ surface?: string;
8
+ /** The projected content, as HTML. `cardContent()` draws a typical one. */
9
+ content: string;
10
+ /** Extra attributes on the card, e.g. design markers or a style. */
11
+ attributes?: string;
12
+ };
13
+
14
+ export type CardContentMockOptions = {
15
+ title?: string;
16
+ /** Muted text under the title, e.g. a date or a category. */
17
+ subtitle?: string;
18
+ text?: string;
19
+ /** Media at the top, as HTML. `cardPlaceholderMedia` draws a generic one. */
20
+ media?: string;
21
+ actions?: ButtonMockOptions[];
22
+ };
23
+
24
+ /** A generic landscape placeholder as an inline SVG, for drawing media in a card. */
25
+ export const cardPlaceholderMedia =
26
+ '<svg class="et-card-mock-media" viewBox="0 0 320 140" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Placeholder image"><rect width="320" height="140" fill="#8a94a6"/><circle cx="250" cy="40" r="16" fill="#e6e9ef"/><path d="M0 140 90 60l60 50 40-30 130 60z" fill="#e6e9ef"/></svg>';
27
+
28
+ /** The stylesheets `card()` and `cardContent()` draw with: the real card and button CSS, plus the mock's content layout. */
29
+ export const cardStyles = /* @__PURE__ */ [
30
+ buttonStyles,
31
+ cardBase,
32
+ `@layer components {
33
+ .et-card-mock-media {
34
+ display: block;
35
+ inline-size: calc(100% + 2 * var(--et-card-padding));
36
+ block-size: auto;
37
+ margin: calc(-1 * var(--et-card-padding)) calc(-1 * var(--et-card-padding)) 0;
38
+ border-start-start-radius: inherit;
39
+ border-start-end-radius: inherit;
40
+ }
41
+
42
+ .et-card-mock-title {
43
+ margin: 0;
44
+ font-size: 1.6rem;
45
+ font-weight: 600;
46
+ line-height: 1.4;
47
+ }
48
+
49
+ .et-card-mock-subtitle {
50
+ margin: 0.2rem 0 0;
51
+ font-size: 1.3rem;
52
+ color: var(--et-surface-color-muted-solid);
53
+ }
54
+
55
+ .et-card-mock-text {
56
+ margin: 0;
57
+ font-size: 1.4rem;
58
+ line-height: 1.5;
59
+ }
60
+
61
+ .et-card-mock-actions {
62
+ display: flex;
63
+ flex-wrap: wrap;
64
+ justify-content: flex-end;
65
+ gap: 8px;
66
+ }
67
+ }`,
68
+ ].join('\n');
69
+
70
+ /** Draws an `et-card` as the component renders it, as HTML. */
71
+ export const card = (options: CardMockOptions) => {
72
+ const attributes = [
73
+ options.attributes,
74
+ `data-variant="${options.variant ?? 'outlined'}"`,
75
+ `class="et-card et-surface--${options.surface ?? 'inherited'}"`,
76
+ ].filter(Boolean);
77
+
78
+ return `<et-card ${attributes.join(' ')}>${options.content}</et-card>`;
79
+ };
80
+
81
+ /** Draws typical consumer content for a card: media, a title with subtitle, text and an actions row. The SDK card ships none of these parts. */
82
+ export const cardContent = (options: CardContentMockOptions) => {
83
+ const heading = options.title
84
+ ? `<div><h3 class="et-card-mock-title">${options.title}</h3>${options.subtitle ? `<p class="et-card-mock-subtitle">${options.subtitle}</p>` : ''}</div>`
85
+ : '';
86
+ const text = options.text ? `<p class="et-card-mock-text">${options.text}</p>` : '';
87
+ const actions = options.actions?.length
88
+ ? `<div class="et-card-mock-actions">${options.actions.map((each) => button(each)).join('')}</div>`
89
+ : '';
90
+
91
+ return `${options.media ?? ''}${heading}${text}${actions}`;
92
+ };
@@ -0,0 +1,240 @@
1
+ import iconButtonBase from '../src/lib/button/icon-button.component.css?inline';
2
+ import autoplayBase from '../src/lib/carousel/carousel-autoplay-styles.component.css?inline';
3
+ import carouselBase from '../src/lib/carousel/carousel.component.css?inline';
4
+ import { CHEVRON_ICON } from '../src/lib/icon/headless/chevron-icon';
5
+ import iconBase from '../src/lib/icon/headless/icon-styles.component.css?inline';
6
+ import { PAUSE_ICON } from '../src/lib/icon/headless/pause-icon';
7
+ import { PLAY_ICON } from '../src/lib/icon/headless/play-icon';
8
+ import scrollableBase from '../src/lib/scrollable/scrollable.component.css?inline';
9
+ import { buttonStyles } from './button';
10
+ import { iconMarkup } from './icon';
11
+ import { scrollable } from './scrollable';
12
+
13
+ export type CarouselMockSlide = {
14
+ /** The slide's inner HTML, as the consumer's slide template renders it. */
15
+ content: string;
16
+ };
17
+
18
+ export type CarouselMockOptions = {
19
+ slides: CarouselMockSlide[];
20
+ /** The index of the active slide. Left out, 0. */
21
+ active?: number;
22
+ /** Left out, `full`: one slide at a time. */
23
+ itemSize?: 'full' | 'half' | 'third';
24
+ /** Left out, true, as the `loop` input defaults to. Looping draws the clones and never disables the arrows. */
25
+ loop?: boolean;
26
+ slideAlign?: 'start' | 'center';
27
+ /** The gap between the slides in px, set as `--et-carousel-slide-gap`. Left out, 0. */
28
+ slideGap?: number;
29
+ /** Left out, true. */
30
+ showControls?: boolean;
31
+ /** Left out, true. */
32
+ showDots?: boolean;
33
+ /** Draws the play toggle and the countdown ring. */
34
+ autoplay?: boolean;
35
+ /** Whether autoplay runs: the toggle shows pause and the active dot counts down. Left out, true while `autoplay` is on. */
36
+ playing?: boolean;
37
+ /** The slide's autoplay time in ms. Left out, 5000. */
38
+ autoplayTime?: number;
39
+ /** Where the countdown ring is held, as a fraction of the slide's time. Left out, 0.4. */
40
+ autoplayProgress?: number;
41
+ /** Extra attributes on the previous button, e.g. design markers. */
42
+ previousAttributes?: string;
43
+ /** Extra attributes on the next button, e.g. design markers. */
44
+ nextAttributes?: string;
45
+ /** Extra attributes on the play toggle, e.g. design markers. */
46
+ playToggleAttributes?: string;
47
+ /** Extra attributes on a dot, by index, e.g. design markers. */
48
+ dotAttributes?: Record<number, string>;
49
+ /** A color theme name the app registers. Left out, the carousel takes the color theme around it. */
50
+ color?: string;
51
+ /** Extra attributes on the host, e.g. a style or design markers. */
52
+ attributes?: string;
53
+ /** Extra attributes on the track's scroll container, e.g. design markers. */
54
+ trackAttributes?: string;
55
+ };
56
+
57
+ const PER_VIEW = { full: 1, half: 2, third: 3 };
58
+
59
+ /** The stylesheets `carousel()` draws with: the real carousel, autoplay, scrollable and icon-button CSS, plus the rules that draw the countdown at one point without its animation. */
60
+ export const carouselStyles = /* @__PURE__ */ [
61
+ buttonStyles,
62
+ iconBase,
63
+ iconButtonBase,
64
+ scrollableBase,
65
+ carouselBase,
66
+ autoplayBase,
67
+ `.et-carousel .et-carousel-dot[data-et-frozen-at] .et-carousel-dot-progress-half::before {
68
+ --_sweep: clamp(0, var(--et-carousel-frozen-at) * 2, 1);
69
+ animation: none;
70
+ opacity: 1;
71
+ rotate: calc(-180deg + var(--_sweep) * 180deg);
72
+ }
73
+ .et-carousel .et-carousel-dot[data-et-frozen-at] .et-carousel-dot-progress-half:last-child::before {
74
+ --_sweep: clamp(0, var(--et-carousel-frozen-at) * 2 - 1, 1);
75
+ opacity: clamp(0, var(--_sweep) * 1000, 1);
76
+ }`,
77
+ ].join('\n');
78
+
79
+ type ControlOptions = { classes: string[]; label: string; disabled: boolean; icons: string; attributes?: string };
80
+
81
+ const control = ({ classes, label, disabled, icons, attributes }: ControlOptions) => {
82
+ const hostClasses = [
83
+ 'et-carousel-control',
84
+ ...classes,
85
+ 'et-icon-button',
86
+ 'et-color-interactive',
87
+ 'et-focus-ring',
88
+ 'et-color--inherited',
89
+ ];
90
+ const host = [
91
+ attributes,
92
+ `class="${hostClasses.join(' ')}"`,
93
+ 'type="button"',
94
+ `aria-label="${label}"`,
95
+ disabled && 'aria-disabled="true"',
96
+ 'data-variant="transparent"',
97
+ 'data-size="sm"',
98
+ ].filter(Boolean);
99
+
100
+ return `<button ${host.join(' ')}><div class="et-icon-button-icon">${icons}</div></button>`;
101
+ };
102
+
103
+ /**
104
+ * Draws `<et-carousel>` as HTML: the scrollable track with its slides (and the clones a looping carousel adds), the
105
+ * previous and next buttons, the dots and the autoplay toggle with its countdown ring. The active slide is brought
106
+ * into view by translating the slides statically.
107
+ */
108
+ export const carousel = (options: CarouselMockOptions) => {
109
+ const count = options.slides.length;
110
+ const active = Math.min(Math.max(options.active ?? 0, 0), Math.max(count - 1, 0));
111
+ const itemSize = options.itemSize ?? 'full';
112
+ const perView = PER_VIEW[itemSize];
113
+ const loop = options.loop ?? true;
114
+ const slideAlign = options.slideAlign ?? 'start';
115
+ const gap = options.slideGap ?? 0;
116
+ const autoplay = options.autoplay ?? false;
117
+ const playing = autoplay && (options.playing ?? true);
118
+ const autoplayTime = options.autoplayTime ?? 5000;
119
+ const cloneCount = loop && count > perView ? Math.min(count, perView + 1) : 0;
120
+ const looping = cloneCount > 0;
121
+ const firstInView = looping ? cloneCount + active : Math.min(active, Math.max(count - perView, 0));
122
+ const shift = slideAlign === 'center' && itemSize !== 'full' ? cloneCount + active - (perView - 1) / 2 : firstInView;
123
+ const atStart = !looping && firstInView === 0;
124
+ const atEnd = !looping && firstInView >= count - perView;
125
+ const canGoPrevious = count > 1 && (loop || !atStart);
126
+ const canGoNext = count > 1 && (loop || !atEnd);
127
+
128
+ const slideItem = (index: number, isClone: boolean) => {
129
+ const attributes = [
130
+ 'class="et-carousel-item"',
131
+ 'role="group"',
132
+ 'aria-roledescription="slide"',
133
+ `aria-label="${index + 1} of ${count}"`,
134
+ isClone && 'aria-hidden="true" inert="" data-clone=""',
135
+ !isClone && index === active && 'data-active=""',
136
+ shift !== 0 && `style="translate: calc(${-shift} * (100% + ${gap}px)) 0"`,
137
+ ].filter(Boolean);
138
+
139
+ return {
140
+ attributes: attributes.join(' '),
141
+ content: `<div class="et-carousel-slide-content">${options.slides[index]?.content ?? ''}</div>`,
142
+ };
143
+ };
144
+
145
+ const items = [
146
+ ...Array.from({ length: cloneCount }, (_, offset) => slideItem(count - cloneCount + offset, true)),
147
+ ...Array.from({ length: count }, (_, index) => slideItem(index, false)),
148
+ ...Array.from({ length: cloneCount }, (_, index) => slideItem(index, true)),
149
+ ];
150
+
151
+ const track = scrollable({
152
+ items,
153
+ canScroll: count > perView,
154
+ position: atStart ? 'start' : atEnd ? 'end' : 'middle',
155
+ renderMasks: false,
156
+ snap: true,
157
+ itemSize,
158
+ itemGap: gap,
159
+ class: 'et-carousel-track',
160
+ containerAttributes: options.trackAttributes,
161
+ });
162
+
163
+ const progressAttributes = [
164
+ 'class="et-carousel-dot-progress"',
165
+ 'aria-hidden="true"',
166
+ `style="--_et-carousel-autoplay-duration: ${autoplayTime}ms"`,
167
+ ];
168
+ const progress = autoplay
169
+ ? `<span ${progressAttributes.join(' ')}>` +
170
+ `<span class="et-carousel-dot-progress-half"></span><span class="et-carousel-dot-progress-half"></span></span>`
171
+ : '';
172
+
173
+ const dots = Array.from({ length: count }, (_, index) => {
174
+ const isActive = index === active;
175
+ const counting = isActive && playing;
176
+ const attributes = [
177
+ options.dotAttributes?.[index],
178
+ 'class="et-carousel-dot et-focus-ring"',
179
+ 'type="button"',
180
+ `aria-label="Go to slide ${index + 1}"`,
181
+ isActive && 'aria-current="true" data-active=""',
182
+ counting && 'data-counting=""',
183
+ counting && `data-et-frozen-at="" style="--et-carousel-frozen-at: ${options.autoplayProgress ?? 0.4}"`,
184
+ ].filter(Boolean);
185
+
186
+ return `<button ${attributes.join(' ')}>${progress}</button>`;
187
+ }).join('');
188
+
189
+ const showControls = options.showControls ?? true;
190
+ const showDots = options.showDots ?? true;
191
+
192
+ const controls = [
193
+ showControls &&
194
+ control({
195
+ classes: [],
196
+ label: 'Previous slide',
197
+ disabled: !canGoPrevious,
198
+ icons: iconMarkup(CHEVRON_ICON, 'et-carousel-control-icon et-carousel-control-icon--previous'),
199
+ attributes: options.previousAttributes,
200
+ }),
201
+ showDots && `<div class="et-carousel-dots">${dots}</div>`,
202
+ autoplay &&
203
+ control({
204
+ classes: ['et-carousel-play-toggle'],
205
+ label: playing ? 'Pause automatic slide show' : 'Start automatic slide show',
206
+ disabled: false,
207
+ icons:
208
+ iconMarkup(PLAY_ICON, 'et-carousel-control-icon et-carousel-play-toggle-icon') +
209
+ iconMarkup(PAUSE_ICON, 'et-carousel-control-icon et-carousel-play-toggle-icon'),
210
+ attributes: [options.playToggleAttributes, playing && 'data-playing=""'].filter(Boolean).join(' '),
211
+ }),
212
+ showControls &&
213
+ control({
214
+ classes: [],
215
+ label: 'Next slide',
216
+ disabled: !canGoNext,
217
+ icons: iconMarkup(CHEVRON_ICON, 'et-carousel-control-icon et-carousel-control-icon--next'),
218
+ attributes: options.nextAttributes,
219
+ }),
220
+ ].filter(Boolean);
221
+
222
+ const host = [
223
+ options.attributes,
224
+ `class="${['et-carousel', options.color && `et-color--${options.color}`].filter(Boolean).join(' ')}"`,
225
+ 'role="region"',
226
+ 'aria-roledescription="carousel"',
227
+ 'aria-label="Carousel"',
228
+ `data-slide-align="${slideAlign}"`,
229
+ 'data-transition="none"',
230
+ looping && 'data-looping=""',
231
+ playing && 'data-autoplaying=""',
232
+ gap && `style="--et-carousel-slide-gap: ${gap}px"`,
233
+ ].filter(Boolean);
234
+
235
+ return (
236
+ `<et-carousel ${host.join(' ')}>${track}` +
237
+ (controls.length ? `<div class="et-carousel-controls">${controls.join('')}</div>` : '') +
238
+ `</et-carousel>`
239
+ );
240
+ };