@boostdev/design-system-components 1.2.0 → 1.2.2

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 (168) hide show
  1. package/AGENTS.md +46 -4
  2. package/README.md +15 -15
  3. package/dist/client.cjs +149 -124
  4. package/dist/client.css +503 -525
  5. package/dist/client.d.cts +71 -96
  6. package/dist/client.d.ts +71 -96
  7. package/dist/client.js +149 -124
  8. package/dist/index.cjs +149 -124
  9. package/dist/index.css +503 -525
  10. package/dist/index.d.cts +71 -96
  11. package/dist/index.d.ts +71 -96
  12. package/dist/index.js +149 -124
  13. package/dist/web-components/{chunk-6MH5UWUD.js → chunk-2FGATTGT.js} +5 -0
  14. package/dist/web-components/{chunk-5IPHEONG.js → chunk-6I2DBFQ7.js} +1 -0
  15. package/dist/web-components/{chunk-DI46Q2EA.js → chunk-OCODKRVZ.js} +57 -82
  16. package/dist/web-components/index.d.ts +571 -1
  17. package/dist/web-components/index.js +1501 -4
  18. package/dist/web-components/interaction/bds-collapsible.js +1 -1
  19. package/dist/web-components/interaction/bds-drawer.js +1 -1
  20. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  21. package/package.json +1 -1
  22. package/src/components/interaction/Command/Command.tsx +4 -3
  23. package/src/components/interaction/Dialog/Dialog.tsx +4 -5
  24. package/src/components/interaction/Drawer/Drawer.module.css +37 -62
  25. package/src/components/interaction/Drawer/Drawer.spec.tsx +3 -3
  26. package/src/components/interaction/Drawer/Drawer.tsx +25 -24
  27. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +4 -2
  28. package/src/components/interaction/Popover/Popover.tsx +4 -3
  29. package/src/components/interaction/Rating/Rating.tsx +4 -2
  30. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +4 -6
  31. package/src/components/interaction/form/Combobox/Combobox.tsx +4 -2
  32. package/src/components/interaction/form/FileInput/FileInput.tsx +4 -3
  33. package/src/components/interaction/form/NumberInput/NumberInput.tsx +4 -3
  34. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +4 -6
  35. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +8 -4
  36. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +13 -6
  37. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -6
  38. package/src/components/interaction/form/atoms/InputContainer.tsx +2 -2
  39. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +4 -6
  40. package/src/components/layout/Card/Card.tsx +4 -10
  41. package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -5
  42. package/src/components/layout/SectionHeader/SectionHeader.tsx +5 -4
  43. package/src/components/ui/Accordion/Accordion.tsx +4 -3
  44. package/src/components/ui/Alert/Alert.tsx +4 -3
  45. package/src/components/ui/Avatar/Avatar.tsx +5 -3
  46. package/src/components/ui/Badge/Badge.tsx +4 -5
  47. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +4 -3
  48. package/src/components/ui/Calendar/Calendar.tsx +4 -4
  49. package/src/components/ui/Carousel/Carousel.tsx +4 -4
  50. package/src/components/ui/DescriptionList/DescriptionList.tsx +4 -4
  51. package/src/components/ui/Loading/Loading.tsx +4 -3
  52. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +4 -2
  53. package/src/components/ui/Pagination/Pagination.tsx +4 -2
  54. package/src/components/ui/Progress/Progress.tsx +4 -2
  55. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +4 -1
  56. package/src/components/ui/Separator/Separator.tsx +5 -3
  57. package/src/components/ui/Skeleton/Skeleton.tsx +4 -3
  58. package/src/components/ui/SkipLink/SkipLink.tsx +4 -5
  59. package/src/components/ui/Tabs/Tabs.tsx +4 -4
  60. package/src/components/ui/Tooltip/Tooltip.tsx +4 -2
  61. package/src/components/ui/Typography/Typography.tsx +4 -5
  62. package/src/stories/DesignSystem/Grid.mdx +2 -0
  63. package/src/stories/Introduction.mdx +2 -1
  64. package/src/web-components/index.ts +12 -0
  65. package/src/web-components/interaction/BdsAccordion.mdx +80 -28
  66. package/src/web-components/interaction/BdsAccordion.stories.tsx +67 -58
  67. package/src/web-components/interaction/BdsCollapsible.mdx +93 -23
  68. package/src/web-components/interaction/BdsCollapsible.stories.tsx +29 -48
  69. package/src/web-components/interaction/BdsDialog.mdx +88 -27
  70. package/src/web-components/interaction/BdsDialog.stories.tsx +129 -47
  71. package/src/web-components/interaction/BdsDrawer.mdx +85 -27
  72. package/src/web-components/interaction/BdsDrawer.stories.tsx +161 -31
  73. package/src/web-components/interaction/BdsDropdownMenu.mdx +108 -0
  74. package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +91 -0
  75. package/src/web-components/interaction/BdsSkipLink.mdx +72 -16
  76. package/src/web-components/interaction/BdsSkipLink.stories.tsx +47 -34
  77. package/src/web-components/interaction/BdsTabs.mdx +97 -27
  78. package/src/web-components/interaction/BdsTabs.stories.tsx +69 -55
  79. package/src/web-components/interaction/BdsTooltip.mdx +84 -18
  80. package/src/web-components/interaction/BdsTooltip.stories.tsx +49 -30
  81. package/src/web-components/interaction/bds-collapsible.ts +1 -0
  82. package/src/web-components/interaction/bds-drawer.ts +59 -82
  83. package/src/web-components/interaction/bds-dropdown-menu-item.ts +124 -0
  84. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +102 -0
  85. package/src/web-components/interaction/bds-dropdown-menu.ts +200 -0
  86. package/src/web-components/interaction/form/BdsCheckbox.mdx +57 -20
  87. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +35 -36
  88. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +70 -0
  89. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +81 -0
  90. package/src/web-components/interaction/form/BdsCombobox.mdx +52 -33
  91. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +55 -53
  92. package/src/web-components/interaction/form/BdsFileInput.mdx +59 -24
  93. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +38 -53
  94. package/src/web-components/interaction/form/BdsFormInput.mdx +98 -0
  95. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +139 -0
  96. package/src/web-components/interaction/form/BdsNumberInput.mdx +51 -22
  97. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +24 -46
  98. package/src/web-components/interaction/form/BdsRadio.mdx +53 -22
  99. package/src/web-components/interaction/form/BdsRadio.stories.tsx +19 -51
  100. package/src/web-components/interaction/form/BdsRadioGroup.mdx +73 -0
  101. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +88 -0
  102. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +76 -23
  103. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +64 -54
  104. package/src/web-components/interaction/form/BdsSelect.mdx +59 -21
  105. package/src/web-components/interaction/form/BdsSelect.stories.tsx +47 -56
  106. package/src/web-components/interaction/form/BdsSlider.mdx +55 -27
  107. package/src/web-components/interaction/form/BdsSlider.stories.tsx +23 -44
  108. package/src/web-components/interaction/form/BdsSwitch.mdx +53 -20
  109. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +22 -37
  110. package/src/web-components/interaction/form/BdsTextarea.mdx +54 -22
  111. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +35 -44
  112. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +55 -0
  113. package/src/web-components/interaction/form/bds-checkbox-group.ts +117 -0
  114. package/src/web-components/interaction/form/bds-form-input.spec.ts +83 -0
  115. package/src/web-components/interaction/form/bds-form-input.ts +268 -0
  116. package/src/web-components/interaction/form/bds-radio-group.spec.ts +62 -0
  117. package/src/web-components/interaction/form/bds-radio-group.ts +142 -0
  118. package/src/web-components/interaction/form/bds-segmented-control.ts +5 -0
  119. package/src/web-components/ui/BdsAvatar.mdx +29 -20
  120. package/src/web-components/ui/BdsAvatar.stories.tsx +14 -28
  121. package/src/web-components/ui/BdsBreadcrumb.mdx +22 -19
  122. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +11 -21
  123. package/src/web-components/ui/BdsButtonGroup.mdx +69 -0
  124. package/src/web-components/ui/BdsButtonGroup.stories.tsx +66 -0
  125. package/src/web-components/ui/BdsCalendar.mdx +93 -0
  126. package/src/web-components/ui/BdsCalendar.stories.tsx +63 -0
  127. package/src/web-components/ui/BdsCard.mdx +30 -25
  128. package/src/web-components/ui/BdsCard.stories.tsx +19 -42
  129. package/src/web-components/ui/BdsCarousel.mdx +83 -0
  130. package/src/web-components/ui/BdsCarousel.stories.tsx +96 -0
  131. package/src/web-components/ui/BdsDescriptionList.mdx +13 -16
  132. package/src/web-components/ui/BdsDescriptionList.stories.tsx +34 -49
  133. package/src/web-components/ui/BdsIconWrapper.mdx +20 -20
  134. package/src/web-components/ui/BdsIconWrapper.stories.tsx +46 -41
  135. package/src/web-components/ui/BdsLink.mdx +21 -17
  136. package/src/web-components/ui/BdsLink.stories.tsx +19 -20
  137. package/src/web-components/ui/BdsLoading.mdx +9 -13
  138. package/src/web-components/ui/BdsLoading.stories.tsx +8 -10
  139. package/src/web-components/ui/BdsNotificationBanner.mdx +32 -37
  140. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +38 -47
  141. package/src/web-components/ui/BdsPagination.mdx +74 -0
  142. package/src/web-components/ui/BdsPagination.stories.tsx +36 -0
  143. package/src/web-components/ui/BdsProgress.mdx +24 -17
  144. package/src/web-components/ui/BdsProgress.stories.tsx +55 -29
  145. package/src/web-components/ui/BdsProgressCircle.mdx +24 -18
  146. package/src/web-components/ui/BdsProgressCircle.stories.tsx +17 -29
  147. package/src/web-components/ui/BdsRating.mdx +22 -22
  148. package/src/web-components/ui/BdsRating.stories.tsx +19 -13
  149. package/src/web-components/ui/BdsSectionHeader.mdx +31 -28
  150. package/src/web-components/ui/BdsSectionHeader.stories.tsx +21 -23
  151. package/src/web-components/ui/BdsSeparator.mdx +22 -8
  152. package/src/web-components/ui/BdsSeparator.stories.tsx +27 -19
  153. package/src/web-components/ui/BdsSkeleton.mdx +20 -26
  154. package/src/web-components/ui/BdsSkeleton.stories.tsx +23 -23
  155. package/src/web-components/ui/BdsTable.mdx +81 -0
  156. package/src/web-components/ui/BdsTable.stories.tsx +83 -0
  157. package/src/web-components/ui/BdsTypography.mdx +20 -29
  158. package/src/web-components/ui/BdsTypography.stories.tsx +19 -18
  159. package/src/web-components/ui/bds-button-group.spec.ts +40 -0
  160. package/src/web-components/ui/bds-button-group.ts +78 -0
  161. package/src/web-components/ui/bds-calendar.spec.ts +91 -0
  162. package/src/web-components/ui/bds-calendar.ts +427 -0
  163. package/src/web-components/ui/bds-carousel.spec.ts +64 -0
  164. package/src/web-components/ui/bds-carousel.ts +296 -0
  165. package/src/web-components/ui/bds-pagination.spec.ts +67 -0
  166. package/src/web-components/ui/bds-pagination.ts +197 -0
  167. package/src/web-components/ui/bds-table.spec.ts +45 -0
  168. package/src/web-components/ui/bds-table.ts +96 -0
