@ibobbyts/svelte-ui-utils 0.3.9 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +100 -21
  2. package/dist/data-table/DataTable.svelte +139 -22
  3. package/dist/data-table/DataTable.svelte.d.ts +2 -8
  4. package/dist/data-table/DataTable.svelte.d.ts.map +1 -1
  5. package/dist/data-table/FilterControl.svelte +64 -18
  6. package/dist/data-table/FilterControl.svelte.d.ts.map +1 -1
  7. package/dist/data-table/filter.d.ts +3 -1
  8. package/dist/data-table/filter.d.ts.map +1 -1
  9. package/dist/data-table/filter.js +4 -0
  10. package/dist/data-table/index.d.ts +1 -1
  11. package/dist/data-table/index.d.ts.map +1 -1
  12. package/dist/data-table/types.d.ts +37 -3
  13. package/dist/data-table/types.d.ts.map +1 -1
  14. package/dist/dropdown/Dropdown.svelte +102 -0
  15. package/dist/dropdown/Dropdown.svelte.d.ts +2 -0
  16. package/dist/dropdown/Dropdown.svelte.d.ts.map +1 -1
  17. package/dist/dropdown/DropdownMultiSelect.svelte +1 -1
  18. package/dist/dropdown/types.d.ts +2 -0
  19. package/dist/dropdown/types.d.ts.map +1 -1
  20. package/dist/dropdown-search/DropdownSearch.svelte +47 -0
  21. package/dist/dropdown-search/DropdownSearch.svelte.d.ts.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/ordered-list/OrderedListEditor.svelte +1 -1
  26. package/dist/pagination/Pagination.svelte +9 -0
  27. package/dist/pagination/Pagination.svelte.d.ts +1 -0
  28. package/dist/pagination/Pagination.svelte.d.ts.map +1 -1
  29. package/dist/sortable-list/SortableList.svelte +1 -1
  30. package/dist/sortable-table/SortableTable.svelte +1 -1
  31. package/dist/style.css +97 -12
  32. package/dist/tooltip/Tooltip.svelte +149 -0
  33. package/dist/tooltip/Tooltip.svelte.d.ts +22 -0
  34. package/dist/tooltip/Tooltip.svelte.d.ts.map +1 -0
  35. package/dist/tooltip/index.d.ts +2 -0
  36. package/dist/tooltip/index.d.ts.map +1 -0
  37. package/dist/tooltip/index.js +1 -0
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -393,6 +393,18 @@ use `menuAlign="right"` to align their right edges instead. `fitContent` sizes
393
393
  the menu to its longest option while keeping the selected edge aligned.
394
394
  `DataTable` uses this same component for its page-size picker.
395
395
 
396
+ For keyboard typeahead, an option may provide `searchText` separately from its
397
+ display `label`. Typeahead matches the beginning of `searchText` or `label`,
398
+ while the label remains the only text shown in the trigger and menu:
399
+
400
+ ```svelte
401
+ <Dropdown
402
+ value="cn"
403
+ options={[{ label: '中国', searchText: 'China', value: 'cn' }]}
404
+ ariaLabel="Country"
405
+ />
406
+ ```
407
+
396
408
  Set `multiselect={true}` to control the component with a `DropdownValue[]`.
397
409
  Each option then shows a checkbox, selecting or deselecting an option keeps the
398
410
  menu open, and `onChange` receives the complete selected-value array in option
@@ -507,7 +519,16 @@ can stop event propagation without preventing the dropdown from opening.
507
519
 
