@isoftdata/svelte-table 2.9.5 → 2.10.0-beta.0

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.
package/README.md CHANGED
@@ -1,387 +1,387 @@
1
- # Svelte Table
2
-
3
- For the Pagination and Td components, See Pagination.md and Td.md.
4
-
5
- ![Screenshot of the table component](screenshot.png?raw=true "Screenshot")
6
-
7
- ## Install
8
-
9
- ```sh
10
- pnpm i @isoftdata/svelte-table
11
- ```
12
-
13
- ## Breaking changes
14
-
15
- ### 2.0.0
16
-
17
- - Require Svelte 5
18
- - Slots -> Snippets
19
- - `no-rows` and `footer-row` had to be renamed to `noRows` and `footerRow`
20
- - Removed `footer` snippet/slot
21
- - Events -> Callbacks
22
- - `columnInfo` is now powered by runes instead of a writable store
23
- - Use `columnInfo.current` rather than `$columnInfo` to access its value
24
- - Type is now `ColumnInfoRunicStore` (a class) instead of `ColumnInfoStore`
25
-
26
- ## A Note on Types
27
-
28
- This component uses a generic type `R` to represent the type of a single row passed to the `rows` prop of this table. Then, `R` is extended by another type, `IndexedRow`, which includes the `uuid` and `originalIndex` properties. The original `rows` array you pass in will not be mutated, but the contents of `filteredRows`, `sortedRows`, and `currentPageRows` will be of type `IndexedRow`.
29
-
30
- ## Props
31
-
32
- | Name | Type | Description | Default Value |
33
- | --- | --- | --- | --- |
34
- | bordered | `boolean` | Whether the table is bordered or not. | `true` |
35
- | class | `string` | Any extra classes to apply to the `table`. | `""` |
36
- | columnClickedMethod | `(column: Column, direction: SortDirection) => void` | Called when a column is clicked. Should update `previousSortOrder`, `previousSortDirection`, `previousSortColumn`, and `sortedRows`. | `defaultColumnClicked` |
37
- | columnHidingEnabled | `boolean` | When enabled, the user will be able to right click a column header and hide/show it. | `false` |
38
- | columnResizingEnabled | `boolean` | When enabled, the user will be able to drag on the edge of a column header and resize it. | `false` |
39
- | columns | `Array<Column>` | The definition of the columns. See columns docs below. | **Required** |
40
- | currentPageNumber | `number` | The current page number. | `1` |
41
- | currentPageRows | `Array<IndexedRow>` | It's the current page of rows to show, computed based on `sortedRows`, `currentPageNumber`, and `perPageCount`. **The table component will set this.** | `sortedRows.slice(0, perPageCount)` |
42
- | filter | `string` | The filter string. | `""` |
43
- | filterColumnClass | [`ClassValue`](https://github.com/lukeed/clsx) | The class for the filter column in the header row. | `"col-lg-2 col-md-4 col-sm-6 align-self-end"` |
44
- | filterDisabled | `boolean` | Whether the filter input is disabled or not. | `false` |
45
- | filterEnabled | `boolean` | Whether to show the filter input above the table. | `false` |
46
- | filterLabel | `string` | The label for the filter input. | `"Filter"` |
47
- | filterMethod | `(filter: string, rows: Array<R>, columns: Array<Column>) => Array<IndexedRow>` | A function that filters the rows based on the given filter string. Should return the rows that match the filter, with `uuid` (if not already specified in `rows`) and `originalIndex` properties added. | `defaultFilter` |
48
- | filterPlaceholder | `string` | The `placeholder` text for the filter input. | `"Filter"` |
49
- | filterProps | `Array<string> \| false` | An array of strings that represent the properties to filter on. If `false`, uses all column properties. | `false` |
50
- | filterReadonly | `boolean` | Whether the filter input is readonly or not. | `false` |
51
- | filteredRows | `Array<IndexedRow>` | An array of objects that represent the filtered table rows. You probably don't need to interact with this. | `filterMethod("", rows, columns)` |
52
- | filterLazy | `boolean \| number` | The laziness of the filter input. `true` means the value will update `onchange`, `false` means `oninput`. A millisecond number value indicates the delay from the last UI interaction(debouncing). Consider setting a numeric value if your filter change needs to make a network call. | `false` |
53
- | footers | `Array<FooterValue>` | Values to display in the footer. Calculated automatically by the Table component. | `[]` |
54
- | rowMatchesFilterMethod | `(filter: string, row: R, props: Array<RowProperties<R>>) => boolean` | A function that determines whether the a given row matches the filter. Useful if you want to change how rows are matched by the filter without overriding the whole `filterMethod`. See [Custom Filter Logic](#custom-filter-logic) for more details | `undefined`|
55
- | headerColumnClass | [`ClassValue`](https://github.com/lukeed/clsx) | When the filter is enabled, a `header` slot will be inserted to the area to the left of the filter input. This is the column class for that yield's parent div. | `"col"` |
56
- | headerRowClass | [`ClassValue`](https://github.com/lukeed/clsx) | The class for the table header row. | `"form-row mr-auto"` |
57
- | hideButtonClass | [`ClassValue`](https://github.com/lukeed/clsx) | Any other classes to add the the "hide" dropdown button. | `""` |
58
- | hover | `boolean` | Whether the table will highlight the hovered row. | `true` |
59
- | idProp | `K \| "uuid"` | Property on the objects in the `rows` Array that uniquely identifies the row. Used for lazy sorting. | `"uuid"` |
60
- | lastClickedIdForRange | `number \| IndexedRow[keyof R \| "uuid"] \| undefined` | The last clicked ID for the range selection mode. | `undefined` |
61
- | lazySort | `boolean` | When true, the table will only be resorted when the user clicks on a column header (not in real time as data changes). See idProp. | `false` |
62
- | multiSelectEnabled | `boolean` | Whether to allow selecting multiple rows at a time | `true` |
63
- | localStorageKey | `string \| undefined` | If specified, the localstorage key where user-modified column info such as visibility and resizing are saved | `undefined` |
64
- | parentClass | [`ClassValue`](https://github.com/lukeed/clsx) | CSS class(es) to apply to the `<table>`'s parent div. | `""` |
65
- | parentStyle | `string` | CSS style(s) to apply to the `<table>`'s parent div. | `""` |
66
- | perPageCount | `number` | The number of rows to display per page. Enables pagination if `> 0` | `0` |
67
- | responsive | `boolean` | Whether the table is responsive or not. | `false` |
68
- | rows | `Array<R>` | An array of objects that represent the table rows. | **Required** |
69
- | rowSelectionIdProp | `K \| "uuid"` | The property to use as the unique identifier for each row in the row selection feature. | `"uuid"` |
70
- | rowSelectionRequiresModKey | `boolean` | Whether the row selection feature requires a modifier key or not. | `false` |
71
- | selectionEnabled | `boolean` | Whether the row selection feature is enabled or not. | `false` |
72
- | selectionMode | `"SINGLE" \| "RANGE" \| null` | The selection mode for the row selection feature. When `multiSelectEnabled` is true, both selection modes will allow you to select multiple rows. | `null` |
73
- | showFilterLabel | `boolean` | Whether the filter label is shown or not. | `false` |
74
- | showFooter | `boolean` | Defines if a `<tfoot>` should be shown. See `footers` slot below. | `false` |
75
- | size | `"sm" \| ""` | The size of the table. | `"sm"` |
76
- | sortedRows | `Array<IndexedRow>` | The `rows`, but sorted based on `sortColumn` and `sortDirection`, and optionally `filter`ed(See filter options below). **The table component will set this.** | `filteredRows` |
77
- | sortDirection | `"ASC" \| "DESC"` | The direction of the current sort. | `"ASC"` |
78
- | stickyHeader | `boolean` | Whether the table header will stay at the top when scrolling the table. | `false` |
79
- | striped | `boolean` | Whether the table is striped or not. | `true` |
80
- | tableId | `string` | The unique identifier of the table. | `uuid()` |
81
- | totalItemsCount | `number` | The total number of items in the data set. Necessary for serverside pagination and sorting since the rows array won't have every item for every page. | `0` |
82
- | tree | `boolean` | When true, enables a bunch of tree-specific features. Required if you want to use `TreeRow`s. | `false` |
83
- | columnResizingEnabled | `boolean` | Whether the user is allowed to resizing table columns | `false` |
84
-
85
- > When `columnResizingEnabled` is `true`, any `<Td>` component children will have a css rule of `overflow: hidden;` applied. This makes it a column sized so small that the text doesn't fit causes the text to truncate instead of clipping outside the bounds of the cell. This is intended behavior and how most table's with resizable columns work.
86
-
87
- ## Advanced Props
88
-
89
- You usually won't have to use these, but they're here just in case.
90
-
91
- | Name | Type | Description | Default Value |
92
- | --- | --- | --- | --- |
93
- | previousSortColumn | `Column \| undefined` | The previous sort column. | `undefined` |
94
- | previousSortDirection | `"ASC" \| "DESC" \| undefined` | The previous sort direction. | `undefined` |
95
- | previousSortOrder | `Array<IndexedRow & { order: number }>` | An array of objects that represent the previous sort order. | `[]` |
96
- | selectedRowIds | `Array<string \| number \| R[K]>` | An array of strings, numbers, or objects that represent the selected row IDs. | `[]` |
97
- | sortColumn | `Column \| undefined` | The current sort column. | `undefined` |
98
- | columnInfo | `Writable<Record<string, Column & { visible: boolean }>>` | A store containing column metadata. Mostly used for tracking column visibility. Read-only. | *Handled by the component* |
99
-
100
- ## Columns Definition
101
- An Array of Objects, where the Objects have the following properties:
102
-
103
- |Name | Type | Description|
104
- |--- | --- | ---|
105
- |align | `"left" \| "right" \| "center"` | Direction to align columns (and `Td`s in the column)|
106
- |class | [`ClassValue`](https://github.com/lukeed/clsx) | Sets the `class` of the column header.|
107
- |defaultSortColumn | `boolean` | When Svelte initalizes the view, this is the column that will be sorted by.|
108
- |defaultSortDirection | `"ASC" \| "DESC"` | `'ASC'` or `'DESC'` are the only valid options. Defaults to `'ASC'`|
109
- |ellipsis | `boolean` | Whether to apply `text-overflow: ellipsis` to the column|
110
- |footer | **See Below** | Footer options for this column|
111
- |formatter | `(value: unknown) => unknown` |A formatter function to use on this column. The return value will be shown in the `td`.|
112
- |hideForPrint | `boolean` | Whether to hide this column when printing|
113
- |icon | `string` | The FontAwesome icon classes for the icon you want to display, without the leading `fa-`|
114
- |iconLeft | `string` | If you want the icon to display to the left of the `name`, set this to `true`.|
115
- |iconPrefix | `string` | The FA prefix to apply. Defaults to `fas`.|
116
- |minWidth | `string` | Will be passed in as the value for the CSS `min-width` property on the `<th>` style attribute.|
117
- |name | `string` | The name of the column as it will be displayed in the table column header|
118
- |numeric | `boolean` | When true, right aligns the column (and `Td`s in the column) and applies `font-variant-numeric: tabular-nums;`|
119
- |property | `string` | The property name of the column. Must map to a property in the objects defined in `rows`(see above)|
120
- |sortType | `"ALPHA_NUM" \| "STANDARD" \| false` | How to sort this column. Default is `STANDARD`. `ALPHA_NUM` is better for mixed number and letter columns. `false` will disallow sorting this column.|
121
- |title | `string` | The `th`'s `title`|
122
- |unhidable | `boolean` | Whether to disallow hiding the column.|
123
- |width | `string` | Will be passed in as the value for the CSS `width` property on the `<th>` style attribute.|
124
- |wrap | `boolean` | If `true`, will allow column names to wrap.|
125
-
126
- > The `Column` type is generic. If you pass the type of one of your `rows` to it, then the `property` property will be strongly to properties of that object. This is optional for backwards compatability reasons.
127
-
128
- ![Passing your row type to the Column type allows for better intellisense for column properties.](column-type.png)
129
-
130
- ### `footer` Properties
131
-
132
- Below are the properties of the `footer` object
133
-
134
- | Name | Type | Description |
135
- | --- | --- | --- |
136
- |fn | `"COUNT" \| "AVG" \| "SUM"` | A function to call on each row when calculating that column's footer.|
137
- |altProperty | `string` | If you want to compute footers off a different property than `column.property`, specify it here.|
138
- |formatCurrency | `boolean` | If true, will format the column as currenty.|
139
- |requiredValue | `unknown` | If specified, only column values matching this value will be included in calculations|
140
-
141
- You can also pass a custom callback function to `footer.fn`, which changes some of the property types as shown below. The properties that are not repeated below behave the same whether you use a built-in or custom reducer `fn`.
142
-
143
- | Name | Type | Description |
144
- | --- | --- | --- |
145
- | fn | `(previousValue: FooterReducerValue<R>, currentValue: FooterReducerValue<R>) => FooterReducerValue<R>` | A function to call on each row when calculating that column's footer. `currentValue` is the value of `row[property]` or `row[altProperty]` if specified. |
146
- | initialValue | `FooterReducerValue<R>` | The initial value of the reducer function. |
147
-
148
- `FooterReducerValue` is a union of `string`, `number`, and the property types of the row object
149
-
150
- ## Snippets
151
-
152
- | Name | Args Type | Description|
153
- | --- | --- | --- |
154
- | children | `[{ row: R & { originalIndex: number; uuid: string }; index: number; visibleColumnsCount: number }]` | This represents **one** row of the table |
155
- | header | `[]` | Only available if `filterEnabled` is true. This yields inside the first column of a `<div class="form-row"></div>` above the table. The 2nd column contains the filter input itself. |
156
- | noRows | `[{ visibleColumnsCount: number }]` | Shown when `rows.length === 0` |
157
- | body | `[{ rows: Array<R & { originalIndex: number; uuid: string }> }]` | If you want to specify all of the contents of the `tbody` yourself, you can do so here. Specifying anything for this slot will prevent the "default" and "noRows" slot from being rendered. |
158
- | footerRow | `[{ footers: Array<FooterValue> }]` | If `showFooter` is enabled, This is the `tr` where the footer values are shown. |
159
-
160
- ## Callbacks
161
- * pageChange - Fired when the page changes. `({ pageNumber: number }) => void`
162
-
163
- ## Methods
164
-
165
- There are a couple methods you can access by calling them on the Table component instance, which you can access using [bind:this](https://svelte.dev/docs/component-directives#bind-this).
166
-
167
- * `rowClick(row: IndexedRow): void` - handles the logic for clicking on and selecting rows
168
- * `setPageVisibleByItemId({ id, keyName }: { id: number | string; keyName: K })` - Given an item's `id`, and a `keyName`, sets the current page to be the one containing that item
169
- * `expandRow(rowId: (typeof selectedRowIds)[0], expanded = true)` - Tree mode only. Expands a row given its id.
170
- * `setColumnVisibility(columnProperties: Array<string> | string, visible: boolean)` - Sets the visibility of the specified column(s)
171
- * `setColumnVisibilityWatch(columnProperties: Array<string> | string, getVisible: () => boolean)` - Sets the visibility of the specified column(s) whenever the value of `getVisible` changes. Call this in `onMount` or an effect.
172
-
173
- ## Context
174
-
175
- A handful of values are set as context of the component so they're accessible to their children.
176
-
177
- * selectedRowIds - same as listed under Props
178
- * columnInfo - Map of column properties to column definitions (`Record<string, Column & { visible: boolean }>`)
179
-
180
- ## Examples
181
-
182
- There are three ways to use this component, with the difference lying in which slots are specified, which affects how table body is rendered. They are listed in order from "most automatic" to "most manual".
183
-
184
- ### Method 1 : "Full Auto" (No Default or "body" slots)
185
-
186
- For this method, all you have to do is specify the `rows` and `columns` props on the `Table`, and *do not specify anything for the default slot*. For each column in each row, the table component will render a `td`, getting the value from that column's `property` prop, applying any `formatter` function if specified for that column.
187
-
188
- This is the simplest method, and will work for most use cases that don't need to insert any extra HTML into the `td`s.
189
-
190
- ```html
191
- <Table
192
- {rows}
193
- {columns}
194
- />
195
- ```
196
-
197
- You can still specify other slots, for example, "no-rows" and "header", just not the default slot.
198
-
199
- ```svelte
200
- <Table
201
- {rows}
202
- {columns}
203
- >
204
- {#snippet noRows({ visibleColumnsCount })}
205
- <tr>
206
- <td
207
- class="text-center"
208
- colspan={visibleColumnsCount}>No rows here bro
209
- </td>
210
- </tr>
211
- {/snippet}
212
- {#snippet header()}
213
- <Input
214
- label="Items per page"
215
- class="mb-3"
216
- type="number"
217
- bind:value={perPageCount}
218
- />
219
- {/snippet}
220
- </Table>
221
- ```
222
-
223
- ### Method 2 : "Semi Auto" (Default Slot)
224
-
225
- For this method, you specify a single row's contents in the default slot, and access each row's value by adding `let:row` to the `Table`. These rows come from `currentPageRows`, so they've already been filtered, sorted, and paginated.
226
-
227
- With this method, you can insert extra HTML into any of the `td`/`Td`s, but you have to specify them all yourself.
228
-
229
- As above, you can still specify other slots when using this method.
230
-
231
- ```svelte
232
- <Table
233
- {rows}
234
- {columns}
235
- >
236
- {#snippet children({ row })}
237
- <tr>
238
- <Td property="id">
239
- {row.id}
240
- </Td>
241
- <Td property="name">
242
- {row.name}
243
- </Td>
244
- <Td property="email">
245
- {row.email}
246
- </Td>
247
- <Td property="deleted">
248
- <Button color="danger" icon="trash">Delete<Button>
249
- </Td>
250
- </tr>
251
- {/snippet}
252
- </Table>
253
- ```
254
-
255
- ### Method 3 : "Full Manual" ("body" Slot)
256
-
257
- With this method, you put anything you want to render in the `tbody` into the "body" snippet. You can access the `currentPageRows` with the `rows` property of the snippet, and the # of visible columns with `visibleColumnsCount`, as shown below.
258
-
259
- This method is the most similar to how our Ractive table component works, and requires you to specify the whole contents of the `tbody`.
260
-
261
- The contents of the "children" and "noRows" snippets will be ignored when using this method, but the "header" snippet will still work.
262
-
263
- ```svelte
264
- <Table
265
- {rows}
266
- {columns}
267
- >
268
- {#snippet body({ rows, visibleColumnsCount })}
269
- {#each rows as row}
270
- <tr>
271
- <Td property="id">
272
- {row.id}
273
- </Td>
274
- <Td property="name">
275
- {row.name}
276
- </Td>
277
- <Td property="email">
278
- {row.email}
279
- </Td>
280
- <Td property="deleted">
281
- <Button color="danger" icon="trash">Delete<Button>
282
- </Td>
283
- </tr>
284
- {:else}
285
- <tr colspan={visibleColumnsCount}>No Rows.</td>
286
- {/each}
287
- {/snippet}
288
- </Table>
289
- ```
290
-
291
- ### Bonus Method: As a Tree Control
292
-
293
- See [TreeRow](./TreeRow.md).
294
-
295
- ## Extending
296
-
297
- Guides on how to extend the Table component for various common use cases
298
-
299
- ### Using the Table with server-side pagination/sorting/filtering
300
-
301
- > There is a fully-functioning demo at `/serverside` on the demo page.
302
-
303
- To successfully use the Table component in a context where the server/API is handling the sorting/pagination/filtering, you'll want to ensure you do the following:
304
- 1. Pass the `sortDirection`, `sortColumn`, `perPageCount`, `currentPageNumber`, & `totalItemsCount` [props](#props).
305
- 2. Set the `filterEnabled` prop to `false`. Alternatively, if your backend API has the ability to filter by string input, you can leave it `true` and pass an `async` function to `filterMethod` prop that either sets the `rows` or performs a state change with the now-filtered `rows`.
306
- 3. Pass a `pageChange` [callback](#callbacks).
307
- 4. Pass a `columnClickedMethod` function. This will be called whenever the user clicks on a column.
308
- 5. Make sure you pass `false` for the `sortType` property on the [columns definition](#columns-definition) for any columns which lack sorting support by your backend API.
309
- 6. Don't use the `let:rows` method of handling the rows. Instead, use the "[Full Manual](#method-3--full-manual-body-slot)" option without a `let:rows` declaration. Example:
310
-
311
- ```html
312
- <Table
313
- {rows}
314
- {columns}
315
- sortDirection={orderByDirection}
316
- sortColumn={columns.find(column => column.property === orderByColumn)}
317
- perPageCount={pageSize}
318
- {currentPageNumber}
319
- {totalItemsCount}
320
- >
321
- {#snippet body({rows})}
322
- {#each rows as row}
323
- <tr>
324
- <!-- your Tds here -->
325
- </tr>
326
- {/each}
327
- {/snippet}
328
- </Table
329
- ```
330
-
331
- ### Custom Filter Logic
332
-
333
- Various ways to change how the table filters rows, ordered by complexity.
334
-
335
- #### `filterProps`
336
-
337
- By default, the table will check the filter against every `property` in the `columns` array. If you want to match against a different set of properties, you can specify them with the `filterProps` prop.
338
-
339
- ```html
340
- <Table
341
- {rows}
342
- {columns}
343
- filterProps={['foo', 'bar']}
344
- ></Table>
345
- ```
346
-
347
- #### `rowMatchesFilterMethod`
348
-
349
- By default, the table will show any rows whose `filterProps` (or column properties) contain a substring of the filter string. If you want to override that logic, you can pass a `rowMatchesFilterMethod`.
350
-
351
- In simple cases, you can just use the default method, and add your own logic as well:
352
-
353
- ```ts
354
- function rowMatchesFilterMethod(filter: string, row: R, props: Array<RowProperties<R>>) {
355
- return table?.defaultRowMatchesFilter(...args) || someOtherLogic()
356
- }
357
- ```
358
-
359
- But you can use all your own logic too.
360
-
361
- ```ts
362
- function rowMatchesFilterMethod(filter: string, row: R, props: Array<RowProperties<R>>) {
363
- return props.some(prop => {
364
- // ...
365
- })
366
- }
367
- ```
368
-
369
- #### `filterMethod`
370
-
371
- If you really need to override some larger part of the filter logic, you can pass the `filterMethod` prop. This isn't recommended and is mostly a holdover from the original version of the component, but you can use it if you want to.
372
-
373
- ### Column Resizing
374
-
375
- Enable the `columnResizingEnabled` prop, and pass the `localStorageKey` prop if you want to save the column sizes (and whether it's shown/hidden) to localstorage. These values are also stored in the `columnInfo` store if you want to do something else with it.
376
-
377
- ## See Also
378
-
379
- ### TreeRow
380
- To use this component as a tree control, add the `tree` prop to your `Table`, use the [TreeRow Component](./TreeRow.md).
381
-
382
- ### Td
383
-
384
- You can use the [Td Component](./Td.md) in your table to support hiding columns.
385
-
386
- ### Pagination
387
- The table component will handle the [Pagination Component](./Pagination.md) for you.
1
+ # Svelte Table
2
+
3
+ For the Pagination and Td components, See Pagination.md and Td.md.
4
+
5
+ ![Screenshot of the table component](screenshot.png?raw=true "Screenshot")
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm i @isoftdata/svelte-table
11
+ ```
12
+
13
+ ## Breaking changes
14
+
15
+ ### 2.0.0
16
+
17
+ - Require Svelte 5
18
+ - Slots -> Snippets
19
+ - `no-rows` and `footer-row` had to be renamed to `noRows` and `footerRow`
20
+ - Removed `footer` snippet/slot
21
+ - Events -> Callbacks
22
+ - `columnInfo` is now powered by runes instead of a writable store
23
+ - Use `columnInfo.current` rather than `$columnInfo` to access its value
24
+ - Type is now `ColumnInfoRunicStore` (a class) instead of `ColumnInfoStore`
25
+
26
+ ## A Note on Types
27
+
28
+ This component uses a generic type `R` to represent the type of a single row passed to the `rows` prop of this table. Then, `R` is extended by another type, `IndexedRow`, which includes the `uuid` and `originalIndex` properties. The original `rows` array you pass in will not be mutated, but the contents of `filteredRows`, `sortedRows`, and `currentPageRows` will be of type `IndexedRow`.
29
+
30
+ ## Props
31
+
32
+ | Name | Type | Description | Default Value |
33
+ | --- | --- | --- | --- |
34
+ | bordered | `boolean` | Whether the table is bordered or not. | `true` |
35
+ | class | `string` | Any extra classes to apply to the `table`. | `""` |
36
+ | columnClickedMethod | `(column: Column, direction: SortDirection) => void` | Called when a column is clicked. Should update `previousSortOrder`, `previousSortDirection`, `previousSortColumn`, and `sortedRows`. | `defaultColumnClicked` |
37
+ | columnHidingEnabled | `boolean` | When enabled, the user will be able to right click a column header and hide/show it. | `false` |
38
+ | columnResizingEnabled | `boolean` | When enabled, the user will be able to drag on the edge of a column header and resize it. | `false` |
39
+ | columns | `Array<Column>` | The definition of the columns. See columns docs below. | **Required** |
40
+ | currentPageNumber | `number` | The current page number. | `1` |
41
+ | currentPageRows | `Array<IndexedRow>` | It's the current page of rows to show, computed based on `sortedRows`, `currentPageNumber`, and `perPageCount`. **The table component will set this.** | `sortedRows.slice(0, perPageCount)` |
42
+ | filter | `string` | The filter string. | `""` |
43
+ | filterColumnClass | [`ClassValue`](https://github.com/lukeed/clsx) | The class for the filter column in the header row. | `"col-lg-2 col-md-4 col-sm-6 align-self-end"` |
44
+ | filterDisabled | `boolean` | Whether the filter input is disabled or not. | `false` |
45
+ | filterEnabled | `boolean` | Whether to show the filter input above the table. | `false` |
46
+ | filterLabel | `string` | The label for the filter input. | `"Filter"` |
47
+ | filterMethod | `(filter: string, rows: Array<R>, columns: Array<Column>) => Array<IndexedRow>` | A function that filters the rows based on the given filter string. Should return the rows that match the filter, with `uuid` (if not already specified in `rows`) and `originalIndex` properties added. | `defaultFilter` |
48
+ | filterPlaceholder | `string` | The `placeholder` text for the filter input. | `"Filter"` |
49
+ | filterProps | `Array<string> \| false` | An array of strings that represent the properties to filter on. If `false`, uses all column properties. | `false` |
50
+ | filterReadonly | `boolean` | Whether the filter input is readonly or not. | `false` |
51
+ | filteredRows | `Array<IndexedRow>` | An array of objects that represent the filtered table rows. You probably don't need to interact with this. | `filterMethod("", rows, columns)` |
52
+ | filterLazy | `boolean \| number` | The laziness of the filter input. `true` means the value will update `onchange`, `false` means `oninput`. A millisecond number value indicates the delay from the last UI interaction(debouncing). Consider setting a numeric value if your filter change needs to make a network call. | `false` |
53
+ | footers | `Array<FooterValue>` | Values to display in the footer. Calculated automatically by the Table component. | `[]` |
54
+ | rowMatchesFilterMethod | `(filter: string, row: R, props: Array<RowProperties<R>>) => boolean` | A function that determines whether the a given row matches the filter. Useful if you want to change how rows are matched by the filter without overriding the whole `filterMethod`. See [Custom Filter Logic](#custom-filter-logic) for more details | `undefined`|
55
+ | headerColumnClass | [`ClassValue`](https://github.com/lukeed/clsx) | When the filter is enabled, a `header` slot will be inserted to the area to the left of the filter input. This is the column class for that yield's parent div. | `"col"` |
56
+ | headerRowClass | [`ClassValue`](https://github.com/lukeed/clsx) | The class for the table header row. | `"form-row mr-auto"` |
57
+ | hideButtonClass | [`ClassValue`](https://github.com/lukeed/clsx) | Any other classes to add the the "hide" dropdown button. | `""` |
58
+ | hover | `boolean` | Whether the table will highlight the hovered row. | `true` |
59
+ | idProp | `K \| "uuid"` | Property on the objects in the `rows` Array that uniquely identifies the row. Used for lazy sorting. | `"uuid"` |
60
+ | lastClickedIdForRange | `number \| IndexedRow[keyof R \| "uuid"] \| undefined` | The last clicked ID for the range selection mode. | `undefined` |
61
+ | lazySort | `boolean` | When true, the table will only be resorted when the user clicks on a column header (not in real time as data changes). See idProp. | `false` |
62
+ | multiSelectEnabled | `boolean` | Whether to allow selecting multiple rows at a time | `true` |
63
+ | localStorageKey | `string \| undefined` | If specified, the localstorage key where user-modified column info such as visibility and resizing are saved | `undefined` |
64
+ | parentClass | [`ClassValue`](https://github.com/lukeed/clsx) | CSS class(es) to apply to the `<table>`'s parent div. | `""` |
65
+ | parentStyle | `string` | CSS style(s) to apply to the `<table>`'s parent div. | `""` |
66
+ | perPageCount | `number` | The number of rows to display per page. Enables pagination if `> 0` | `0` |
67
+ | responsive | `boolean` | Whether the table is responsive or not. | `false` |
68
+ | rows | `Array<R>` | An array of objects that represent the table rows. | **Required** |
69
+ | rowSelectionIdProp | `K \| "uuid"` | The property to use as the unique identifier for each row in the row selection feature. | `"uuid"` |
70
+ | rowSelectionRequiresModKey | `boolean` | Whether the row selection feature requires a modifier key or not. | `false` |
71
+ | selectionEnabled | `boolean` | Whether the row selection feature is enabled or not. | `false` |
72
+ | selectionMode | `"SINGLE" \| "RANGE" \| null` | The selection mode for the row selection feature. When `multiSelectEnabled` is true, both selection modes will allow you to select multiple rows. | `null` |
73
+ | showFilterLabel | `boolean` | Whether the filter label is shown or not. | `false` |
74
+ | showFooter | `boolean` | Defines if a `<tfoot>` should be shown. See `footers` slot below. | `false` |
75
+ | size | `"sm" \| ""` | The size of the table. | `"sm"` |
76
+ | sortedRows | `Array<IndexedRow>` | The `rows`, but sorted based on `sortColumn` and `sortDirection`, and optionally `filter`ed(See filter options below). **The table component will set this.** | `filteredRows` |
77
+ | sortDirection | `"ASC" \| "DESC"` | The direction of the current sort. | `"ASC"` |
78
+ | stickyHeader | `boolean` | Whether the table header will stay at the top when scrolling the table. | `false` |
79
+ | striped | `boolean` | Whether the table is striped or not. | `true` |
80
+ | tableId | `string` | The unique identifier of the table. | `uuid()` |
81
+ | totalItemsCount | `number` | The total number of items in the data set. Necessary for serverside pagination and sorting since the rows array won't have every item for every page. | `0` |
82
+ | tree | `boolean` | When true, enables a bunch of tree-specific features. Required if you want to use `TreeRow`s. | `false` |
83
+ | columnResizingEnabled | `boolean` | Whether the user is allowed to resizing table columns | `false` |
84
+
85
+ > When `columnResizingEnabled` is `true`, any `<Td>` component children will have a css rule of `overflow: hidden;` applied. This makes it a column sized so small that the text doesn't fit causes the text to truncate instead of clipping outside the bounds of the cell. This is intended behavior and how most table's with resizable columns work.
86
+
87
+ ## Advanced Props
88
+
89
+ You usually won't have to use these, but they're here just in case.
90
+
91
+ | Name | Type | Description | Default Value |
92
+ | --- | --- | --- | --- |
93
+ | previousSortColumn | `Column \| undefined` | The previous sort column. | `undefined` |
94
+ | previousSortDirection | `"ASC" \| "DESC" \| undefined` | The previous sort direction. | `undefined` |
95
+ | previousSortOrder | `Array<IndexedRow & { order: number }>` | An array of objects that represent the previous sort order. | `[]` |
96
+ | selectedRowIds | `Array<string \| number \| R[K]>` | An array of strings, numbers, or objects that represent the selected row IDs. | `[]` |
97
+ | sortColumn | `Column \| undefined` | The current sort column. | `undefined` |
98
+ | columnInfo | `Writable<Record<string, Column & { visible: boolean }>>` | A store containing column metadata. Mostly used for tracking column visibility. Read-only. | *Handled by the component* |
99
+
100
+ ## Columns Definition
101
+ An Array of Objects, where the Objects have the following properties:
102
+
103
+ |Name | Type | Description|
104
+ |--- | --- | ---|
105
+ |align | `"left" \| "right" \| "center"` | Direction to align columns (and `Td`s in the column)|
106
+ |class | [`ClassValue`](https://github.com/lukeed/clsx) | Sets the `class` of the column header.|
107
+ |defaultSortColumn | `boolean` | When Svelte initalizes the view, this is the column that will be sorted by.|
108
+ |defaultSortDirection | `"ASC" \| "DESC"` | `'ASC'` or `'DESC'` are the only valid options. Defaults to `'ASC'`|
109
+ |ellipsis | `boolean` | Whether to apply `text-overflow: ellipsis` to the column|
110
+ |footer | **See Below** | Footer options for this column|
111
+ |formatter | `(value: unknown) => unknown` |A formatter function to use on this column. The return value will be shown in the `td`.|
112
+ |hideForPrint | `boolean` | Whether to hide this column when printing|
113
+ |icon | `string` | The FontAwesome icon classes for the icon you want to display, without the leading `fa-`|
114
+ |iconLeft | `string` | If you want the icon to display to the left of the `name`, set this to `true`.|
115
+ |iconPrefix | `string` | The FA prefix to apply. Defaults to `fas`.|
116
+ |minWidth | `string` | Will be passed in as the value for the CSS `min-width` property on the `<th>` style attribute.|
117
+ |name | `string` | The name of the column as it will be displayed in the table column header|
118
+ |numeric | `boolean` | When true, right aligns the column (and `Td`s in the column) and applies `font-variant-numeric: tabular-nums;`|
119
+ |property | `string` | The property name of the column. Must map to a property in the objects defined in `rows`(see above)|
120
+ |sortType | `"ALPHA_NUM" \| "STANDARD" \| false` | How to sort this column. Default is `STANDARD`. `ALPHA_NUM` is better for mixed number and letter columns. `false` will disallow sorting this column.|
121
+ |title | `string` | The `th`'s `title`|
122
+ |unhidable | `boolean` | Whether to disallow hiding the column.|
123
+ |width | `string` | Will be passed in as the value for the CSS `width` property on the `<th>` style attribute.|
124
+ |wrap | `boolean` | If `true`, will allow column names to wrap.|
125
+
126
+ > The `Column` type is generic. If you pass the type of one of your `rows` to it, then the `property` property will be strongly to properties of that object. This is optional for backwards compatability reasons.
127
+
128
+ ![Passing your row type to the Column type allows for better intellisense for column properties.](column-type.png)
129
+
130
+ ### `footer` Properties
131
+
132
+ Below are the properties of the `footer` object
133
+
134
+ | Name | Type | Description |
135
+ | --- | --- | --- |
136
+ |fn | `"COUNT" \| "AVG" \| "SUM"` | A function to call on each row when calculating that column's footer.|
137
+ |altProperty | `string` | If you want to compute footers off a different property than `column.property`, specify it here.|
138
+ |formatCurrency | `boolean` | If true, will format the column as currenty.|
139
+ |requiredValue | `unknown` | If specified, only column values matching this value will be included in calculations|
140
+
141
+ You can also pass a custom callback function to `footer.fn`, which changes some of the property types as shown below. The properties that are not repeated below behave the same whether you use a built-in or custom reducer `fn`.
142
+
143
+ | Name | Type | Description |
144
+ | --- | --- | --- |
145
+ | fn | `(previousValue: FooterReducerValue<R>, currentValue: FooterReducerValue<R>) => FooterReducerValue<R>` | A function to call on each row when calculating that column's footer. `currentValue` is the value of `row[property]` or `row[altProperty]` if specified. |
146
+ | initialValue | `FooterReducerValue<R>` | The initial value of the reducer function. |
147
+
148
+ `FooterReducerValue` is a union of `string`, `number`, and the property types of the row object
149
+
150
+ ## Snippets
151
+
152
+ | Name | Args Type | Description|
153
+ | --- | --- | --- |
154
+ | children | `[{ row: R & { originalIndex: number; uuid: string }; index: number; visibleColumnsCount: number }]` | This represents **one** row of the table |
155
+ | header | `[]` | Only available if `filterEnabled` is true. This yields inside the first column of a `<div class="form-row"></div>` above the table. The 2nd column contains the filter input itself. |
156
+ | noRows | `[{ visibleColumnsCount: number }]` | Shown when `rows.length === 0` |
157
+ | body | `[{ rows: Array<R & { originalIndex: number; uuid: string }> }]` | If you want to specify all of the contents of the `tbody` yourself, you can do so here. Specifying anything for this slot will prevent the "default" and "noRows" slot from being rendered. |
158
+ | footerRow | `[{ footers: Array<FooterValue> }]` | If `showFooter` is enabled, This is the `tr` where the footer values are shown. |
159
+
160
+ ## Callbacks
161
+ * pageChange - Fired when the page changes. `({ pageNumber: number }) => void`
162
+
163
+ ## Methods
164
+
165
+ There are a couple methods you can access by calling them on the Table component instance, which you can access using [bind:this](https://svelte.dev/docs/component-directives#bind-this).
166
+
167
+ * `rowClick(row: IndexedRow): void` - handles the logic for clicking on and selecting rows
168
+ * `setPageVisibleByItemId({ id, keyName }: { id: number | string; keyName: K })` - Given an item's `id`, and a `keyName`, sets the current page to be the one containing that item
169
+ * `expandRow(rowId: (typeof selectedRowIds)[0], expanded = true)` - Tree mode only. Expands a row given its id.
170
+ * `setColumnVisibility(columnProperties: Array<string> | string, visible: boolean)` - Sets the visibility of the specified column(s)
171
+ * `setColumnVisibilityWatch(columnProperties: Array<string> | string, getVisible: () => boolean)` - Sets the visibility of the specified column(s) whenever the value of `getVisible` changes. Call this in `onMount` or an effect.
172
+
173
+ ## Context
174
+
175
+ A handful of values are set as context of the component so they're accessible to their children.
176
+
177
+ * selectedRowIds - same as listed under Props
178
+ * columnInfo - Map of column properties to column definitions (`Record<string, Column & { visible: boolean }>`)
179
+
180
+ ## Examples
181
+
182
+ There are three ways to use this component, with the difference lying in which slots are specified, which affects how table body is rendered. They are listed in order from "most automatic" to "most manual".
183
+
184
+ ### Method 1 : "Full Auto" (No Default or "body" slots)
185
+
186
+ For this method, all you have to do is specify the `rows` and `columns` props on the `Table`, and *do not specify anything for the default slot*. For each column in each row, the table component will render a `td`, getting the value from that column's `property` prop, applying any `formatter` function if specified for that column.
187
+
188
+ This is the simplest method, and will work for most use cases that don't need to insert any extra HTML into the `td`s.
189
+
190
+ ```html
191
+ <Table
192
+ {rows}
193
+ {columns}
194
+ />
195
+ ```
196
+
197
+ You can still specify other slots, for example, "no-rows" and "header", just not the default slot.
198
+
199
+ ```svelte
200
+ <Table
201
+ {rows}
202
+ {columns}
203
+ >
204
+ {#snippet noRows({ visibleColumnsCount })}
205
+ <tr>
206
+ <td
207
+ class="text-center"
208
+ colspan={visibleColumnsCount}>No rows here bro
209
+ </td>
210
+ </tr>
211
+ {/snippet}
212
+ {#snippet header()}
213
+ <Input
214
+ label="Items per page"
215
+ class="mb-3"
216
+ type="number"
217
+ bind:value={perPageCount}
218
+ />
219
+ {/snippet}
220
+ </Table>
221
+ ```
222
+
223
+ ### Method 2 : "Semi Auto" (Default Slot)
224
+
225
+ For this method, you specify a single row's contents in the default slot, and access each row's value by adding `let:row` to the `Table`. These rows come from `currentPageRows`, so they've already been filtered, sorted, and paginated.
226
+
227
+ With this method, you can insert extra HTML into any of the `td`/`Td`s, but you have to specify them all yourself.
228
+
229
+ As above, you can still specify other slots when using this method.
230
+
231
+ ```svelte
232
+ <Table
233
+ {rows}
234
+ {columns}
235
+ >
236
+ {#snippet children({ row })}
237
+ <tr>
238
+ <Td property="id">
239
+ {row.id}
240
+ </Td>
241
+ <Td property="name">
242
+ {row.name}
243
+ </Td>
244
+ <Td property="email">
245
+ {row.email}
246
+ </Td>
247
+ <Td property="deleted">
248
+ <Button color="danger" icon="trash">Delete<Button>
249
+ </Td>
250
+ </tr>
251
+ {/snippet}
252
+ </Table>
253
+ ```
254
+
255
+ ### Method 3 : "Full Manual" ("body" Slot)
256
+
257
+ With this method, you put anything you want to render in the `tbody` into the "body" snippet. You can access the `currentPageRows` with the `rows` property of the snippet, and the # of visible columns with `visibleColumnsCount`, as shown below.
258
+
259
+ This method is the most similar to how our Ractive table component works, and requires you to specify the whole contents of the `tbody`.
260
+
261
+ The contents of the "children" and "noRows" snippets will be ignored when using this method, but the "header" snippet will still work.
262
+
263
+ ```svelte
264
+ <Table
265
+ {rows}
266
+ {columns}
267
+ >
268
+ {#snippet body({ rows, visibleColumnsCount })}
269
+ {#each rows as row}
270
+ <tr>
271
+ <Td property="id">
272
+ {row.id}
273
+ </Td>
274
+ <Td property="name">
275
+ {row.name}
276
+ </Td>
277
+ <Td property="email">
278
+ {row.email}
279
+ </Td>
280
+ <Td property="deleted">
281
+ <Button color="danger" icon="trash">Delete<Button>
282
+ </Td>
283
+ </tr>
284
+ {:else}
285
+ <tr colspan={visibleColumnsCount}>No Rows.</td>
286
+ {/each}
287
+ {/snippet}
288
+ </Table>
289
+ ```
290
+
291
+ ### Bonus Method: As a Tree Control
292
+
293
+ See [TreeRow](./TreeRow.md).
294
+
295
+ ## Extending
296
+
297
+ Guides on how to extend the Table component for various common use cases
298
+
299
+ ### Using the Table with server-side pagination/sorting/filtering
300
+
301
+ > There is a fully-functioning demo at `/serverside` on the demo page.
302
+
303
+ To successfully use the Table component in a context where the server/API is handling the sorting/pagination/filtering, you'll want to ensure you do the following:
304
+ 1. Pass the `sortDirection`, `sortColumn`, `perPageCount`, `currentPageNumber`, & `totalItemsCount` [props](#props).
305
+ 2. Set the `filterEnabled` prop to `false`. Alternatively, if your backend API has the ability to filter by string input, you can leave it `true` and pass an `async` function to `filterMethod` prop that either sets the `rows` or performs a state change with the now-filtered `rows`.
306
+ 3. Pass a `pageChange` [callback](#callbacks).
307
+ 4. Pass a `columnClickedMethod` function. This will be called whenever the user clicks on a column.
308
+ 5. Make sure you pass `false` for the `sortType` property on the [columns definition](#columns-definition) for any columns which lack sorting support by your backend API.
309
+ 6. Don't use the `let:rows` method of handling the rows. Instead, use the "[Full Manual](#method-3--full-manual-body-slot)" option without a `let:rows` declaration. Example:
310
+
311
+ ```html
312
+ <Table
313
+ {rows}
314
+ {columns}
315
+ sortDirection={orderByDirection}
316
+ sortColumn={columns.find(column => column.property === orderByColumn)}
317
+ perPageCount={pageSize}
318
+ {currentPageNumber}
319
+ {totalItemsCount}
320
+ >
321
+ {#snippet body({rows})}
322
+ {#each rows as row}
323
+ <tr>
324
+ <!-- your Tds here -->
325
+ </tr>
326
+ {/each}
327
+ {/snippet}
328
+ </Table
329
+ ```
330
+
331
+ ### Custom Filter Logic
332
+
333
+ Various ways to change how the table filters rows, ordered by complexity.
334
+
335
+ #### `filterProps`
336
+
337
+ By default, the table will check the filter against every `property` in the `columns` array. If you want to match against a different set of properties, you can specify them with the `filterProps` prop.
338
+
339
+ ```html
340
+ <Table
341
+ {rows}
342
+ {columns}
343
+ filterProps={['foo', 'bar']}
344
+ ></Table>
345
+ ```
346
+
347
+ #### `rowMatchesFilterMethod`
348
+
349
+ By default, the table will show any rows whose `filterProps` (or column properties) contain a substring of the filter string. If you want to override that logic, you can pass a `rowMatchesFilterMethod`.
350
+
351
+ In simple cases, you can just use the default method, and add your own logic as well:
352
+
353
+ ```ts
354
+ function rowMatchesFilterMethod(filter: string, row: R, props: Array<RowProperties<R>>) {
355
+ return table?.defaultRowMatchesFilter(...args) || someOtherLogic()
356
+ }
357
+ ```
358
+
359
+ But you can use all your own logic too.
360
+
361
+ ```ts
362
+ function rowMatchesFilterMethod(filter: string, row: R, props: Array<RowProperties<R>>) {
363
+ return props.some(prop => {
364
+ // ...
365
+ })
366
+ }
367
+ ```
368
+
369
+ #### `filterMethod`
370
+
371
+ If you really need to override some larger part of the filter logic, you can pass the `filterMethod` prop. This isn't recommended and is mostly a holdover from the original version of the component, but you can use it if you want to.
372
+
373
+ ### Column Resizing
374
+
375
+ Enable the `columnResizingEnabled` prop, and pass the `localStorageKey` prop if you want to save the column sizes (and whether it's shown/hidden) to localstorage. These values are also stored in the `columnInfo` store if you want to do something else with it.
376
+
377
+ ## See Also
378
+
379
+ ### TreeRow
380
+ To use this component as a tree control, add the `tree` prop to your `Table`, use the [TreeRow Component](./TreeRow.md).
381
+
382
+ ### Td
383
+
384
+ You can use the [Td Component](./Td.md) in your table to support hiding columns.
385
+
386
+ ### Pagination
387
+ The table component will handle the [Pagination Component](./Pagination.md) for you.