@@ -21,7 +21,7 @@ import {
21
21
  } from "./chunk-HHHRF2PS.js";
22
22
  import {
23
23
  BdsSegmentedControl
24
- } from "./chunk-6MH5UWUD.js";
24
+ } from "./chunk-2FGATTGT.js";
25
25
  import {
26
26
  BdsSelect
27
27
  } from "./chunk-X3FKVHLK.js";
@@ -30,7 +30,7 @@ import {
30
30
  } from "./chunk-7FMR53BW.js";
31
31
  import {
32
32
  BdsDrawer
33
- } from "./chunk-DI46Q2EA.js";
33
+ } from "./chunk-OCODKRVZ.js";
34
34
  import {
35
35
  BdsPopover
36
36
  } from "./chunk-ECW3HHWA.js";
@@ -72,7 +72,7 @@ import {
72
72
  } from "./chunk-3REOIRDW.js";
73
73
  import {
74
74
  BdsCollapsible
75
- } from "./chunk-5IPHEONG.js";
75
+ } from "./chunk-6I2DBFQ7.js";
76
76
  import {
77
77
  BdsDialog
78
78
  } from "./chunk-N6IMUOY4.js";
@@ -120,7 +120,1494 @@ import {
120
120
  import {
121
121
  BdsDescriptionList
122
122
  } from "./chunk-AGKJTMJI.js";
123
- import "./chunk-LGEZYFUU.js";
123
+ import {
124
+ T,
125
+ i,
126
+ i2
127
+ } from "./chunk-LGEZYFUU.js";
128
+
129
+ // src/web-components/ui/bds-button-group.ts
130
+ var BdsButtonGroup = class extends i2 {
131
+ static styles = i`
132
+ :host {
133
+ display: block;
134
+ }
135
+
136
+ .group {
137
+ display: inline-flex;
138
+ flex-direction: row;
139
+ align-items: center;
140
+ gap: 0;
141
+ }
142
+
143
+ :host([orientation='vertical']) .group {
144
+ flex-direction: column;
145
+ align-items: stretch;
146
+ }
147
+
148
+ /* Collapse border radius between adjacent buttons */
149
+ ::slotted(*:not(:first-child)) {
150
+ --button_radius-start: 0;
151
+ }
152
+
153
+ ::slotted(*:not(:last-child)) {
154
+ --button_radius-end: 0;
155
+ }
156
+ `;
157
+ static properties = {
158
+ orientation: { type: String, reflect: true },
159
+ "aria-label": { type: String }
160
+ };
161
+ constructor() {
162
+ super();
163
+ this.orientation = "horizontal";
164
+ }
165
+ render() {
166
+ return T`
167
+ <div class="group" role="group" aria-label=${this["aria-label"] ?? ""}>
168
+ <slot></slot>
169
+ </div>
170
+ `;
171
+ }
172
+ };
173
+ customElements.define("bds-button-group", BdsButtonGroup);
174
+
175
+ // src/web-components/ui/bds-table.ts
176
+ var BdsTable = class extends i2 {
177
+ static styles = i`
178
+ :host {
179
+ display: block;
180
+ }
181
+
182
+ .wrapper {
183
+ overflow-inline: auto;
184
+ -webkit-overflow-scrolling: touch;
185
+ }
186
+
187
+ ::slotted(table) {
188
+ inline-size: 100%;
189
+ border-collapse: collapse;
190
+ font-size: var(--bds-font_size--body);
191
+ font-family: var(--bds-font_family--body);
192
+ color: var(--bds-color_on-bg);
193
+ }
194
+
195
+ ::slotted(thead) {
196
+ background-color: var(--bds-color_bg--subtle);
197
+ }
198
+
199
+ ::slotted(th) {
200
+ padding: var(--bds-space_xs) var(--bds-space_s);
201
+ font-weight: var(--bds-font_weight--semibold);
202
+ font-size: var(--bds-font_size--body--s);
203
+ text-align: start;
204
+ color: var(--bds-color_on-bg--subtle);
205
+ border-block-end: 1px solid var(--bds-color_bg--subtle);
206
+ white-space: nowrap;
207
+ }
208
+
209
+ ::slotted(td) {
210
+ padding: var(--bds-space_xs) var(--bds-space_s);
211
+ border-block-end: 1px solid var(--bds-color_bg--subtle);
212
+ vertical-align: middle;
213
+ }
214
+
215
+ ::slotted(tr:last-child td) {
216
+ border-block-end: none;
217
+ }
218
+ `;
219
+ static properties = {
220
+ stickyHeader: { type: Boolean, attribute: "sticky-header", reflect: true }
221
+ };
222
+ constructor() {
223
+ super();
224
+ this.stickyHeader = false;
225
+ }
226
+ render() {
227
+ return T`
228
+ <div
229
+ class="wrapper"
230
+ style=${this.stickyHeader ? "max-block-size: 100%; overflow-block: auto;" : ""}
231
+ >
232
+ <slot></slot>
233
+ </div>
234
+ `;
235
+ }
236
+ };
237
+ customElements.define("bds-table", BdsTable);
238
+
239
+ // src/web-components/ui/bds-pagination.ts
240
+ function getPageRange(current, total) {
241
+ const delta = 1;
242
+ const range = [];
243
+ for (let i3 = 1; i3 <= total; i3++) {
244
+ if (i3 === 1 || i3 === total || i3 >= current - delta && i3 <= current + delta) {
245
+ range.push(i3);
246
+ } else if (range[range.length - 1] !== "...") {
247
+ range.push("...");
248
+ }
249
+ }
250
+ return range;
251
+ }
252
+ var BdsPagination = class extends i2 {
253
+ static styles = i`
254
+ :host {
255
+ display: block;
256
+ }
257
+
258
+ .pagination {
259
+ display: flex;
260
+ align-items: center;
261
+ }
262
+
263
+ .list {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: var(--bds-space_xxs);
267
+ list-style: none;
268
+ margin: 0;
269
+ padding: 0;
270
+ }
271
+
272
+ .button {
273
+ display: inline-flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ min-inline-size: 2rem;
277
+ block-size: 2rem;
278
+ padding-inline: var(--bds-space_xs);
279
+ font-size: var(--bds-font_size--body--s);
280
+ font-family: var(--bds-font_family--body);
281
+ border: none;
282
+ background: none;
283
+ border-radius: var(--bds-border_radius--xs);
284
+ cursor: pointer;
285
+ color: var(--bds-color_on-bg);
286
+ transition: var(--bds-animation_transition);
287
+ --bdc_color: transparent;
288
+ --bdc-outline_radius: var(--bds-border_radius--xs);
289
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bdc-outline_shadow);
290
+ }
291
+
292
+ .button:focus-visible {
293
+ outline: var(--bds-outline_default);
294
+ outline-offset: var(--bds-outline_offset);
295
+ }
296
+
297
+ @media (hover: hover) and (pointer: fine) {
298
+ .button:not(:disabled):not(.--active):hover {
299
+ background-color: var(--bds-color_bg--subtle);
300
+ }
301
+ }
302
+
303
+ .button.--active {
304
+ background-color: var(--bds-color_interactive);
305
+ color: var(--bds-color_on-interactive);
306
+ font-weight: var(--bds-font_weight--semibold);
307
+ }
308
+
309
+ .button.--nav {
310
+ color: var(--bds-color_on-bg--subtle);
311
+ }
312
+
313
+ .button:disabled {
314
+ opacity: 0.4;
315
+ cursor: not-allowed;
316
+ }
317
+
318
+ .ellipsis {
319
+ display: inline-flex;
320
+ align-items: center;
321
+ justify-content: center;
322
+ min-inline-size: 2rem;
323
+ color: var(--bds-color_on-bg--subtle);
324
+ font-size: var(--bds-font_size--body--s);
325
+ user-select: none;
326
+ }
327
+
328
+ .nav-icon {
329
+ inline-size: 1rem;
330
+ block-size: 1rem;
331
+ }
332
+ `;
333
+ static properties = {
334
+ currentPage: { type: Number, attribute: "current-page", reflect: true },
335
+ totalPages: { type: Number, attribute: "total-pages", reflect: true }
336
+ };
337
+ constructor() {
338
+ super();
339
+ this.currentPage = 1;
340
+ this.totalPages = 1;
341
+ }
342
+ _navigate(page) {
343
+ if (page < 1 || page > this.totalPages || page === this.currentPage) return;
344
+ this.currentPage = page;
345
+ this.dispatchEvent(new CustomEvent("bds-page-change", {
346
+ detail: { page },
347
+ bubbles: true,
348
+ composed: true
349
+ }));
350
+ }
351
+ render() {
352
+ const pages = getPageRange(this.currentPage, this.totalPages);
353
+ return T`
354
+ <nav class="pagination" aria-label="Pagination">
355
+ <ul class="list">
356
+ <li>
357
+ <button
358
+ type="button"
359
+ class="button --nav"
360
+ ?disabled=${this.currentPage <= 1}
361
+ aria-label="Previous page"
362
+ @click=${() => this._navigate(this.currentPage - 1)}
363
+ >
364
+ <svg class="nav-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
365
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
366
+ </svg>
367
+ </button>
368
+ </li>
369
+ ${pages.map(
370
+ (page) => page === "..." ? T`<li><span class="ellipsis" aria-hidden="true">…</span></li>` : T`
371
+ <li>
372
+ <button
373
+ type="button"
374
+ class="button ${page === this.currentPage ? "--active" : ""}"
375
+ aria-current=${page === this.currentPage ? "page" : "false"}
376
+ @click=${() => this._navigate(page)}
377
+ >${page}</button>
378
+ </li>
379
+ `
380
+ )}
381
+ <li>
382
+ <button
383
+ type="button"
384
+ class="button --nav"
385
+ ?disabled=${this.currentPage >= this.totalPages}
386
+ aria-label="Next page"
387
+ @click=${() => this._navigate(this.currentPage + 1)}
388
+ >
389
+ <svg class="nav-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
390
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
391
+ </svg>
392
+ </button>
393
+ </li>
394
+ </ul>
395
+ </nav>
396
+ `;
397
+ }
398
+ };
399
+ customElements.define("bds-pagination", BdsPagination);
400
+
401
+ // src/web-components/interaction/form/bds-checkbox-group.ts
402
+ var BdsCheckboxGroup = class extends i2 {
403
+ static styles = i`
404
+ :host {
405
+ display: block;
406
+ }
407
+
408
+ fieldset {
409
+ border: none;
410
+ margin: 0;
411
+ padding: 0;
412
+ }
413
+
414
+ .legend {
415
+ font-size: var(--bds-font_size--body--s);
416
+ font-family: var(--bds-font_family--body);
417
+ font-weight: var(--bds-font_weight--semibold);
418
+ color: var(--bds-color_on-bg);
419
+ margin-block-end: var(--bds-space_xs);
420
+ }
421
+
422
+ .required {
423
+ color: var(--bds-color_error);
424
+ margin-inline-start: var(--bds-space_xxxs);
425
+ }
426
+
427
+ .items {
428
+ display: flex;
429
+ flex-direction: column;
430
+ gap: var(--bds-space_xs);
431
+ }
432
+
433
+ .message {
434
+ margin-block-start: var(--bds-space_xs);
435
+ font-size: var(--bds-font_size--body--s);
436
+ font-family: var(--bds-font_family--body);
437
+ }
438
+
439
+ .message--error {
440
+ color: var(--bds-color_error);
441
+ }
442
+
443
+ .message--hint {
444
+ color: var(--bds-color_on-bg--subtle);
445
+ }
446
+ `;
447
+ static properties = {
448
+ label: { type: String },
449
+ error: { type: String },
450
+ hint: { type: String },
451
+ disabled: { type: Boolean, reflect: true },
452
+ required: { type: Boolean, reflect: true }
453
+ };
454
+ constructor() {
455
+ super();
456
+ this.label = "";
457
+ this.disabled = false;
458
+ this.required = false;
459
+ }
460
+ render() {
461
+ return T`
462
+ <fieldset ?disabled=${this.disabled}>
463
+ <legend class="legend">
464
+ ${this.label}
465
+ ${this.required ? T`<span class="required" aria-hidden="true"> *</span>` : ""}
466
+ </legend>
467
+ <div class="items">
468
+ <slot></slot>
469
+ </div>
470
+ ${this.error ? T`<p class="message message--error" role="alert">${this.error}</p>` : this.hint ? T`<p class="message message--hint">${this.hint}</p>` : ""}
471
+ </fieldset>
472
+ `;
473
+ }
474
+ };
475
+ customElements.define("bds-checkbox-group", BdsCheckboxGroup);
476
+
477
+ // src/web-components/interaction/form/bds-radio-group.ts
478
+ var BdsRadioGroup = class extends i2 {
479
+ static styles = i`
480
+ :host {
481
+ display: block;
482
+ }
483
+
484
+ fieldset {
485
+ border: none;
486
+ margin: 0;
487
+ padding: 0;
488
+ }
489
+
490
+ .legend {
491
+ font-size: var(--bds-font_size--body--s);
492
+ font-family: var(--bds-font_family--body);
493
+ font-weight: var(--bds-font_weight--semibold);
494
+ color: var(--bds-color_on-bg);
495
+ margin-block-end: var(--bds-space_xs);
496
+ }
497
+
498
+ .required {
499
+ color: var(--bds-color_error);
500
+ margin-inline-start: var(--bds-space_xxxs);
501
+ }
502
+
503
+ .items {
504
+ display: flex;
505
+ flex-direction: column;
506
+ gap: var(--bds-space_xs);
507
+ }
508
+
509
+ .message {
510
+ margin-block-start: var(--bds-space_xs);
511
+ font-size: var(--bds-font_size--body--s);
512
+ font-family: var(--bds-font_family--body);
513
+ }
514
+
515
+ .message--error {
516
+ color: var(--bds-color_error);
517
+ }
518
+
519
+ .message--hint {
520
+ color: var(--bds-color_on-bg--subtle);
521
+ }
522
+ `;
523
+ static properties = {
524
+ label: { type: String },
525
+ name: { type: String, reflect: true },
526
+ error: { type: String },
527
+ hint: { type: String },
528
+ disabled: { type: Boolean, reflect: true },
529
+ required: { type: Boolean, reflect: true }
530
+ };
531
+ constructor() {
532
+ super();
533
+ this.label = "";
534
+ this.name = "";
535
+ this.disabled = false;
536
+ this.required = false;
537
+ }
538
+ _propagateName = () => {
539
+ if (!this.name) return;
540
+ this.querySelectorAll("bds-radio").forEach((radio) => {
541
+ radio.setAttribute("name", this.name);
542
+ });
543
+ };
544
+ connectedCallback() {
545
+ super.connectedCallback();
546
+ Promise.resolve().then(this._propagateName);
547
+ }
548
+ updated(changed) {
549
+ super.updated(changed);
550
+ if (changed.has("name")) {
551
+ this._propagateName();
552
+ }
553
+ }
554
+ render() {
555
+ return T`
556
+ <fieldset ?disabled=${this.disabled}>
557
+ <legend class="legend">
558
+ ${this.label}
559
+ ${this.required ? T`<span class="required" aria-hidden="true"> *</span>` : ""}
560
+ </legend>
561
+ <div class="items">
562
+ <slot @slotchange=${this._propagateName}></slot>
563
+ </div>
564
+ ${this.error ? T`<p class="message message--error" role="alert">${this.error}</p>` : this.hint ? T`<p class="message message--hint">${this.hint}</p>` : ""}
565
+ </fieldset>
566
+ `;
567
+ }
568
+ };
569
+ customElements.define("bds-radio-group", BdsRadioGroup);
570
+
571
+ // src/web-components/interaction/form/bds-form-input.ts
572
+ var BdsFormInput = class extends i2 {
573
+ static formAssociated = true;
574
+ static styles = i`
575
+ :host {
576
+ display: block;
577
+ }
578
+
579
+ :host([disabled]) {
580
+ opacity: 0.5;
581
+ pointer-events: none;
582
+ }
583
+
584
+ .label {
585
+ display: block;
586
+ font-size: var(--bds-font_size--body--s);
587
+ font-family: var(--bds-font_family--body);
588
+ font-weight: var(--bds-font_weight--semibold);
589
+ color: var(--bds-color_on-bg);
590
+ margin-block-end: var(--bds-space_xxs);
591
+ }
592
+
593
+ .required {
594
+ color: var(--bds-color_error);
595
+ margin-inline-start: var(--bds-space_xxxs);
596
+ }
597
+
598
+ .wrapper {
599
+ position: relative;
600
+ display: flex;
601
+ align-items: center;
602
+ --bdc_color: currentcolor;
603
+ --bdc-outline_radius: var(--bds-border_radius--xs);
604
+
605
+ border-radius: var(--formInput_radius, var(--bdc-outline_radius));
606
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--formInput_shadow, var(--bdc-outline_shadow));
607
+ background-color: var(--bds-color_bg);
608
+ transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing);
609
+ }
610
+
611
+ :host([error]) .wrapper {
612
+ --bdc_color: var(--bds-color_error);
613
+ }
614
+
615
+ .wrapper:focus-within {
616
+ --bdc_color: var(--bdc_color--focus);
617
+ }
618
+
619
+ :host([error]) .wrapper:focus-within {
620
+ --bdc_color: var(--bds-color_error);
621
+ }
622
+
623
+ .input {
624
+ flex: 1;
625
+ min-inline-size: 0;
626
+ border: none;
627
+ background: transparent;
628
+ padding: var(--bds-space_s);
629
+ font-size: var(--bds-font_size--body);
630
+ font-family: var(--bds-font_family--body);
631
+ color: var(--bds-color_on-bg);
632
+ outline: none;
633
+ }
634
+
635
+ .input::placeholder {
636
+ color: var(--bds-color_on-bg--subtle);
637
+ }
638
+
639
+ .input:focus-visible {
640
+ outline: none;
641
+ }
642
+
643
+ .wrapper:focus-within {
644
+ outline: var(--bds-outline_default);
645
+ outline-offset: var(--bds-outline_offset);
646
+ border-radius: var(--formInput_radius, var(--bdc-outline_radius));
647
+ }
648
+
649
+ .prefix,
650
+ .suffix {
651
+ display: flex;
652
+ align-items: center;
653
+ color: var(--bds-color_on-bg--subtle);
654
+ flex-shrink: 0;
655
+ }
656
+
657
+ .prefix {
658
+ padding-inline-start: var(--bds-space_s);
659
+ }
660
+
661
+ .suffix {
662
+ padding-inline-end: var(--bds-space_s);
663
+ }
664
+
665
+ .prefix:empty,
666
+ .suffix:empty {
667
+ display: none;
668
+ }
669
+
670
+ .message {
671
+ margin-block-start: var(--bds-space_xxs);
672
+ font-size: var(--bds-font_size--body--s);
673
+ font-family: var(--bds-font_family--body);
674
+ }
675
+
676
+ .message--error {
677
+ color: var(--bds-color_error);
678
+ }
679
+
680
+ .message--hint {
681
+ color: var(--bds-color_on-bg--subtle);
682
+ }
683
+ `;
684
+ static properties = {
685
+ name: { type: String, reflect: true },
686
+ type: { type: String, reflect: true },
687
+ value: { type: String },
688
+ placeholder: { type: String },
689
+ label: { type: String },
690
+ hint: { type: String },
691
+ error: { type: String, reflect: true },
692
+ disabled: { type: Boolean, reflect: true },
693
+ required: { type: Boolean, reflect: true }
694
+ };
695
+ _internals;
696
+ _uid = Math.random().toString(36).slice(2, 8);
697
+ constructor() {
698
+ super();
699
+ this._internals = this.attachInternals();
700
+ this.name = "";
701
+ this.type = "text";
702
+ this.value = "";
703
+ this.placeholder = "";
704
+ this.label = "";
705
+ this.disabled = false;
706
+ this.required = false;
707
+ }
708
+ updated(changed) {
709
+ super.updated(changed);
710
+ if (changed.has("value")) {
711
+ this._internals.setFormValue?.(this.value);
712
+ }
713
+ if (changed.has("required") || changed.has("value")) {
714
+ if (this.required && !this.value) {
715
+ this._internals.setValidity?.({ valueMissing: true }, "Required");
716
+ } else {
717
+ this._internals.setValidity?.({});
718
+ }
719
+ }
720
+ }
721
+ formResetCallback() {
722
+ this.value = "";
723
+ this._internals.setFormValue?.("");
724
+ }
725
+ _handleInput(e) {
726
+ const input = e.target;
727
+ this.value = input.value;
728
+ this.dispatchEvent(new CustomEvent("bds-input", {
729
+ detail: { value: this.value },
730
+ bubbles: true,
731
+ composed: true
732
+ }));
733
+ }
734
+ _handleChange(e) {
735
+ const input = e.target;
736
+ this.value = input.value;
737
+ this.dispatchEvent(new CustomEvent("bds-change", {
738
+ detail: { value: this.value },
739
+ bubbles: true,
740
+ composed: true
741
+ }));
742
+ }
743
+ render() {
744
+ const id = `bds-form-input-${this._uid}`;
745
+ const hintId = `${id}-hint`;
746
+ const errorId = `${id}-error`;
747
+ const describedBy = [this.error && errorId, this.hint && hintId].filter(Boolean).join(" ") || void 0;
748
+ return T`
749
+ ${this.label ? T`
750
+ <label class="label" for=${id}>
751
+ ${this.label}
752
+ ${this.required ? T`<span class="required" aria-hidden="true">*</span>` : ""}
753
+ </label>
754
+ ` : ""}
755
+ <div class="wrapper">
756
+ <span class="prefix"><slot name="prefix"></slot></span>
757
+ <input
758
+ id=${id}
759
+ class="input"
760
+ type=${this.type}
761
+ .value=${this.value}
762
+ placeholder=${this.placeholder}
763
+ ?disabled=${this.disabled}
764
+ ?required=${this.required}
765
+ aria-invalid=${this.error ? "true" : "false"}
766
+ aria-describedby=${describedBy ?? ""}
767
+ aria-required=${this.required ? "true" : "false"}
768
+ @input=${this._handleInput}
769
+ @change=${this._handleChange}
770
+ />
771
+ <span class="suffix"><slot name="suffix"></slot></span>
772
+ </div>
773
+ ${this.error ? T`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>` : this.hint ? T`<p id=${hintId} class="message message--hint">${this.hint}</p>` : ""}
774
+ `;
775
+ }
776
+ };
777
+ customElements.define("bds-form-input", BdsFormInput);
778
+
779
+ // src/web-components/ui/bds-carousel.ts
780
+ var BdsCarousel = class extends i2 {
781
+ static styles = i`
782
+ :host {
783
+ display: block;
784
+ position: relative;
785
+ }
786
+
787
+ .track {
788
+ display: flex;
789
+ overflow-inline: auto;
790
+ scroll-snap-type: inline mandatory;
791
+ scroll-behavior: smooth;
792
+ scrollbar-width: none;
793
+ -ms-overflow-style: none;
794
+ }
795
+
796
+ .track::-webkit-scrollbar {
797
+ display: none;
798
+ }
799
+
800
+ ::slotted(*) {
801
+ flex-shrink: 0;
802
+ inline-size: 100%;
803
+ scroll-snap-align: start;
804
+ }
805
+
806
+ .controls {
807
+ display: flex;
808
+ align-items: center;
809
+ justify-content: space-between;
810
+ margin-block-start: var(--bds-space_s);
811
+ }
812
+
813
+ .nav-btn {
814
+ display: inline-flex;
815
+ align-items: center;
816
+ justify-content: center;
817
+ inline-size: 2.5rem;
818
+ block-size: 2.5rem;
819
+ border: none;
820
+ border-radius: var(--bds-border_radius--full, 9999px);
821
+ background-color: var(--bds-color_bg--subtle);
822
+ color: var(--bds-color_on-bg);
823
+ cursor: pointer;
824
+ transition: var(--bds-animation_transition);
825
+ }
826
+
827
+ .nav-btn:focus-visible {
828
+ outline: var(--bds-outline_default);
829
+ outline-offset: var(--bds-outline_offset);
830
+ }
831
+
832
+ @media (hover: hover) and (pointer: fine) {
833
+ .nav-btn:hover {
834
+ background-color: var(--bds-color_bg--strong);
835
+ }
836
+ }
837
+
838
+ .nav-btn:disabled {
839
+ opacity: 0.4;
840
+ cursor: not-allowed;
841
+ }
842
+
843
+ .nav-btn svg {
844
+ inline-size: 1.25rem;
845
+ block-size: 1.25rem;
846
+ }
847
+
848
+ .indicators {
849
+ display: flex;
850
+ gap: var(--bds-space_xxs);
851
+ align-items: center;
852
+ justify-content: center;
853
+ flex: 1;
854
+ }
855
+
856
+ .dot {
857
+ inline-size: 0.5rem;
858
+ block-size: 0.5rem;
859
+ border-radius: 50%;
860
+ border: none;
861
+ padding: 0;
862
+ background-color: var(--bds-color_bg--strong);
863
+ cursor: pointer;
864
+ transition: var(--bds-animation_transition);
865
+ }
866
+
867
+ .dot.--active {
868
+ background-color: var(--bds-color_interactive);
869
+ inline-size: 1rem;
870
+ border-radius: 0.25rem;
871
+ }
872
+
873
+ .dot:focus-visible {
874
+ outline: var(--bds-outline_default);
875
+ outline-offset: var(--bds-outline_offset);
876
+ }
877
+
878
+ @media (prefers-reduced-motion: reduce) {
879
+ .track {
880
+ scroll-behavior: auto;
881
+ }
882
+ }
883
+ `;
884
+ static properties = {
885
+ currentIndex: { type: Number, attribute: "current-index", reflect: true },
886
+ indicators: { type: Boolean, reflect: true },
887
+ loop: { type: Boolean, reflect: true }
888
+ };
889
+ _slideCount = 0;
890
+ _io = null;
891
+ constructor() {
892
+ super();
893
+ this.currentIndex = 0;
894
+ this.indicators = false;
895
+ this.loop = false;
896
+ }
897
+ get _track() {
898
+ return this.shadowRoot?.querySelector(".track") ?? null;
899
+ }
900
+ connectedCallback() {
901
+ super.connectedCallback();
902
+ this._updateSlideCount();
903
+ }
904
+ disconnectedCallback() {
905
+ super.disconnectedCallback();
906
+ this._io?.disconnect();
907
+ this._io = null;
908
+ }
909
+ _updateSlideCount() {
910
+ this._slideCount = this.children.length;
911
+ this.requestUpdate();
912
+ }
913
+ _goTo(index) {
914
+ const count = this._slideCount;
915
+ if (count === 0) return;
916
+ let next = index;
917
+ if (this.loop) {
918
+ next = (index % count + count) % count;
919
+ } else {
920
+ next = Math.max(0, Math.min(index, count - 1));
921
+ }
922
+ if (next === this.currentIndex && index === next) return;
923
+ this.currentIndex = next;
924
+ const track = this._track;
925
+ const slides = track?.querySelector("slot")?.assignedElements() ?? [];
926
+ const target = slides[next];
927
+ if (target && typeof target.scrollIntoView === "function") {
928
+ target.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" });
929
+ }
930
+ this.dispatchEvent(new CustomEvent("bds-slide-change", {
931
+ detail: { index: next },
932
+ bubbles: true,
933
+ composed: true
934
+ }));
935
+ }
936
+ _onSlotChange() {
937
+ this._updateSlideCount();
938
+ this._io?.disconnect();
939
+ const track = this._track;
940
+ if (!track) return;
941
+ const slot = track.querySelector("slot");
942
+ const slides = slot?.assignedElements() ?? [];
943
+ if (typeof IntersectionObserver === "undefined") return;
944
+ this._io = new IntersectionObserver(
945
+ (entries) => {
946
+ const visible = entries.find((e) => e.isIntersecting && e.intersectionRatio >= 0.5);
947
+ if (visible) {
948
+ const idx = slides.indexOf(visible.target);
949
+ if (idx !== -1 && idx !== this.currentIndex) {
950
+ this.currentIndex = idx;
951
+ this.requestUpdate();
952
+ }
953
+ }
954
+ },
955
+ { root: track, threshold: 0.5 }
956
+ );
957
+ slides.forEach((slide) => this._io.observe(slide));
958
+ }
959
+ render() {
960
+ const atStart = this.currentIndex <= 0;
961
+ const atEnd = this.currentIndex >= this._slideCount - 1;
962
+ const showPrev = this.loop || !atStart;
963
+ const showNext = this.loop || !atEnd;
964
+ return T`
965
+ <div class="track" aria-label="Carousel" role="region">
966
+ <slot @slotchange=${this._onSlotChange}></slot>
967
+ </div>
968
+ <div class="controls">
969
+ <slot name="prev">
970
+ <button
971
+ class="nav-btn"
972
+ type="button"
973
+ aria-label="Previous slide"
974
+ ?disabled=${!showPrev}
975
+ @click=${() => this._goTo(this.currentIndex - 1)}
976
+ >
977
+ <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
978
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
979
+ </svg>
980
+ </button>
981
+ </slot>
982
+
983
+ ${this.indicators ? T`
984
+ <div class="indicators" role="tablist" aria-label="Slides">
985
+ ${Array.from({ length: this._slideCount }, (_, i3) => T`
986
+ <button
987
+ class="dot ${i3 === this.currentIndex ? "--active" : ""}"
988
+ type="button"
989
+ role="tab"
990
+ aria-selected=${i3 === this.currentIndex ? "true" : "false"}
991
+ aria-label="Slide ${i3 + 1}"
992
+ @click=${() => this._goTo(i3)}
993
+ ></button>
994
+ `)}
995
+ </div>
996
+ ` : T`<span style="flex:1"></span>`}
997
+
998
+ <slot name="next">
999
+ <button
1000
+ class="nav-btn"
1001
+ type="button"
1002
+ aria-label="Next slide"
1003
+ ?disabled=${!showNext}
1004
+ @click=${() => this._goTo(this.currentIndex + 1)}
1005
+ >
1006
+ <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1007
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
1008
+ </svg>
1009
+ </button>
1010
+ </slot>
1011
+ </div>
1012
+ `;
1013
+ }
1014
+ };
1015
+ customElements.define("bds-carousel", BdsCarousel);
1016
+
1017
+ // src/web-components/ui/bds-calendar.ts
1018
+ function toISO(date) {
1019
+ const y = date.getFullYear();
1020
+ const m = String(date.getMonth() + 1).padStart(2, "0");
1021
+ const d = String(date.getDate()).padStart(2, "0");
1022
+ return `${y}-${m}-${d}`;
1023
+ }
1024
+ function parseISO(iso) {
1025
+ if (!iso) return null;
1026
+ const [y, m, d] = iso.split("-").map(Number);
1027
+ if (!y || !m || !d) return null;
1028
+ const date = new Date(y, m - 1, d);
1029
+ return isNaN(date.getTime()) ? null : date;
1030
+ }
1031
+ var BdsCalendar = class extends i2 {
1032
+ static formAssociated = true;
1033
+ static styles = i`
1034
+ :host {
1035
+ display: inline-block;
1036
+ font-family: var(--bds-font_family--body);
1037
+ font-size: var(--bds-font_size--body);
1038
+ }
1039
+
1040
+ :host([disabled]) {
1041
+ opacity: 0.5;
1042
+ pointer-events: none;
1043
+ }
1044
+
1045
+ .calendar {
1046
+ background-color: var(--bds-color_bg);
1047
+ border-radius: var(--bds-border_radius--m);
1048
+ box-shadow: var(--bds-shadow_s);
1049
+ padding: var(--bds-space_s);
1050
+ inline-size: fit-content;
1051
+ }
1052
+
1053
+ .header {
1054
+ display: flex;
1055
+ align-items: center;
1056
+ justify-content: space-between;
1057
+ margin-block-end: var(--bds-space_s);
1058
+ }
1059
+
1060
+ .month-label {
1061
+ font-weight: var(--bds-font_weight--semibold);
1062
+ color: var(--bds-color_on-bg);
1063
+ }
1064
+
1065
+ .nav-btn {
1066
+ display: inline-flex;
1067
+ align-items: center;
1068
+ justify-content: center;
1069
+ inline-size: 2rem;
1070
+ block-size: 2rem;
1071
+ border: none;
1072
+ border-radius: var(--bds-border_radius--xs);
1073
+ background: none;
1074
+ color: var(--bds-color_on-bg);
1075
+ cursor: pointer;
1076
+ transition: var(--bds-animation_transition);
1077
+ }
1078
+
1079
+ @media (hover: hover) and (pointer: fine) {
1080
+ .nav-btn:hover {
1081
+ background-color: var(--bds-color_bg--subtle);
1082
+ }
1083
+ }
1084
+
1085
+ .nav-btn:focus-visible {
1086
+ outline: var(--bds-outline_default);
1087
+ outline-offset: var(--bds-outline_offset);
1088
+ }
1089
+
1090
+ .nav-btn svg {
1091
+ inline-size: 1rem;
1092
+ block-size: 1rem;
1093
+ }
1094
+
1095
+ .grid {
1096
+ display: grid;
1097
+ grid-template-columns: repeat(7, 2.25rem);
1098
+ gap: var(--bds-space_xxxs);
1099
+ }
1100
+
1101
+ .weekday {
1102
+ display: flex;
1103
+ align-items: center;
1104
+ justify-content: center;
1105
+ block-size: 2rem;
1106
+ font-size: var(--bds-font_size--body--s);
1107
+ font-weight: var(--bds-font_weight--semibold);
1108
+ color: var(--bds-color_on-bg--subtle);
1109
+ user-select: none;
1110
+ }
1111
+
1112
+ .day {
1113
+ display: flex;
1114
+ align-items: center;
1115
+ justify-content: center;
1116
+ inline-size: 2.25rem;
1117
+ block-size: 2.25rem;
1118
+ border: none;
1119
+ border-radius: var(--bds-border_radius--xs);
1120
+ background: none;
1121
+ color: var(--bds-color_on-bg);
1122
+ cursor: pointer;
1123
+ font-size: var(--bds-font_size--body--s);
1124
+ font-family: inherit;
1125
+ transition: var(--bds-animation_transition);
1126
+ }
1127
+
1128
+ .day:focus-visible {
1129
+ outline: var(--bds-outline_default);
1130
+ outline-offset: var(--bds-outline_offset);
1131
+ }
1132
+
1133
+ @media (hover: hover) and (pointer: fine) {
1134
+ .day:not(.--selected):not(.--outside):not([disabled]):hover {
1135
+ background-color: var(--bds-color_bg--subtle);
1136
+ }
1137
+ }
1138
+
1139
+ .day.--today {
1140
+ font-weight: var(--bds-font_weight--semibold);
1141
+ color: var(--bds-color_interactive_on-bg);
1142
+ }
1143
+
1144
+ .day.--selected {
1145
+ background-color: var(--bds-color_interactive);
1146
+ color: var(--bds-color_on-interactive);
1147
+ font-weight: var(--bds-font_weight--semibold);
1148
+ }
1149
+
1150
+ .day.--outside {
1151
+ color: var(--bds-color_on-bg--subtle);
1152
+ opacity: 0.4;
1153
+ }
1154
+
1155
+ .day[disabled] {
1156
+ opacity: 0.3;
1157
+ cursor: not-allowed;
1158
+ }
1159
+ `;
1160
+ static properties = {
1161
+ name: { type: String, reflect: true },
1162
+ value: { type: String, reflect: true },
1163
+ min: { type: String },
1164
+ max: { type: String },
1165
+ disabled: { type: Boolean, reflect: true }
1166
+ };
1167
+ _internals;
1168
+ _viewYear;
1169
+ _viewMonth;
1170
+ // 0-based
1171
+ constructor() {
1172
+ super();
1173
+ this._internals = this.attachInternals();
1174
+ this.name = "";
1175
+ this.value = "";
1176
+ this.min = "";
1177
+ this.max = "";
1178
+ this.disabled = false;
1179
+ const now = /* @__PURE__ */ new Date();
1180
+ this._viewYear = now.getFullYear();
1181
+ this._viewMonth = now.getMonth();
1182
+ }
1183
+ updated(changed) {
1184
+ super.updated(changed);
1185
+ if (changed.has("value")) {
1186
+ this._internals.setFormValue?.(this.value);
1187
+ if (this.value) {
1188
+ const d = parseISO(this.value);
1189
+ if (d) {
1190
+ this._viewYear = d.getFullYear();
1191
+ this._viewMonth = d.getMonth();
1192
+ }
1193
+ }
1194
+ }
1195
+ }
1196
+ formResetCallback() {
1197
+ this.value = "";
1198
+ this._internals.setFormValue?.("");
1199
+ }
1200
+ _prevMonth() {
1201
+ if (this._viewMonth === 0) {
1202
+ this._viewMonth = 11;
1203
+ this._viewYear--;
1204
+ } else {
1205
+ this._viewMonth--;
1206
+ }
1207
+ this.requestUpdate();
1208
+ }
1209
+ _nextMonth() {
1210
+ if (this._viewMonth === 11) {
1211
+ this._viewMonth = 0;
1212
+ this._viewYear++;
1213
+ } else {
1214
+ this._viewMonth++;
1215
+ }
1216
+ this.requestUpdate();
1217
+ }
1218
+ _selectDate(iso) {
1219
+ if (this._isDisabledDate(iso)) return;
1220
+ this.value = iso;
1221
+ this._internals.setFormValue?.(iso);
1222
+ this.dispatchEvent(new CustomEvent("bds-date-select", {
1223
+ detail: { date: iso },
1224
+ bubbles: true,
1225
+ composed: true
1226
+ }));
1227
+ }
1228
+ _isDisabledDate(iso) {
1229
+ if (this.min && iso < this.min) return true;
1230
+ if (this.max && iso > this.max) return true;
1231
+ return false;
1232
+ }
1233
+ _handleKeydown = (e) => {
1234
+ const focused = this.shadowRoot?.activeElement;
1235
+ if (!focused?.dataset.date) return;
1236
+ const current = parseISO(focused.dataset.date);
1237
+ if (!current) return;
1238
+ let next = null;
1239
+ if (e.key === "ArrowRight") {
1240
+ e.preventDefault();
1241
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + 1);
1242
+ } else if (e.key === "ArrowLeft") {
1243
+ e.preventDefault();
1244
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() - 1);
1245
+ } else if (e.key === "ArrowDown") {
1246
+ e.preventDefault();
1247
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + 7);
1248
+ } else if (e.key === "ArrowUp") {
1249
+ e.preventDefault();
1250
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() - 7);
1251
+ } else if (e.key === "Home") {
1252
+ e.preventDefault();
1253
+ const dow = current.getDay();
1254
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() - dow);
1255
+ } else if (e.key === "End") {
1256
+ e.preventDefault();
1257
+ const dow = current.getDay();
1258
+ next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + (6 - dow));
1259
+ } else if (e.key === "PageUp") {
1260
+ e.preventDefault();
1261
+ this._prevMonth();
1262
+ return;
1263
+ } else if (e.key === "PageDown") {
1264
+ e.preventDefault();
1265
+ this._nextMonth();
1266
+ return;
1267
+ }
1268
+ if (next) {
1269
+ const iso = toISO(next);
1270
+ if (next.getMonth() !== this._viewMonth || next.getFullYear() !== this._viewYear) {
1271
+ this._viewYear = next.getFullYear();
1272
+ this._viewMonth = next.getMonth();
1273
+ this.requestUpdate();
1274
+ this.updateComplete.then(() => {
1275
+ this.shadowRoot?.querySelector(`[data-date="${iso}"]`)?.focus();
1276
+ });
1277
+ } else {
1278
+ this.shadowRoot?.querySelector(`[data-date="${iso}"]`)?.focus();
1279
+ }
1280
+ }
1281
+ };
1282
+ _buildGrid() {
1283
+ const year = this._viewYear;
1284
+ const month = this._viewMonth;
1285
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1286
+ const firstDow = new Date(year, month, 1).getDay();
1287
+ const cells = [];
1288
+ const prevMonthDays = new Date(year, month, 0).getDate();
1289
+ for (let i3 = firstDow - 1; i3 >= 0; i3--) {
1290
+ const d = new Date(year, month - 1, prevMonthDays - i3);
1291
+ cells.push({ iso: toISO(d), outside: true });
1292
+ }
1293
+ for (let d = 1; d <= daysInMonth; d++) {
1294
+ cells.push({ iso: toISO(new Date(year, month, d)), outside: false });
1295
+ }
1296
+ const trailing = (7 - cells.length % 7) % 7;
1297
+ for (let d = 1; d <= trailing; d++) {
1298
+ cells.push({ iso: toISO(new Date(year, month + 1, d)), outside: true });
1299
+ }
1300
+ return cells;
1301
+ }
1302
+ render() {
1303
+ const today = toISO(/* @__PURE__ */ new Date());
1304
+ const cells = this._buildGrid();
1305
+ const fmt = new Intl.DateTimeFormat(void 0, { month: "long", year: "numeric" });
1306
+ const monthLabel = fmt.format(new Date(this._viewYear, this._viewMonth, 1));
1307
+ const weekdayFmt = new Intl.DateTimeFormat(void 0, { weekday: "short" });
1308
+ const weekdays = Array.from({ length: 7 }, (_, i3) => {
1309
+ const sunday = new Date(2023, 0, 1 + i3);
1310
+ return weekdayFmt.format(sunday);
1311
+ });
1312
+ return T`
1313
+ <div class="calendar" @keydown=${this._handleKeydown}>
1314
+ <div class="header">
1315
+ <button
1316
+ class="nav-btn"
1317
+ type="button"
1318
+ aria-label="Previous month"
1319
+ @click=${this._prevMonth}
1320
+ >
1321
+ <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1322
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
1323
+ </svg>
1324
+ </button>
1325
+ <span class="month-label" aria-live="polite">${monthLabel}</span>
1326
+ <button
1327
+ class="nav-btn"
1328
+ type="button"
1329
+ aria-label="Next month"
1330
+ @click=${this._nextMonth}
1331
+ >
1332
+ <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1333
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
1334
+ </svg>
1335
+ </button>
1336
+ </div>
1337
+
1338
+ <div class="grid" role="grid" aria-label=${monthLabel}>
1339
+ ${weekdays.map((day) => T`<div class="weekday" role="columnheader" aria-label=${day}>${day}</div>`)}
1340
+ ${cells.map(({ iso, outside }) => {
1341
+ const isSelected = iso === this.value;
1342
+ const isToday = iso === today;
1343
+ const isDisabled = this._isDisabledDate(iso);
1344
+ const classes = [
1345
+ "day",
1346
+ outside ? "--outside" : "",
1347
+ isSelected ? "--selected" : "",
1348
+ isToday && !isSelected ? "--today" : ""
1349
+ ].filter(Boolean).join(" ");
1350
+ return T`
1351
+ <button
1352
+ class=${classes}
1353
+ type="button"
1354
+ role="gridcell"
1355
+ data-date=${iso}
1356
+ ?disabled=${isDisabled || outside}
1357
+ aria-selected=${isSelected ? "true" : "false"}
1358
+ aria-current=${isToday ? "date" : "false"}
1359
+ aria-disabled=${isDisabled ? "true" : "false"}
1360
+ tabindex=${isSelected || !this.value && isToday ? "0" : "-1"}
1361
+ @click=${() => this._selectDate(iso)}
1362
+ >
1363
+ ${(/* @__PURE__ */ new Date(iso + "T00:00:00")).getDate()}
1364
+ </button>
1365
+ `;
1366
+ })}
1367
+ </div>
1368
+ </div>
1369
+ `;
1370
+ }
1371
+ };
1372
+ customElements.define("bds-calendar", BdsCalendar);
1373
+
1374
+ // src/web-components/interaction/bds-dropdown-menu.ts
1375
+ var BdsDropdownMenu = class extends i2 {
1376
+ static styles = i`
1377
+ :host {
1378
+ display: inline-block;
1379
+ position: relative;
1380
+ }
1381
+
1382
+ .panel {
1383
+ position: absolute;
1384
+ inset-block-start: calc(100% + var(--bds-space_xxs));
1385
+ inset-inline-start: 0;
1386
+ min-inline-size: 10rem;
1387
+ background-color: var(--bds-color_bg);
1388
+ border-radius: var(--bds-border_radius--s);
1389
+ box-shadow: var(--bds-shadow_m);
1390
+ padding: var(--bds-space_xxs);
1391
+ z-index: var(--bds-z-index_dropdown, 200);
1392
+ display: none;
1393
+ flex-direction: column;
1394
+ }
1395
+
1396
+ :host([placement='bottom-end']) .panel {
1397
+ inset-inline-start: auto;
1398
+ inset-inline-end: 0;
1399
+ }
1400
+
1401
+ :host([placement='top-start']) .panel {
1402
+ inset-block-start: auto;
1403
+ inset-block-end: calc(100% + var(--bds-space_xxs));
1404
+ inset-inline-start: 0;
1405
+ }
1406
+
1407
+ :host([placement='top-end']) .panel {
1408
+ inset-block-start: auto;
1409
+ inset-block-end: calc(100% + var(--bds-space_xxs));
1410
+ inset-inline-start: auto;
1411
+ inset-inline-end: 0;
1412
+ }
1413
+
1414
+ :host([open]) .panel {
1415
+ display: flex;
1416
+ }
1417
+ `;
1418
+ static properties = {
1419
+ open: { type: Boolean, reflect: true },
1420
+ placement: { type: String, reflect: true }
1421
+ };
1422
+ constructor() {
1423
+ super();
1424
+ this.open = false;
1425
+ this.placement = "bottom-start";
1426
+ }
1427
+ connectedCallback() {
1428
+ super.connectedCallback();
1429
+ document.addEventListener("click", this._handleOutsideClick);
1430
+ this.addEventListener("keydown", this._handleKeydown);
1431
+ this.addEventListener("bds-select", this._handleItemSelect);
1432
+ }
1433
+ disconnectedCallback() {
1434
+ super.disconnectedCallback();
1435
+ document.removeEventListener("click", this._handleOutsideClick);
1436
+ this.removeEventListener("keydown", this._handleKeydown);
1437
+ this.removeEventListener("bds-select", this._handleItemSelect);
1438
+ }
1439
+ get _items() {
1440
+ return [...this.querySelectorAll("bds-dropdown-menu-item")].filter(
1441
+ (item) => !item.disabled
1442
+ );
1443
+ }
1444
+ get _trigger() {
1445
+ const slot = this.shadowRoot?.querySelector('slot[name="trigger"]');
1446
+ return slot?.assignedElements()[0] ?? null;
1447
+ }
1448
+ _open() {
1449
+ if (this.open) return;
1450
+ this.open = true;
1451
+ this.dispatchEvent(new CustomEvent("bds-open", { bubbles: true, composed: true }));
1452
+ this.updateComplete.then(() => {
1453
+ this._items[0]?.shadowRoot?.querySelector("button")?.focus();
1454
+ });
1455
+ }
1456
+ _close() {
1457
+ if (!this.open) return;
1458
+ this.open = false;
1459
+ this.dispatchEvent(new CustomEvent("bds-close", { bubbles: true, composed: true }));
1460
+ this._trigger?.focus();
1461
+ }
1462
+ _handleTriggerClick = (e) => {
1463
+ e.stopPropagation();
1464
+ if (this.open) {
1465
+ this._close();
1466
+ } else {
1467
+ this._open();
1468
+ }
1469
+ };
1470
+ _handleOutsideClick = (e) => {
1471
+ if (this.open && !this.contains(e.target)) {
1472
+ this._close();
1473
+ }
1474
+ };
1475
+ _handleKeydown = (e) => {
1476
+ if (!this.open) return;
1477
+ const items = this._items;
1478
+ const focused = items.findIndex(
1479
+ (item) => item.shadowRoot?.activeElement !== null && item.shadowRoot?.querySelector("button") === item.shadowRoot?.activeElement
1480
+ );
1481
+ if (e.key === "Escape") {
1482
+ e.preventDefault();
1483
+ this._close();
1484
+ } else if (e.key === "ArrowDown") {
1485
+ e.preventDefault();
1486
+ const next = (focused + 1) % items.length;
1487
+ items[next]?.shadowRoot?.querySelector("button")?.focus();
1488
+ } else if (e.key === "ArrowUp") {
1489
+ e.preventDefault();
1490
+ const prev = (focused - 1 + items.length) % items.length;
1491
+ items[prev]?.shadowRoot?.querySelector("button")?.focus();
1492
+ } else if (e.key === "Tab") {
1493
+ this._close();
1494
+ }
1495
+ };
1496
+ _handleItemSelect = () => {
1497
+ this._close();
1498
+ };
1499
+ _onTriggerSlotChange() {
1500
+ const trigger = this._trigger;
1501
+ if (trigger) {
1502
+ trigger.removeEventListener("click", this._handleTriggerClick);
1503
+ trigger.addEventListener("click", this._handleTriggerClick);
1504
+ }
1505
+ }
1506
+ render() {
1507
+ return T`
1508
+ <slot name="trigger" @slotchange=${this._onTriggerSlotChange}></slot>
1509
+ <div
1510
+ class="panel"
1511
+ role="menu"
1512
+ aria-hidden=${!this.open ? "true" : "false"}
1513
+ >
1514
+ <slot></slot>
1515
+ </div>
1516
+ `;
1517
+ }
1518
+ };
1519
+ customElements.define("bds-dropdown-menu", BdsDropdownMenu);
1520
+
1521
+ // src/web-components/interaction/bds-dropdown-menu-item.ts
1522
+ var BdsDropdownMenuItem = class extends i2 {
1523
+ static styles = i`
1524
+ :host {
1525
+ display: block;
1526
+ }
1527
+
1528
+ :host([disabled]) {
1529
+ opacity: 0.5;
1530
+ pointer-events: none;
1531
+ }
1532
+
1533
+ .item {
1534
+ display: flex;
1535
+ align-items: center;
1536
+ gap: var(--bds-space_xs);
1537
+ inline-size: 100%;
1538
+ padding: var(--bds-space_xs) var(--bds-space_s);
1539
+ font-size: var(--bds-font_size--body);
1540
+ font-family: var(--bds-font_family--body);
1541
+ color: var(--bds-color_on-bg);
1542
+ background: none;
1543
+ border: none;
1544
+ text-align: start;
1545
+ cursor: pointer;
1546
+ border-radius: var(--bds-border_radius--xs);
1547
+ transition: var(--bds-animation_transition);
1548
+ }
1549
+
1550
+ :host([destructive]) .item {
1551
+ color: var(--bds-color_error);
1552
+ }
1553
+
1554
+ @media (hover: hover) and (pointer: fine) {
1555
+ .item:hover {
1556
+ background-color: var(--bds-color_bg--subtle);
1557
+ }
1558
+
1559
+ :host([destructive]) .item:hover {
1560
+ background-color: color-mix(in srgb, var(--bds-color_error) 10%, transparent);
1561
+ }
1562
+ }
1563
+
1564
+ .item:focus-visible {
1565
+ outline: var(--bds-outline_default);
1566
+ outline-offset: var(--bds-outline_offset);
1567
+ }
1568
+ `;
1569
+ static properties = {
1570
+ value: { type: String, reflect: true },
1571
+ disabled: { type: Boolean, reflect: true },
1572
+ destructive: { type: Boolean, reflect: true }
1573
+ };
1574
+ constructor() {
1575
+ super();
1576
+ this.value = "";
1577
+ this.disabled = false;
1578
+ this.destructive = false;
1579
+ }
1580
+ _handleClick() {
1581
+ if (this.disabled) return;
1582
+ this.dispatchEvent(new CustomEvent("bds-select", {
1583
+ detail: { value: this.value },
1584
+ bubbles: true,
1585
+ composed: true
1586
+ }));
1587
+ }
1588
+ _handleKeydown(e) {
1589
+ if (e.key === "Enter" || e.key === " ") {
1590
+ e.preventDefault();
1591
+ this._handleClick();
1592
+ }
1593
+ }
1594
+ render() {
1595
+ return T`
1596
+ <button
1597
+ class="item"
1598
+ type="button"
1599
+ role="menuitem"
1600
+ tabindex=${this.disabled ? "-1" : "0"}
1601
+ ?disabled=${this.disabled}
1602
+ @click=${this._handleClick}
1603
+ @keydown=${this._handleKeydown}
1604
+ >
1605
+ <slot></slot>
1606
+ </button>
1607
+ `;
1608
+ }
1609
+ };
1610
+ customElements.define("bds-dropdown-menu-item", BdsDropdownMenuItem);
124
1611
  export {
125
1612
  BdsAccordion,
126
1613
  BdsAccordionItem,
@@ -129,23 +1616,32 @@ export {
129
1616
  BdsBadge,
130
1617
  BdsBreadcrumb,
131
1618
  BdsButton,
1619
+ BdsButtonGroup,
1620
+ BdsCalendar,
132
1621
  BdsCard,
1622
+ BdsCarousel,
133
1623
  BdsCheckbox,
1624
+ BdsCheckboxGroup,
134
1625
  BdsCollapsible,
135
1626
  BdsCombobox,
136
1627
  BdsDescriptionList,
137
1628
  BdsDialog,
138
1629
  BdsDrawer,
1630
+ BdsDropdownMenu,
1631
+ BdsDropdownMenuItem,
139
1632
  BdsFileInput,
1633
+ BdsFormInput,
140
1634
  BdsIconWrapper,
141
1635
  BdsLink,
142
1636
  BdsLoading,
143
1637
  BdsNotificationBanner,
144
1638
  BdsNumberInput,
1639
+ BdsPagination,
145
1640
  BdsPopover,
146
1641
  BdsProgress,
147
1642
  BdsProgressCircle,
148
1643
  BdsRadio,
1644
+ BdsRadioGroup,
149
1645
  BdsRating,
150
1646
  BdsSectionHeader,
151
1647
  BdsSegmentedControl,
@@ -157,6 +1653,7 @@ export {
157
1653
  BdsSwitch,
158
1654
  BdsTab,
159
1655
  BdsTabPanel,
1656
+ BdsTable,
160
1657
  BdsTabs,
161
1658
  BdsTextarea,
162
1659
  BdsToastProvider,