508
520
  ```svelte
509
521
  <script lang="ts">
510
- import { DataTable } from '@ibobbyts/svelte-ui-utils/table';
522
+ import {
523
+ DataTable,
524
+ type PaginationState,
525
+ type SortState
526
+ } from '@ibobbyts/svelte-ui-utils/table';
527
+
528
+ type UserRow = {
529
+ name: string;
530
+ createdAt: string;
531
+ };
511
532
 
512
533
  const columns = [
513
534
  { key: 'name', header: 'Name', sortable: true },
@@ -522,9 +543,27 @@ can stop event propagation without preventing the dropdown from opening.
522
543
  }
523
544
  ];
524
545
 
525
- let sort = null;
526
- let page = 1;
527
- let pageSize = 20;
546
+ let rows: UserRow[] = [];
547
+ let totalRows = 0;
548
+ let searchTerm = '';
549
+ let sort: SortState | null = null;
550
+
551
+ async function loadRows({ page, pageSize }: PaginationState) {
552
+ const params = new URLSearchParams({
553
+ page: String(page),
554
+ pageSize: String(pageSize),
555
+ search: searchTerm
556
+ });
557
+ const response = await fetch(`/api/users?${params}`);
558
+
559
+ if (!response.ok) {
560
+ throw new Error('Failed to load users');
561
+ }
562
+
563
+ const result = (await response.json()) as { rows: UserRow[]; totalRows: number };
564
+ rows = result.rows;
565
+ totalRows = result.totalRows;
566
+ }
528
567
  </script>
529
568
 
530
569
  <DataTable
@@ -532,27 +571,44 @@ can stop event propagation without preventing the dropdown from opening.
532
571
  rows={rows}
533
572
  {columns}
534
573
  {sort}
535
- {page}
536
- {pageSize}
537
- totalRows={totalRows}
574
+ pagination={{
575
+ tableId: 'admin-users',
576
+ totalRows,
577
+ defaultPageSize: 20,
578
+ pageSizeOptions: [10, 20, 50, 100],
579
+ persistPageSize: true,
580
+ queryKey: searchTerm,
581
+ onRequest: loadRows
582
+ }}
538
583
  tableLayout="auto"
539
584
  stickyHeader={true}
540
585
  onSortChange={(next) => {
541
586
  sort = next;
542
- page = 1;
543
- }}
544
- onPaginationChange={(next) => {
545
- page = next.page;
546
- pageSize = next.pageSize;
547
587
  }}
548
588
  />
549
589
  ```
550
590
 
591
+ `DataTable` only supports server pagination. Pass the current page rows in
592
+ `rows`, the server count in `pagination.totalRows`, and load each requested page
593
+ through `pagination.onRequest`. The component owns page and page-size state,
594
+ resets to page `1` when the primitive `queryKey` changes, disables both
595
+ pagination bars while a request is pending, and rolls back its controls if that
596
+ request rejects. The callback remains responsible for presenting request errors.
597
+
598
+ Set `persistPageSize: true` and provide a stable, non-empty `tableId` to retain
599
+ the selected page size in browser `localStorage`. The generated key is
600
+ `svelte-ui-utils:data-table:<tableId>:page-size`; callers do not construct it.
601
+ Stored values are accepted only when present in the normalized
602
+ `pageSizeOptions`. Storage failures are ignored. Options default to
603
+ `[10, 20, 50, 100]`, and `defaultPageSize` falls back to the first unique
604
+ positive integer option when omitted or invalid.
605
+
551
606
  `DataTable` renders page-number pagination above and below the data table by
552
607
  default, including a page-size selector. Use `language` for package-owned
553
608
  defaults such as empty state, pagination label, and page-size label; use
554
- `pageSizeLabel` or `emptyText` when a specific app needs to override them.
555
- Use `showPagination={false}` for static tables. Use `showHeader={false}` for
609
+ `pagination.pageSizeLabel` or `emptyText` when a specific app needs to override
610
+ them.
611
+ Pass `pagination={false}` for static tables. Use `showHeader={false}` for
556
612
  tables that should render body rows without a header section. Sortable headers
557
613
  preserve the current window scroll position by default and wait for an async
558
614
  `onSortChange` before restoring scroll position.
@@ -590,9 +646,10 @@ pagination outside `DataTable`:
590
646
  />
591
647
  ```
592
648
 
593
- Render two synchronized pagination bars by passing both instances the same
594
- controlled `pagination` value and the same `onPaginationChange` handler. This
595
- is the same contract `DataTable` uses for its top and bottom pagination.
649
+ Render two synchronized standalone pagination bars by passing both instances
650
+ the same controlled `pagination` value and the same `onPaginationChange`
651
+ handler. Standalone `Pagination` remains controlled; `DataTable` owns its own
652
+ server-pagination state.
596
653
 
597
654
  `FilterTable` is filter-only. It accepts `rows`, where each row has a `title`
598
655
  for the left column and a controlled filter created with the `filter` helper:
@@ -676,16 +733,17 @@ toast close labels, dropdown loading/empty/clear labels, table empty and
676
733
  pagination labels, date range labels and presets, and number range labels.
677
734
  Business labels such as column headers, filter row titles, button labels, and
678
735
  placeholders should still be passed by the consuming app. Explicit props such
679
- as `closeLabel`, `clearLabel`, `noResultsText`, `emptyText`, `pageSizeLabel`,
680
- `startLabel`, and `minLabel` always override the language defaults.
736
+ as `closeLabel`, `clearLabel`, `noResultsText`, `emptyText`, `startLabel`, and
737
+ `minLabel`, plus nested settings such as `pagination.pageSizeLabel`, always
738
+ override the language defaults.
681
739
 
682
- Use `DataTable showPagination={false}` for a non-paginated data table:
740
+ Pass `pagination={false}` for a non-paginated data table:
683
741
 
684
742
  ```svelte
