@ibobbyts/svelte-ui-utils 0.1.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +379 -0
  3. package/dist/data-table/BaseDataTable.svelte +322 -0
  4. package/dist/data-table/BaseDataTable.svelte.d.ts +49 -0
  5. package/dist/data-table/BaseDataTable.svelte.d.ts.map +1 -0
  6. package/dist/data-table/DataTable.svelte +104 -0
  7. package/dist/data-table/DataTable.svelte.d.ts +59 -0
  8. package/dist/data-table/DataTable.svelte.d.ts.map +1 -0
  9. package/dist/data-table/DateRangeFilter.svelte +174 -0
  10. package/dist/data-table/DateRangeFilter.svelte.d.ts +30 -0
  11. package/dist/data-table/DateRangeFilter.svelte.d.ts.map +1 -0
  12. package/dist/data-table/FilterControl.svelte +184 -0
  13. package/dist/data-table/FilterControl.svelte.d.ts +5 -0
  14. package/dist/data-table/FilterControl.svelte.d.ts.map +1 -0
  15. package/dist/data-table/FilterTable.svelte +25 -0
  16. package/dist/data-table/FilterTable.svelte.d.ts +24 -0
  17. package/dist/data-table/FilterTable.svelte.d.ts.map +1 -0
  18. package/dist/data-table/NumberRangeFilter.svelte +80 -0
  19. package/dist/data-table/NumberRangeFilter.svelte.d.ts +32 -0
  20. package/dist/data-table/NumberRangeFilter.svelte.d.ts.map +1 -0
  21. package/dist/data-table/filter.d.ts +23 -0
  22. package/dist/data-table/filter.d.ts.map +1 -0
  23. package/dist/data-table/filter.js +38 -0
  24. package/dist/data-table/index.d.ts +11 -0
  25. package/dist/data-table/index.d.ts.map +1 -0
  26. package/dist/data-table/index.js +8 -0
  27. package/dist/data-table/state.d.ts +11 -0
  28. package/dist/data-table/state.d.ts.map +1 -0
  29. package/dist/data-table/state.js +58 -0
  30. package/dist/data-table/types.d.ts +167 -0
  31. package/dist/data-table/types.d.ts.map +1 -0
  32. package/dist/data-table/types.js +1 -0
  33. package/dist/dropdown/Dropdown.svelte +159 -0
  34. package/dist/dropdown/Dropdown.svelte.d.ts +28 -0
  35. package/dist/dropdown/Dropdown.svelte.d.ts.map +1 -0
  36. package/dist/dropdown/index.d.ts +3 -0
  37. package/dist/dropdown/index.d.ts.map +1 -0
  38. package/dist/dropdown/index.js +1 -0
  39. package/dist/dropdown/types.d.ts +9 -0
  40. package/dist/dropdown/types.d.ts.map +1 -0
  41. package/dist/dropdown/types.js +1 -0
  42. package/dist/dropdown-search/DropdownSearch.svelte +312 -0
  43. package/dist/dropdown-search/DropdownSearch.svelte.d.ts +51 -0
  44. package/dist/dropdown-search/DropdownSearch.svelte.d.ts.map +1 -0
  45. package/dist/dropdown-search/index.d.ts +6 -0
  46. package/dist/dropdown-search/index.d.ts.map +1 -0
  47. package/dist/dropdown-search/index.js +3 -0
  48. package/dist/dropdown-search/state.d.ts +15 -0
  49. package/dist/dropdown-search/state.d.ts.map +1 -0
  50. package/dist/dropdown-search/state.js +44 -0
  51. package/dist/dropdown-search/types.d.ts +24 -0
  52. package/dist/dropdown-search/types.d.ts.map +1 -0
  53. package/dist/dropdown-search/types.js +1 -0
  54. package/dist/i18n.d.ts +31 -0
  55. package/dist/i18n.d.ts.map +1 -0
  56. package/dist/i18n.js +118 -0
  57. package/dist/index.d.ts +7 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +5 -0
  60. package/dist/pagination/Pagination.svelte +112 -0
  61. package/dist/pagination/Pagination.svelte.d.ts +30 -0
  62. package/dist/pagination/Pagination.svelte.d.ts.map +1 -0
  63. package/dist/pagination/index.d.ts +6 -0
  64. package/dist/pagination/index.d.ts.map +1 -0
  65. package/dist/pagination/index.js +3 -0
  66. package/dist/style.css +1110 -0
  67. package/dist/toast/Toast.svelte +37 -0
  68. package/dist/toast/Toast.svelte.d.ts +26 -0
  69. package/dist/toast/Toast.svelte.d.ts.map +1 -0
  70. package/dist/toast/ToastManager.svelte +29 -0
  71. package/dist/toast/ToastManager.svelte.d.ts +26 -0
  72. package/dist/toast/ToastManager.svelte.d.ts.map +1 -0
  73. package/dist/toast/index.d.ts +7 -0
  74. package/dist/toast/index.d.ts.map +1 -0
  75. package/dist/toast/index.js +4 -0
  76. package/dist/toast/store.d.ts +7 -0
  77. package/dist/toast/store.d.ts.map +1 -0
  78. package/dist/toast/store.js +114 -0
  79. package/dist/toast/types.d.ts +36 -0
  80. package/dist/toast/types.d.ts.map +1 -0
  81. package/dist/toast/types.js +1 -0
  82. package/package.json +83 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bobby
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,379 @@
1
+ # svelte-ui-utils
2
+
3
+ Reusable Svelte 5 UI utilities published to the npm registry as
4
+ `@ibobbyts/svelte-ui-utils`.
5
+
6
+ ## Install
7
+
8
+ Install from the public npm registry. No GitHub Packages token is required.
9
+
10
+ With Bun:
11
+
12
+ ```bash
13
+ bun add @ibobbyts/svelte-ui-utils@0.1.2
14
+ ```
15
+
16
+ With npm:
17
+
18
+ ```bash
19
+ npm install @ibobbyts/svelte-ui-utils@0.1.2
20
+ ```
21
+
22
+ The repository does not track `dist/`; releases and local integration builds run
23
+ `npm run package` to generate the published files.
24
+
25
+ Import the stylesheet once in your app entry:
26
+
27
+ ```ts
28
+ import '@ibobbyts/svelte-ui-utils/style.css';
29
+ ```
30
+
31
+ ## Module imports
32
+
33
+ ```svelte
34
+ <script lang="ts">
35
+ import { ToastManager, toast } from '@ibobbyts/svelte-ui-utils/toast';
36
+ import { Dropdown } from '@ibobbyts/svelte-ui-utils/dropdown';
37
+ import { DropdownSearch } from '@ibobbyts/svelte-ui-utils/dropdown-search';
38
+ import { DataTable, DateRangeFilter, FilterTable, NumberRangeFilter } from '@ibobbyts/svelte-ui-utils/table';
39
+ </script>
40
+ ```
41
+
42
+ The package root also re-exports the public modules:
43
+
44
+ ```ts
45
+ import { ToastManager, Dropdown, DropdownSearch, DataTable } from '@ibobbyts/svelte-ui-utils';
46
+ ```
47
+
48
+ ## Toast
49
+
50
+ ```svelte
51
+ <script lang="ts">
52
+ import { ToastManager, toast } from '@ibobbyts/svelte-ui-utils/toast';
53
+ import '@ibobbyts/svelte-ui-utils/style.css';
54
+
55
+ function save() {
56
+ toast.success({
57
+ title: 'Saved',
58
+ message: 'The record was updated.',
59
+ duration: 4000,
60
+ position: 'top-right'
61
+ });
62
+ }
63
+ </script>
64
+
65
+ <ToastManager language="en_us" closeLabel="Close" />
66
+ <button on:click={save}>Save</button>
67
+ ```
68
+
69
+ Supported positions are `top-left`, `top-center`, `top-right`, `right-center`,
70
+ `bottom-right`, `bottom-center`, `bottom-left`, and `left-center`.
71
+
72
+ ## DropdownSearch
73
+
74
+ ```svelte
75
+ <script lang="ts">
76
+ import { DropdownSearch } from '@ibobbyts/svelte-ui-utils/dropdown-search';
77
+
78
+ async function loadOptions(query, { limit, signal }) {
79
+ const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=${limit}`, { signal });
80
+ return response.json();
81
+ }
82
+ </script>
83
+
84
+ <DropdownSearch
85
+ language="en_us"
86
+ placeholder="Search"
87
+ debounceMs={500}
88
+ clearLabel="Clear search"
89
+ width="24rem"
90
+ maxWidth="100%"
91
+ {loadOptions}
92
+ searchOnExternalValueChange={true}
93
+ />
94
+ ```
95
+
96
+ `loadOptions` returns `{ options, exactMatch }`. An item uses this shape:
97
+
98
+ ```ts
99
+ {
100
+ id: '123',
101
+ title: 'Jane Doe',
102
+ param_dict: { ID: 'M-123' }
103
+ }
104
+ ```
105
+
106
+ The input is valid when the server returns one unique `exactMatch`, or when the
107
+ user selects an item. Non-empty text without a unique match is invalid.
108
+ Set `validate={false}` when the field should only show selectable options and
109
+ stay visually neutral instead of turning green or red. In that mode the
110
+ component ignores `exactMatch` for status and auto-selection.
111
+ Use `searchOnExternalValueChange` for scanner or programmatic input workflows.
112
+ When the input has text, `DropdownSearch` shows an internal clear button on the
113
+ right side of the field. Use `clearLabel` to localize that button's accessible
114
+ label, or use `language` to select the package default.
115
+ Use `width`, `minWidth`, and `maxWidth` to size the control directly when a
116
+ wrapper is not convenient.
117
+ Server-side code and Node tests that only need pure helpers should import from
118
+ `@ibobbyts/svelte-ui-utils/dropdown-search/state` so they do not load Svelte
119
+ component files.
120
+
121
+ ## Dropdown
122
+
123
+ ```svelte
124
+ <script lang="ts">
125
+ import { Dropdown, type DropdownOption, type DropdownValue } from '@ibobbyts/svelte-ui-utils/dropdown';
126
+
127
+ const pageSizeOptions: DropdownOption[] = [
128
+ { label: '10', value: 10 },
129
+ { label: '20', value: 20 },
130
+ { label: '50', value: 50 }
131
+ ];
132
+
133
+ let pageSize: DropdownValue = 20;
134
+ </script>
135
+
136
+ <Dropdown
137
+ value={pageSize}
138
+ options={pageSizeOptions}
139
+ ariaLabel="Rows"
140
+ placement="down"
141
+ onChange={(next) => {
142
+ pageSize = next;
143
+ }}
144
+ />
145
+ ```
146
+
147
+ `Dropdown` is a controlled select-like component for simple option lists. Use
148
+ `placement="up"` when the menu should open above the trigger, such as bottom
149
+ pagination bars. `DataTable` uses this same component for its page-size picker.
150
+
151
+ ## DataTable
152
+
153
+ ```svelte
154
+ <script lang="ts">
155
+ import { DataTable } from '@ibobbyts/svelte-ui-utils/table';
156
+
157
+ const columns = [
158
+ { key: 'name', header: 'Name', sortable: true },
159
+ { key: 'createdAt', header: 'Created', sortable: true }
160
+ ];
161
+
162
+ let sort = null;
163
+ let page = 1;
164
+ let pageSize = 20;
165
+ </script>
166
+
167
+ <DataTable
168
+ language="en_us"
169
+ rows={rows}
170
+ {columns}
171
+ {sort}
172
+ {page}
173
+ {pageSize}
174
+ totalRows={totalRows}
175
+ tableLayout="auto"
176
+ stickyHeader={true}
177
+ onSortChange={(next) => {
178
+ sort = next;
179
+ page = 1;
180
+ }}
181
+ onPaginationChange={(next) => {
182
+ page = next.page;
183
+ pageSize = next.pageSize;
184
+ }}
185
+ />
186
+ ```
187
+
188
+ `DataTable` renders page-number pagination above and below the data table by
189
+ default, including a page-size selector. Use `language` for package-owned
190
+ defaults such as empty state, pagination label, and page-size label; use
191
+ `pageSizeLabel` or `emptyText` when a specific app needs to override them.
192
+ Use `showPagination={false}` for static tables. Sortable headers preserve the
193
+ current window scroll position by default and wait for an async `onSortChange`
194
+ before restoring scroll position.
195
+
196
+ `Pagination` is also available as a standalone module when an app needs
197
+ pagination outside `DataTable`:
198
+
199
+ ```svelte
200
+ <script lang="ts">
201
+ import { Pagination, type PaginationState } from '@ibobbyts/svelte-ui-utils/pagination';
202
+
203
+ let pagination: PaginationState = { page: 1, pageSize: 20 };
204
+ </script>
205
+
206
+ <Pagination
207
+ {pagination}
208
+ totalRows={totalRows}
209
+ pageSizeOptions={[10, 20, 50, 100]}
210
+ pageSizeDropdownPlacement="down"
211
+ onPaginationChange={(next) => {
212
+ pagination = next;
213
+ }}
214
+ />
215
+ ```
216
+
217
+ Render two synchronized pagination bars by passing both instances the same
218
+ controlled `pagination` value and the same `onPaginationChange` handler. This
219
+ is the same contract `DataTable` uses for its top and bottom pagination.
220
+
221
+ `FilterTable` is filter-only. It accepts `rows`, where each row has a `title`
222
+ for the left column and a controlled filter created with the `filter` helper:
223
+
224
+ ```svelte
225
+ <script lang="ts">
226
+ import { FilterTable, filter } from '@ibobbyts/svelte-ui-utils/table';
227
+
228
+ const filterRows = [
229
+ {
230
+ key: 'status',
231
+ title: 'Status',
232
+ filter: filter.checkbox({
233
+ value: selectedStatuses,
234
+ options: [
235
+ { label: 'Active', value: 'active' },
236
+ { label: 'Archived', value: 'archived' }
237
+ ],
238
+ onChange: (value) => (selectedStatuses = value)
239
+ })
240
+ },
241
+ {
242
+ key: 'search',
243
+ title: 'Search',
244
+ filter: filter.container([
245
+ filter.dropdownSearch({
246
+ value: searchValue,
247
+ selectedItem,
248
+ status: searchStatus,
249
+ width: '24rem',
250
+ maxWidth: '100%',
251
+ clearLabel: 'Clear search',
252
+ loadOptions,
253
+ onChange: (detail) => updateSearch(detail)
254
+ }),
255
+ filter.button({ icon: 'search', label: 'Find', onClick: submitSearch })
256
+ ])
257
+ }
258
+ ];
259
+ </script>
260
+
261
+ <FilterTable rows={filterRows} language="en_us" />
262
+ ```
263
+
264
+ If a dropdown-style filter appears clipped, check the parent containers first.
265
+ `DropdownSearch` renders its result list as an absolutely positioned child, so
266
+ any ancestor with `overflow: hidden`, `overflow: auto`, or `overflow: scroll`
267
+ can clip the menu even when the menu has a high `z-index`. Keep the nearest
268
+ filter container at `overflow: visible`, or move the clipping/scrolling behavior
269
+ to a parent that does not wrap the dropdown menu directly.
270
+
271
+ `dateRange` renders two browser date inputs plus preset buttons:
272
+ `last 24 hours`, `last 7 days`, `last 30 days`, `today`, `this week`,
273
+ `this month`, and `this year`. Manual changes emit `{ startDate, endDate,
274
+ preset: null }`. The `last24Hours` preset also emits `startDateTime` and
275
+ `endDateTime` so a consuming app can run an exact timestamp query while still
276
+ showing the covered dates in the inputs.
277
+
278
+ `numberRange` renders min/max number inputs and supports `prefixLabel`, for
279
+ example `$` for currency filters.
280
+
281
+ ## Localization
282
+
283
+ Components that render package-owned text accept `language="en_us"`,
284
+ `language="zh_cn"`, or `language="zh_tw"`. This affects only built-in defaults:
285
+ toast close labels, dropdown loading/empty/clear labels, table empty and
286
+ pagination labels, date range labels and presets, and number range labels.
287
+ Business labels such as column headers, filter row titles, button labels, and
288
+ placeholders should still be passed by the consuming app. Explicit props such
289
+ as `closeLabel`, `clearLabel`, `noResultsText`, `emptyText`, `pageSizeLabel`,
290
+ `startLabel`, and `minLabel` always override the language defaults.
291
+
292
+ Use `DataTable showPagination={false}` for a non-paginated data table:
293
+
294
+ ```svelte
295
+ <DataTable
296
+ rows={rows}
297
+ {columns}
298
+ showPagination={false}
299
+ rowKey="id"
300
+ tableLayout="fixed"
301
+ stickyHeader={true}
302
+ stickyHeaderOffset="4rem"
303
+ verticalSeparators={true}
304
+ preserveScrollOnSort={true}
305
+ rowAttributes={(row) => ({ 'data-row-id': row.id })}
306
+ onSortChange={(sort) => updateUrl(sort)}
307
+ >
308
+ <svelte:fragment slot="cell" let:row let:column let:value>
309
+ {#if column.key === 'actions'}
310
+ <button type="button">Open</button>
311
+ {:else}
312
+ {value}
313
+ {/if}
314
+ </svelte:fragment>
315
+ </DataTable>
316
+ ```
317
+
318
+ Set `preserveScrollOnSort={false}` when a page should intentionally return to
319
+ the top after sorting. `DataTable` headers are sticky by default. Use
320
+ `stickyHeader={false}` to disable this, or set `stickyHeaderOffset` when an app
321
+ has a fixed or sticky navbar. The offset accepts any browser CSS length such as
322
+ `64px`, `4rem`, or `calc(...)`, and it is used both for the fixed header
323
+ position and for the scroll threshold. When the original header top reaches the
324
+ offset, a synchronized fixed header takes over while the original header keeps
325
+ its layout space. The older `stickyHeaderTop` prop and
326
+ `--suu-table-sticky-top` CSS variable remain supported.
327
+
328
+ ## Theme variables
329
+
330
+ The package ships plain CSS and CSS variables. Override variables globally or
331
+ inside a theme root:
332
+
333
+ ```css
334
+ :root {
335
+ --suu-color-bg: #ffffff;
336
+ --suu-color-text: #111827;
337
+ --suu-color-border: #d1d5db;
338
+ --suu-color-accent: #2563eb;
339
+ }
340
+
341
+ [data-theme='dark'] {
342
+ --suu-color-bg: #111827;
343
+ --suu-color-text: #f9fafb;
344
+ --suu-color-border: #374151;
345
+ --suu-color-accent: #60a5fa;
346
+ }
347
+ ```
348
+
349
+ ## Release
350
+
351
+ Publishing is release-driven:
352
+
353
+ Do not publish this package as part of consumer-app local integration work.
354
+ Create tags, GitHub Releases, or package publishes only when a release is
355
+ explicitly requested.
356
+
357
+ ```bash
358
+ gh repo create iBobbyTS/svelte-ui-utils --public --source . --remote origin --push
359
+ git tag v0.1.2
360
+ git push origin v0.1.2
361
+ gh release create v0.1.2 --title "v0.1.2" --notes "Publish public npm package"
362
+ gh run watch
363
+ ```
364
+
365
+ The release workflow runs checks and publishes to the public npm registry with
366
+ the repository secret `NPM_TOKEN`. The token must have permission to publish
367
+ `@ibobbyts/svelte-ui-utils`; do not commit it to this repository.
368
+
369
+ ## Bun install verification
370
+
371
+ The repository includes a `Bun Consumer Check` workflow. It creates a temporary
372
+ consumer project and installs the released npm version without registry-specific
373
+ tokens:
374
+
375
+ ```bash
376
+ bun add @ibobbyts/svelte-ui-utils@0.1.2 svelte
377
+ ```
378
+
379
+ Use it after each release when a Bun-based project will consume the package.