685
743
  <DataTable
686
744
  rows={rows}
687
745
  {columns}
688
- showPagination={false}
746
+ pagination={false}
689
747
  rowKey="id"
690
748
  tableLayout="fixed"
691
749
  stickyHeader={true}
@@ -736,6 +794,27 @@ inside a theme root:
736
794
  }
737
795
  ```
738
796
 
797
+ ### Corner-radius tokens
798
+
799
+ All non-zero component corner radii come from the token definitions in
800
+ `src/lib/style.css`. Reuse an existing token before adding another radius; when
801
+ a confirmed design requirement needs a new size, define it in that token block
802
+ first and reference it from components.
803
+
804
+ | Token | Default | Intended use |
805
+ | --- | ---: | --- |
806
+ | `--suu-radius-xs` | `4px` | compact checks and small indicators |
807
+ | `--suu-radius-sm` | `6px` | compact controls |
808
+ | `--suu-radius-md` | `8px` | standard controls and surfaces |
809
+ | `--suu-radius-lg` | standard radius plus `2px` | dialogs and prominent surfaces |
810
+ | `--suu-radius-pill` | `999px` | pills and circular controls |
811
+ | `--suu-radius` | `--suu-radius-md` | backward-compatible standard radius override |
812
+ | `--suu-radius-inner` | standard radius minus `1px` | inner fill inside a standard `1px` border |
813
+
814
+ Use `0` directly when a component intentionally has square corners. Component
815
+ styles must not introduce other numeric `border-radius` values or ad hoc radius
816
+ calculations.
817
+
739
818
  ## Release
740
819
 
741
820
  Do not publish this package as part of consumer-app local integration work.
@@ -1,16 +1,18 @@
1
1
  <svelte:options runes={false} />
2
2
 
3
3
  <script lang="ts">
4
+ import { onMount } from 'svelte';
4
5
  import BaseDataTable from './BaseDataTable.svelte';
5
6
  import Pagination from '../pagination/Pagination.svelte';
6
7
  import { getUiMessages, type UiLanguage } from '../i18n.js';
8
+ import { normalizePagination } from './state.js';
7
9
  import type {
8
10
  DataTableColumn,
9
11
  DataTableLayout,
10
12
  DataTableRowAttributes,
11
13
  DataTableRowKey,
12
14
  DataTableSortChangeHandler,
13
- PaginationChangeHandler,
15
+ DataTableServerPagination,
14
16
  PaginationState,
15
17
  SortState
16
18
  } from './types.js';
@@ -23,14 +25,8 @@
23
25
  export let sort: SortState | null = null;
24
26
  export let onSortChange: DataTableSortChangeHandler | undefined = undefined;
25
27
  export let showHeader = true;
26
- export let showPagination = true;
27
- export let page = 1;
28
- export let pageSize = 20;
29
- export let totalRows: number | undefined = undefined;
30
- export let pageSizeOptions: number[] = [10, 20, 50, 100];
31
- export let pageSizeLabel: string | undefined = undefined;
28
+ export let pagination: false | DataTableServerPagination;
32
29
  export let maxPageButtons = 15;
33
- export let onPaginationChange: PaginationChangeHandler | undefined = undefined;
34
30
  export let zebra = true;
35
31
  export let hoverable = true;
36
32
  export let bordered = true;
@@ -43,30 +39,150 @@
43
39
  export let rowClass: string | ((row: unknown, index: number) => string | undefined | null) | undefined = undefined;
44
40
  export let rowAttributes: DataTableRowAttributes | undefined = undefined;
45
41
 
42
+ const defaultPageSizeOptions = [10, 20, 50, 100];
43
+ const initialPaginationConfig = pagination === false ? undefined : pagination;
44
+
45
+ let currentPagination: PaginationState = {
46
+ page: 1,
47
+ pageSize: resolveDefaultPageSize(initialPaginationConfig)
48
+ };
49
+ let requestPending = false;
50
+ let requestSequence = 0;
51
+ let mounted = false;
52
+ let lastQueryKey = initialPaginationConfig?.queryKey;
53
+
46
54
  $: messages = getUiMessages(language);
47
55
  $: resolvedEmptyText = emptyText ?? messages.table.emptyText;
48
- $: resolvedPageSizeLabel = pageSizeLabel ?? messages.table.pageSizeLabel;
49
- $: resolvedTotalRows = totalRows ?? rows.length;
50
- $: minimumPageSizeOption = pageSizeOptions.length > 0 ? Math.min(...pageSizeOptions) : 0;
51
- $: shouldShowPagination = showPagination && (minimumPageSizeOption <= 0 || resolvedTotalRows >= minimumPageSizeOption);
52
- $: pagination = { page, pageSize };
53
-
54
- function updatePagination(next: PaginationState) {
55
- void onPaginationChange?.(next);
56
+ $: serverPagination = pagination === false ? undefined : pagination;
57
+ $: resolvedPageSizeOptions = normalizePageSizeOptions(serverPagination?.pageSizeOptions);
58
+ $: resolvedDefaultPageSize = resolveDefaultPageSize(serverPagination);
59
+ $: resolvedTotalRows = serverPagination?.totalRows ?? 0;
60
+ $: resolvedPageSizeLabel = serverPagination?.pageSizeLabel ?? messages.table.pageSizeLabel;
61
+ $: minimumPageSizeOption = Math.min(...resolvedPageSizeOptions);
62
+ $: shouldShowPagination = serverPagination !== undefined && resolvedTotalRows >= minimumPageSizeOption;
63
+
64
+ $: if (mounted && serverPagination && serverPagination.queryKey !== lastQueryKey) {
65
+ lastQueryKey = serverPagination.queryKey;
66
+ void requestPagination({ page: 1, pageSize: currentPagination.pageSize }, true);
67
+ }
68
+
69
+ $: if (mounted && serverPagination) {
70
+ const normalized = normalizePagination(currentPagination, resolvedTotalRows);
71
+ if (resolvedTotalRows === 0 && currentPagination.page !== 1) {
72
+ currentPagination = { ...currentPagination, page: 1 };
73
+ } else if (resolvedTotalRows > 0 && normalized.page !== currentPagination.page) {
74
+ void requestPagination(normalized);
75
+ }
76
+ }
77
+
78
+ $: if (mounted && serverPagination && !resolvedPageSizeOptions.includes(currentPagination.pageSize)) {
79
+ void requestPagination({ page: 1, pageSize: resolvedDefaultPageSize });
80
+ }
81
+
82
+ function normalizePageSizeOptions(options: number[] | undefined): number[] {
83
+ const normalized = [...new Set((options ?? defaultPageSizeOptions).filter((option) => Number.isInteger(option) && option > 0))];
84
+ return normalized.length > 0 ? normalized : [...defaultPageSizeOptions];
56
85
  }
86
+
87
+ function resolveDefaultPageSize(config: DataTableServerPagination | undefined): number {
88
+ const options = normalizePageSizeOptions(config?.pageSizeOptions);
89
+ return config?.defaultPageSize !== undefined && options.includes(config.defaultPageSize)
90
+ ? config.defaultPageSize
91
+ : (options[0] as number);
92
+ }
93
+
94
+ function storageKey(config: DataTableServerPagination): string | undefined {
95
+ const tableId = config.tableId.trim();
96
+ return config.persistPageSize === true && tableId.length > 0
97
+ ? `svelte-ui-utils:data-table:${tableId}:page-size`
98
+ : undefined;
99
+ }
100
+
101
+ function readStoredPageSize(): number | null {
102
+ if (!serverPagination) {
103
+ return null;
104
+ }
105
+ const key = storageKey(serverPagination);
106
+ if (!key) {
107
+ return null;
108
+ }
109
+ try {
110
+ const storedValue = window.localStorage.getItem(key);
111
+ if (storedValue === null) {
112
+ return null;
113
+ }
114
+ const storedPageSize = Number(storedValue);
115
+ return resolvedPageSizeOptions.includes(storedPageSize) ? storedPageSize : null;
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+
121
+ function storePageSize(config: DataTableServerPagination, nextPageSize: number) {
122
+ const key = storageKey(config);
123
+ if (!key || !normalizePageSizeOptions(config.pageSizeOptions).includes(nextPageSize)) {
124
+ return;
125
+ }
126
+ try {
127
+ window.localStorage.setItem(key, String(nextPageSize));
128
+ } catch {
129
+ // localStorage 可能因浏览器隐私策略或存储空间限制而不可用。
130
+ }
131
+ }
132
+
133
+ async function requestPagination(next: PaginationState, force = false) {
134
+ const config = serverPagination;
135
+ if (!config) {
136
+ return;
137
+ }
138
+
139
+ const nextPagination = normalizePagination(next, config.totalRows);
140
+ if (!force && nextPagination.page === currentPagination.page && nextPagination.pageSize === currentPagination.pageSize) {
141
+ return;
142
+ }
143
+
144
+ const previousPagination = currentPagination;
145
+ const sequence = ++requestSequence;
146
+ currentPagination = nextPagination;
147
+ requestPending = true;
148
+
149
+ try {
150
+ await config.onRequest(nextPagination);
151
+ if (sequence === requestSequence) {
152
+ storePageSize(config, nextPagination.pageSize);
153
+ requestPending = false;
154
+ }
155
+ } catch {
156
+ if (sequence === requestSequence) {
157
+ const latestTotalRows = pagination === false ? 0 : pagination.totalRows;
158
+ currentPagination = normalizePagination(previousPagination, latestTotalRows);
159
+ requestPending = false;
160
+ }
161
+ }
162
+ }
163
+
164
+ onMount(() => {
165
+ mounted = true;
166
+ lastQueryKey = pagination === false ? undefined : pagination.queryKey;
167
+ const storedPageSize = readStoredPageSize();
168
+ if (storedPageSize !== null && storedPageSize !== currentPagination.pageSize) {
169
+ void requestPagination({ page: 1, pageSize: storedPageSize });
170
+ }
171
+ });
57
172
  </script>
58
173
 
59
174
  <div class="suu-data-table">
60
175
  {#if shouldShowPagination}
61
176
  <Pagination
62
- {pagination}
177
+ pagination={currentPagination}
63
178
  {language}
64
179
  totalRows={resolvedTotalRows}
65
- {pageSizeOptions}
180
+ pageSizeOptions={resolvedPageSizeOptions}
66
181
  pageSizeLabel={resolvedPageSizeLabel}
67
182
  {maxPageButtons}
183
+ disabled={requestPending}
68
184
  pageSizeDropdownPlacement="down"
69
- onPaginationChange={updatePagination}
185
+ onPaginationChange={requestPagination}
70
186
  />
71
187
  {/if}
72
188
 
@@ -97,14 +213,15 @@
97
213
 
98
214
  {#if shouldShowPagination}
99
215
  <Pagination
100
- {pagination}
216
+ pagination={currentPagination}
101
217
  {language}
102
218
  totalRows={resolvedTotalRows}
103
- {pageSizeOptions}
219
+ pageSizeOptions={resolvedPageSizeOptions}
104
220
  pageSizeLabel={resolvedPageSizeLabel}
105
221
  {maxPageButtons}
222
+ disabled={requestPending}
106
223
  pageSizeDropdownPlacement="up"
107
- onPaginationChange={updatePagination}
224
+ onPaginationChange={requestPagination}
108
225
  />
109
226
  {/if}
110
227
  </div>
@@ -1,5 +1,5 @@
1
1
  import { type UiLanguage } from '../i18n.js';
2
- import type { DataTableColumn, DataTableLayout, DataTableRowAttributes, DataTableRowKey, DataTableSortChangeHandler, PaginationChangeHandler, SortState } from './types.js';
2
+ import type { DataTableColumn, DataTableLayout, DataTableRowAttributes, DataTableRowKey, DataTableSortChangeHandler, DataTableServerPagination, SortState } from './types.js';
3
3
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
4
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
5
  $$bindings?: Bindings;
@@ -22,14 +22,8 @@ declare const DataTable: $$__sveltets_2_IsomorphicComponent<{
22
22
  sort?: SortState | null;
23
23
  onSortChange?: DataTableSortChangeHandler | undefined;
24
24
  showHeader?: boolean;
25
- showPagination?: boolean;
26
- page?: number;
27
- pageSize?: number;
28
- totalRows?: number | undefined;
29
- pageSizeOptions?: number[];
30
- pageSizeLabel?: string | undefined;
25
+ pagination: false | DataTableServerPagination;
31
26
  maxPageButtons?: number;
32
- onPaginationChange?: PaginationChangeHandler | undefined;
33
27
  zebra?: boolean;
34
28
  hoverable?: boolean;
35
29
  bordered?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/DataTable.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EACR,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EAEvB,SAAS,EACV,MAAM,YAAY,CAAC;AAoEtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAUD,QAAA,MAAM,SAAS;WAf2tB,OAAO,EAAE;cAAY,eAAe,EAAE;aAAW,eAAe,GAAG,SAAS;eAAa,UAAU;gBAAc,MAAM,GAAG,SAAS;WAAS,SAAS,GAAG,IAAI;mBAAiB,0BAA0B,GAAG,SAAS;;;;;gBAAsI,MAAM,GAAG,SAAS;sBAAoB,MAAM,EAAE;oBAAkB,MAAM,GAAG,SAAS;;yBAA+D,uBAAuB,GAAG,SAAS;;;;;kBAAgJ,eAAe;;sBAAwD,MAAM,GAAG,SAAS;yBAAuB,MAAM,GAAG,SAAS;;eAAiE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,SAAS;oBAAkB,sBAAsB,GAAG,SAAS;;;;;;;;;;;;;;;cAe3mD,CAAC;AACnF,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/DataTable.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EACR,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,0BAA0B,EAC1B,yBAAyB,EAEzB,SAAS,EACV,MAAM,YAAY,CAAC;AAuLtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAUD,QAAA,MAAM,SAAS;WAf+iB,OAAO,EAAE;cAAY,eAAe,EAAE;aAAW,eAAe,GAAG,SAAS;eAAa,UAAU;gBAAc,MAAM,GAAG,SAAS;WAAS,SAAS,GAAG,IAAI;mBAAiB,0BAA0B,GAAG,SAAS;;gBAA8C,KAAK,GAAG,yBAAyB;;;;;;kBAAwL,eAAe;;sBAAwD,MAAM,GAAG,SAAS;yBAAuB,MAAM,GAAG,SAAS;;eAAiE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,SAAS;oBAAkB,sBAAsB,GAAG,SAAS;;;;;;;;;;;;;;;cAe5vC,CAAC;AACnF,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,eAAe,SAAS,CAAC"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  <script lang="ts">
4
4
  import Dropdown from '../dropdown/Dropdown.svelte';
5
+ import DropdownMultiSelect from '../dropdown/DropdownMultiSelect.svelte';
5
6
  import DropdownSearch from '../dropdown-search/DropdownSearch.svelte';
6
7
  import type { UiLanguage } from '../i18n.js';
7
8
  import DateRangeFilter from './DateRangeFilter.svelte';
@@ -41,24 +42,51 @@
41
42
  </div>
42
43
  {:else if control.type === 'checkbox'}
43
44
  <div class="suu-filter-table__options">
44
- {#each control.options as option}
45
- <label class="suu-filter-table__option" class:suu-filter-table__option--checked={control.value.includes(option.value)}>
46
- <input
47
- class="suu-filter-table__option-input"
48
- type="checkbox"
49
- value={option.value}
50
- disabled={option.disabled}
51
- checked={control.value.includes(option.value)}
52
- on:change={(event) => toggleCheckbox(option.value, (event.currentTarget as HTMLInputElement).checked)}
53
- />
54
- <span class="suu-filter-table__option-label">{option.label}</span>
55
- <span class="suu-filter-table__option-check" aria-hidden="true">
56
- <svg viewBox="0 0 20 20">
57
- <path d="m5 10 3 3 7-7"></path>
58
- </svg>
59
- </span>
60
- </label>
61
- {/each}
45
+ {#if control.optionGroups?.length}
46
+ {#each control.optionGroups as group, groupIndex}
47
+ {#each group.options as option, optionIndex}
48
+ <label
49
+ class="suu-filter-table__option"
50
+ class:suu-filter-table__option--checked={control.value.includes(option.value)}
51
+ class:suu-filter-table__option--group-end={groupIndex < control.optionGroups.length - 1 && optionIndex === group.options.length - 1}
52
+ >
53
+ <input
54
+ class="suu-filter-table__option-input"
55
+ type="checkbox"
56
+ value={option.value}
57
+ disabled={option.disabled}
58
+ checked={control.value.includes(option.value)}
59
+ on:change={(event) => toggleCheckbox(option.value, (event.currentTarget as HTMLInputElement).checked)}
60
+ />
61
+ <span class="suu-filter-table__option-label">{option.label}</span>
62
+ <span class="suu-filter-table__option-check" aria-hidden="true">
63
+ <svg viewBox="0 0 20 20">
64
+ <path d="m5 10 3 3 7-7"></path>
65
+ </svg>
66
+ </span>
67
+ </label>
68
+ {/each}
69
+ {/each}
70
+ {:else}
71
+ {#each control.options as option}
72
+ <label class="suu-filter-table__option" class:suu-filter-table__option--checked={control.value.includes(option.value)}>
73
+ <input
74
+ class="suu-filter-table__option-input"
75
+ type="checkbox"
76
+ value={option.value}
77
+ disabled={option.disabled}
78
+ checked={control.value.includes(option.value)}
79
+ on:change={(event) => toggleCheckbox(option.value, (event.currentTarget as HTMLInputElement).checked)}
80
+ />
81
+ <span class="suu-filter-table__option-label">{option.label}</span>
82
+ <span class="suu-filter-table__option-check" aria-hidden="true">
83
+ <svg viewBox="0 0 20 20">
84
+ <path d="m5 10 3 3 7-7"></path>
85
+ </svg>
86
+ </span>
87
+ </label>
88
+ {/each}
89
+ {/if}
62
90
  </div>
63
91
  {:else if control.type === 'radio'}
64
92
  <div class="suu-filter-table__options">
@@ -191,4 +219,22 @@
191
219
  }
192
220
  }}
193
221
  />
222
+ {:else if control.type === 'dropdownMultiSelect'}
223
+ <DropdownMultiSelect
224
+ value={control.value}
225
+ options={control.options}
226
+ optionGroups={control.optionGroups}
227
+ ariaLabel={control.ariaLabel}
228
+ placement={control.placement}
229
+ menuAlign={control.menuAlign}
230
+ fitViewport={control.fitViewport}
231
+ fitContent={control.fitContent ?? true}
232
+ disabled={control.disabled}
233
+ width={control.width}
234
+ minWidth={control.minWidth}
235
+ maxWidth={control.maxWidth}
236
+ portal={control.portal}
237
+ onChange={control.onChange}
238
+ onTriggerClick={control.onTriggerClick}
239
+ />
194
240
  {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"FilterControl.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/FilterControl.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwIhD,QAAA,MAAM,aAAa,KAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"FilterControl.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/FilterControl.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA2JhD,QAAA,MAAM,aAAa,KAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { CheckboxFilterControl, DateRangeFilterControl, DropdownSearchFilterControl, FilterButtonControl, FilterContainerControl, FilterControl, FilterLinkControl, FilterSelectControl, NumberRangeFilterControl, RadioFilterControl } from './types.js';
1
+ import type { CheckboxFilterControl, DateRangeFilterControl, DropdownMultiSelectFilterControl, DropdownSearchFilterControl, FilterButtonControl, FilterContainerControl, FilterControl, FilterLinkControl, FilterSelectControl, NumberRangeFilterControl, RadioFilterControl } from './types.js';
2
2
  declare function checkbox(options: Omit<CheckboxFilterControl, 'type'>): CheckboxFilterControl;
3
3
  declare function radio(options: Omit<RadioFilterControl, 'type'>): RadioFilterControl;
4
4
  declare function dropdownSearch(options: Omit<DropdownSearchFilterControl, 'type'>): DropdownSearchFilterControl;
@@ -7,6 +7,7 @@ declare function numberRange(options: Omit<NumberRangeFilterControl, 'type'>): N
7
7
  declare function button(options: Omit<FilterButtonControl, 'type'>): FilterButtonControl;
8
8
  declare function link(options: Omit<FilterLinkControl, 'type'>): FilterLinkControl;
9
9
  declare function select(options: Omit<FilterSelectControl, 'type'>): FilterSelectControl;
10
+ declare function dropdownMultiSelect(options: Omit<DropdownMultiSelectFilterControl, 'type'>): DropdownMultiSelectFilterControl;
10
11
  declare function container(controls: FilterControl[]): FilterContainerControl;
11
12
  export declare const filter: {
12
13
  checkbox: typeof checkbox;
@@ -17,6 +18,7 @@ export declare const filter: {
17
18
  button: typeof button;
18
19
  link: typeof link;
19
20
  select: typeof select;
21
+ dropdownMultiSelect: typeof dropdownMultiSelect;
20
22
  container: typeof container;
21
23
  };
22
24
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,iBAAS,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,qBAAqB,CAErF;AAED,iBAAS,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG,kBAAkB,CAE5E;AAED,iBAAS,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,2BAA2B,EAAE,MAAM,CAAC,GAAG,2BAA2B,CAEvG;AAED,iBAAS,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,GAAG,sBAAsB,CAExF;AAED,iBAAS,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,GAAG,wBAAwB,CAE9F;AAED,iBAAS,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,mBAAmB,CAE/E;AAED,iBAAS,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,iBAAiB,CAEzE;AAED,iBAAS,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,mBAAmB,CAE/E;AAED,iBAAS,SAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,sBAAsB,CAEpE;AAED,eAAO,MAAM,MAAM;;;;;;;;;;CAUlB,CAAC"}
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,gCAAgC,EAChC,2BAA2B,EAC3B,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,iBAAS,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,qBAAqB,CAErF;AAED,iBAAS,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG,kBAAkB,CAE5E;AAED,iBAAS,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,2BAA2B,EAAE,MAAM,CAAC,GAAG,2BAA2B,CAEvG;AAED,iBAAS,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,GAAG,sBAAsB,CAExF;AAED,iBAAS,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,GAAG,wBAAwB,CAE9F;AAED,iBAAS,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,mBAAmB,CAE/E;AAED,iBAAS,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,iBAAiB,CAEzE;AAED,iBAAS,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,mBAAmB,CAE/E;AAED,iBAAS,mBAAmB,CAC1B,OAAO,EAAE,IAAI,CAAC,gCAAgC,EAAE,MAAM,CAAC,GACtD,gCAAgC,CAElC;AAED,iBAAS,SAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,sBAAsB,CAEpE;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;CAWlB,CAAC"}
@@ -22,6 +22,9 @@ function link(options) {
22
22
  function select(options) {
23
23
  return { type: 'select', ...options };
24
24
  }
25
+ function dropdownMultiSelect(options) {
26
+ return { type: 'dropdownMultiSelect', ...options };
27
+ }
25
28
  function container(controls) {
26
29
  return { type: 'container', controls };
27
30
  }
@@ -34,5 +37,6 @@ export const filter = {
34
37
  button,
35
38
  link,
36
39
  select,
40
+ dropdownMultiSelect,
37
41
  container
38
42
  };
@@ -8,5 +8,5 @@ export { resolveDateRangePreset } from './date-range.js';
8
8
  export { getUiMessages, resolveUiLanguage, uiLanguages } from '../i18n.js';
9
9
  export { getAriaSort, getCellValue, getPageCount, normalizePagination, setDataTableFilters, setDataTablePageSize, setDataTablePagination, setDataTableSort, toggleSort } from './state.js';
10
10
  export type { UiLanguage, UiMessages } from '../i18n.js';
11
- export type { CheckboxFilterDefinition, CheckboxFilterControl, DataTableColumn, DataTableCellValue, DataTableClassValue, DataTableHorizontalAlign, DataTableLayout, DataTableVerticalAlign, DateRangeFilterDefinition, DateRangeFilterControl, DateRangeFilterValue, DateRangePreset, DataTableRowAttributes, DataTableRowKey, DataTableSortChangeHandler, DataTableState, DataTableStateChangeHandler, DropdownSearchFilterDefinition, DropdownSearchFilterControl, FilterDefinition, FilterButtonControl, FilterContainerControl, FilterControl, FilterLinkControl, FilterOption, FilterSelectControl, FilterState, FilterTableRow, FilterValue, NumberRangeFilterDefinition, NumberRangeFilterControl, NumberRangeFilterValue, PaginationState, PaginationChangeHandler, PaginationDropdownPlacement, RadioFilterDefinition, RadioFilterControl, SortDirection, SortState } from './types.js';
11
+ export type { CheckboxFilterDefinition, CheckboxFilterControl, DataTableColumn, DataTableCellValue, DataTableClassValue, DataTableHorizontalAlign, DataTableLayout, DataTablePaginationQueryKey, DataTableServerPagination, DataTableVerticalAlign, DateRangeFilterDefinition, DateRangeFilterControl, DateRangeFilterValue, DateRangePreset, DataTableRowAttributes, DataTableRowKey, DataTableSortChangeHandler, DataTableState, DataTableStateChangeHandler, DropdownSearchFilterDefinition, DropdownSearchFilterControl, DropdownMultiSelectFilterDefinition, DropdownMultiSelectFilterControl, FilterDefinition, FilterButtonControl, FilterContainerControl, FilterControl, FilterLinkControl, FilterOption, FilterSelectControl, FilterState, FilterTableRow, FilterValue, NumberRangeFilterDefinition, NumberRangeFilterControl, NumberRangeFilterValue, PaginationState, PaginationChangeHandler, PaginationDropdownPlacement, RadioFilterDefinition, RadioFilterControl, SortDirection, SortState } from './types.js';
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,0BAA0B,EAC1B,cAAc,EACd,2BAA2B,EAC3B,8BAA8B,EAC9B,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,WAAW,EACX,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,2BAA2B,EAC3B,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,0BAA0B,EAC1B,cAAc,EACd,2BAA2B,EAC3B,8BAA8B,EAC9B,2BAA2B,EAC3B,mCAAmC,EACnC,gCAAgC,EAChC,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,WAAW,EACX,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC"}