svelora 3.0.14 → 3.0.16

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 (100) hide show
  1. package/dist/components/ConfirmDialog/ConfirmDialog.svelte +167 -0
  2. package/dist/components/ConfirmDialog/ConfirmDialog.svelte.d.ts +10 -0
  3. package/dist/components/ConfirmDialog/confirm.d.ts +9 -0
  4. package/dist/components/ConfirmDialog/confirm.js +24 -0
  5. package/dist/components/ConfirmDialog/confirm.types.d.ts +22 -0
  6. package/dist/components/ConfirmDialog/confirm.types.js +1 -0
  7. package/dist/components/ConfirmDialog/index.d.ts +4 -0
  8. package/dist/components/ConfirmDialog/index.js +2 -0
  9. package/dist/components/Editor/editor.schemas.js +1 -1
  10. package/dist/components/SortableList/SortableList.svelte +54 -0
  11. package/dist/components/SortableList/SortableList.svelte.d.ts +26 -0
  12. package/dist/components/SortableList/index.d.ts +2 -0
  13. package/dist/components/SortableList/index.js +1 -0
  14. package/dist/components/SortableList/sortable-list.types.d.ts +17 -0
  15. package/dist/components/SortableList/sortable-list.types.js +1 -0
  16. package/dist/components/Table/Table.svelte +57 -1
  17. package/dist/components/Table/table.types.d.ts +2 -0
  18. package/dist/components/Table/table.variants.d.ts +34 -0
  19. package/dist/components/Table/table.variants.js +13 -0
  20. package/dist/components/Toast/Toaster.svelte +194 -137
  21. package/dist/components/Toast/index.d.ts +2 -0
  22. package/dist/components/Toast/index.js +1 -0
  23. package/dist/components/Toast/internal/AnimatedIcon.svelte +446 -0
  24. package/dist/components/Toast/internal/AnimatedIcon.svelte.d.ts +15 -0
  25. package/dist/components/Toast/internal/animated-icon.types.d.ts +1 -0
  26. package/dist/components/Toast/internal/animated-icon.types.js +1 -0
  27. package/dist/components/Toast/internal/french-toast/LICENSE.md +21 -0
  28. package/dist/components/Toast/internal/french-toast/components/CheckmarkIcon.svelte +64 -0
  29. package/dist/components/Toast/internal/french-toast/components/CheckmarkIcon.svelte.d.ts +8 -0
  30. package/dist/components/Toast/internal/french-toast/components/ErrorIcon.svelte +74 -0
  31. package/dist/components/Toast/internal/french-toast/components/ErrorIcon.svelte.d.ts +8 -0
  32. package/dist/components/Toast/internal/french-toast/components/LoaderIcon.svelte +28 -0
  33. package/dist/components/Toast/internal/french-toast/components/LoaderIcon.svelte.d.ts +8 -0
  34. package/dist/components/Toast/internal/french-toast/components/ToastBar.svelte +169 -0
  35. package/dist/components/Toast/internal/french-toast/components/ToastBar.svelte.d.ts +20 -0
  36. package/dist/components/Toast/internal/french-toast/components/ToastIcon.svelte +81 -0
  37. package/dist/components/Toast/internal/french-toast/components/ToastIcon.svelte.d.ts +7 -0
  38. package/dist/components/Toast/internal/french-toast/components/ToastMessage.svelte +13 -0
  39. package/dist/components/Toast/internal/french-toast/components/ToastMessage.svelte.d.ts +8 -0
  40. package/dist/components/Toast/internal/french-toast/components/ToastWrapper.svelte +57 -0
  41. package/dist/components/Toast/internal/french-toast/components/ToastWrapper.svelte.d.ts +12 -0
  42. package/dist/components/Toast/internal/french-toast/components/Toaster.svelte +73 -0
  43. package/dist/components/Toast/internal/french-toast/components/Toaster.svelte.d.ts +23 -0
  44. package/dist/components/Toast/internal/french-toast/core/store.svelte.d.ts +15 -0
  45. package/dist/components/Toast/internal/french-toast/core/store.svelte.js +92 -0
  46. package/dist/components/Toast/internal/french-toast/core/toast.d.ts +21 -0
  47. package/dist/components/Toast/internal/french-toast/core/toast.js +67 -0
  48. package/dist/components/Toast/internal/french-toast/core/types.d.ts +58 -0
  49. package/dist/components/Toast/internal/french-toast/core/types.js +4 -0
  50. package/dist/components/Toast/internal/french-toast/core/use-toaster.svelte.d.ts +16 -0
  51. package/dist/components/Toast/internal/french-toast/core/use-toaster.svelte.js +71 -0
  52. package/dist/components/Toast/internal/french-toast/core/utils.d.ts +6 -0
  53. package/dist/components/Toast/internal/french-toast/core/utils.js +25 -0
  54. package/dist/components/Toast/internal/french-toast/toast-context.d.ts +18 -0
  55. package/dist/components/Toast/internal/french-toast/toast-context.js +10 -0
  56. package/dist/components/Toast/internal/notify.d.ts +31 -0
  57. package/dist/components/Toast/internal/notify.js +100 -0
  58. package/dist/components/Toast/internal/toast-icons/ToastError.svelte +8 -0
  59. package/dist/components/Toast/internal/toast-icons/ToastError.svelte.d.ts +4 -0
  60. package/dist/components/Toast/internal/toast-icons/ToastInfo.svelte +8 -0
  61. package/dist/components/Toast/internal/toast-icons/ToastInfo.svelte.d.ts +4 -0
  62. package/dist/components/Toast/internal/toast-icons/ToastLoading.svelte +8 -0
  63. package/dist/components/Toast/internal/toast-icons/ToastLoading.svelte.d.ts +4 -0
  64. package/dist/components/Toast/internal/toast-icons/ToastProcessing.svelte +8 -0
  65. package/dist/components/Toast/internal/toast-icons/ToastProcessing.svelte.d.ts +4 -0
  66. package/dist/components/Toast/internal/toast-icons/ToastSuccess.svelte +8 -0
  67. package/dist/components/Toast/internal/toast-icons/ToastSuccess.svelte.d.ts +4 -0
  68. package/dist/components/Toast/internal/toast-icons/ToastWarning.svelte +8 -0
  69. package/dist/components/Toast/internal/toast-icons/ToastWarning.svelte.d.ts +4 -0
  70. package/dist/components/Toast/internal/toast-icons/index.d.ts +6 -0
  71. package/dist/components/Toast/internal/toast-icons/index.js +6 -0
  72. package/dist/components/Toast/internal/toast-icons/toast-icon.types.d.ts +4 -0
  73. package/dist/components/Toast/internal/toast-icons/toast-icon.types.js +1 -0
  74. package/dist/components/Toast/toast.d.ts +31 -16
  75. package/dist/components/Toast/toast.js +45 -20
  76. package/dist/components/Toast/toast.types.d.ts +20 -7
  77. package/dist/hooks/index.d.ts +2 -0
  78. package/dist/hooks/index.js +2 -0
  79. package/dist/hooks/internal/DragDropProviderScope.svelte +29 -0
  80. package/dist/hooks/internal/DragDropProviderScope.svelte.d.ts +7 -0
  81. package/dist/hooks/internal/SortableProvider.svelte +30 -0
  82. package/dist/hooks/internal/SortableProvider.svelte.d.ts +7 -0
  83. package/dist/hooks/internal/drag-drop-context.d.ts +12 -0
  84. package/dist/hooks/internal/drag-drop-context.js +1 -0
  85. package/dist/hooks/internal/sortable-context.d.ts +12 -0
  86. package/dist/hooks/internal/sortable-context.js +1 -0
  87. package/dist/hooks/useDragDrop/index.d.ts +1 -0
  88. package/dist/hooks/useDragDrop/index.js +1 -0
  89. package/dist/hooks/useDragDrop/useDragDrop.svelte.d.ts +40 -0
  90. package/dist/hooks/useDragDrop/useDragDrop.svelte.js +128 -0
  91. package/dist/hooks/useSortable/index.d.ts +2 -0
  92. package/dist/hooks/useSortable/index.js +2 -0
  93. package/dist/hooks/useSortable/sortable-utils.d.ts +1 -0
  94. package/dist/hooks/useSortable/sortable-utils.js +13 -0
  95. package/dist/hooks/useSortable/useSortable.svelte.d.ts +52 -0
  96. package/dist/hooks/useSortable/useSortable.svelte.js +120 -0
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.js +2 -0
  99. package/dist/mcp/svelora-docs.data.json +14 -6
  100. package/package.json +5 -3
@@ -0,0 +1,167 @@
1
+ <script lang="ts" module>export {};
2
+ </script>
3
+
4
+ <script lang="ts">import { Dialog } from "bits-ui";
5
+ import Button from "../Button/Button.svelte";
6
+ import Input from "../Input/Input.svelte";
7
+ import Textarea from "../Textarea/Textarea.svelte";
8
+ import AnimatedIcon from "../Toast/internal/AnimatedIcon.svelte";
9
+ import { modalVariants } from "../Modal/modal.variants.js";
10
+ let { confirmText: defaultConfirmText = "Confirm", cancelText: defaultCancelText = "Cancel" } = $props();
11
+ let open = $state(false);
12
+ let pending = $state(null);
13
+ let currentInputValue = $state("");
14
+ const classes = $derived(modalVariants({
15
+ transition: "scale",
16
+ size: "sm",
17
+ overlay: true,
18
+ scrollable: false
19
+ }));
20
+ const dismissible = $derived(pending?.options.dismissible !== false);
21
+ const showCancel = $derived(pending?.options.showCancel !== false);
22
+ export function show(options = {}) {
23
+ return new Promise((resolve) => {
24
+ currentInputValue = options.inputValue ?? "";
25
+ pending = {
26
+ options,
27
+ resolve
28
+ };
29
+ open = true;
30
+ });
31
+ }
32
+ function settle(result) {
33
+ const current = pending;
34
+ pending = null;
35
+ open = false;
36
+ current?.resolve(result);
37
+ }
38
+ function handleConfirm() {
39
+ pending?.options.onConfirm?.(currentInputValue);
40
+ settle(true);
41
+ }
42
+ function handleCancel() {
43
+ pending?.options.onCancel?.();
44
+ settle(false);
45
+ }
46
+ function handleOpenChange(value) {
47
+ if (!value && pending) {
48
+ handleCancel();
49
+ return;
50
+ }
51
+ open = value;
52
+ }
53
+ const contentProps = $derived.by(() => {
54
+ const behavior = dismissible ? "close" : "ignore";
55
+ return {
56
+ trapFocus: true,
57
+ preventScroll: true,
58
+ escapeKeydownBehavior: behavior,
59
+ interactOutsideBehavior: behavior
60
+ };
61
+ });
62
+ </script>
63
+
64
+ <Dialog.Root bind:open onOpenChange={handleOpenChange}>
65
+ <Dialog.Portal>
66
+ <Dialog.Overlay class={classes.overlay()} />
67
+ {#if pending}
68
+ <Dialog.Content
69
+ {...contentProps}
70
+ class={[classes.content(), 'p-6 text-center']}
71
+ >
72
+ {#if pending.options.title}
73
+ <Dialog.Title class="sr-only">{pending.options.title}</Dialog.Title>
74
+ {/if}
75
+ {#if pending.options.message}
76
+ <Dialog.Description class="sr-only"
77
+ >{pending.options.message}</Dialog.Description
78
+ >
79
+ {/if}
80
+
81
+ {#if pending.options.icon}
82
+ {#key pending.options.icon}
83
+ <div class="mb-4 flex min-h-20 justify-center">
84
+ <AnimatedIcon
85
+ type={pending.options.icon}
86
+ size={80}
87
+ forceMotion
88
+ idle={false}
89
+ />
90
+ </div>
91
+ {/key}
92
+ {/if}
93
+
94
+ {#if pending.options.title}
95
+ <h3 class="mb-2 text-lg font-semibold text-on-surface">
96
+ {pending.options.title}
97
+ </h3>
98
+ {/if}
99
+
100
+ {#if pending.options.message}
101
+ <p class="mb-6 text-sm text-on-surface-variant">
102
+ {pending.options.message}
103
+ </p>
104
+ {:else if !pending.options.inputPlaceholder}
105
+ <div class="mb-6"></div>
106
+ {/if}
107
+
108
+ {#if pending.options.inputPlaceholder}
109
+ {#if pending.options.inputChoices && pending.options.inputChoices.length > 0}
110
+ <div class="mb-3 flex flex-wrap justify-center gap-1">
111
+ {#each pending.options.inputChoices as choice (choice)}
112
+ <Button
113
+ label={choice}
114
+ size="xs"
115
+ variant={currentInputValue === choice ? 'solid' : 'outline'}
116
+ color={currentInputValue === choice ? 'primary' : 'surface'}
117
+ onclick={() => (currentInputValue = choice)}
118
+ />
119
+ {/each}
120
+ </div>
121
+ {/if}
122
+
123
+ <div class="mb-6 text-left">
124
+ {#if pending.options.inputType === 'textarea'}
125
+ <Textarea
126
+ bind:value={currentInputValue}
127
+ placeholder={pending.options.inputPlaceholder}
128
+ class="w-full"
129
+ />
130
+ {:else}
131
+ <Input
132
+ type={pending.options.inputType ?? 'text'}
133
+ bind:value={currentInputValue}
134
+ placeholder={pending.options.inputPlaceholder}
135
+ class="w-full"
136
+ onkeydown={(e) => {
137
+ if (e.key === 'Enter') {
138
+ e.preventDefault()
139
+ handleConfirm()
140
+ }
141
+ }}
142
+ />
143
+ {/if}
144
+ </div>
145
+ {/if}
146
+
147
+ <div class="flex justify-center gap-2">
148
+ {#if showCancel}
149
+ <Button
150
+ label={pending.options.cancelText ?? defaultCancelText}
151
+ variant="outline"
152
+ class="flex-1"
153
+ onclick={handleCancel}
154
+ />
155
+ {/if}
156
+ <Button
157
+ label={pending.options.confirmText ?? defaultConfirmText}
158
+ variant="solid"
159
+ color={pending.options.confirmColor ?? 'primary'}
160
+ class={showCancel ? 'flex-1' : 'w-full'}
161
+ onclick={handleConfirm}
162
+ />
163
+ </div>
164
+ </Dialog.Content>
165
+ {/if}
166
+ </Dialog.Portal>
167
+ </Dialog.Root>
@@ -0,0 +1,10 @@
1
+ export type Props = {
2
+ confirmText?: string;
3
+ cancelText?: string;
4
+ };
5
+ import type { ConfirmOptions } from './confirm.types.js';
6
+ declare const ConfirmDialog: import("svelte").Component<Props, {
7
+ show: (options?: ConfirmOptions) => Promise<boolean>;
8
+ }, "">;
9
+ type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
10
+ export default ConfirmDialog;
@@ -0,0 +1,9 @@
1
+ import type { ConfirmOptions } from './confirm.types.js';
2
+ type ShowFn = (options?: ConfirmOptions) => Promise<boolean>;
3
+ export declare function registerConfirmDialog(fn: ShowFn | null): void;
4
+ export declare const confirmDialog: {
5
+ show(options?: ConfirmOptions): Promise<boolean>;
6
+ delete(options?: ConfirmOptions): Promise<boolean>;
7
+ };
8
+ export type ConfirmDialogUtility = typeof confirmDialog;
9
+ export {};
@@ -0,0 +1,24 @@
1
+ let showFn = null;
2
+ export function registerConfirmDialog(fn) {
3
+ showFn = fn;
4
+ }
5
+ export const confirmDialog = {
6
+ show(options = {}) {
7
+ if (!showFn) {
8
+ console.warn('[confirmDialog] ConfirmDialog is not registered — add <ConfirmDialog /> to your root layout.');
9
+ return Promise.resolve(false);
10
+ }
11
+ return showFn(options);
12
+ },
13
+ delete(options = {}) {
14
+ return this.show({
15
+ title: 'Delete this item?',
16
+ message: 'This action is permanent and cannot be undone.',
17
+ confirmText: 'Delete',
18
+ cancelText: 'Cancel',
19
+ icon: 'warning',
20
+ confirmColor: 'error',
21
+ ...options
22
+ });
23
+ }
24
+ };
@@ -0,0 +1,22 @@
1
+ import type { ButtonVariantProps } from '../Button/button.variants.js';
2
+ import type { AnimatedIconType } from '../Toast/internal/animated-icon.types.js';
3
+ export type ConfirmIcon = Extract<AnimatedIconType, 'warning' | 'error' | 'question' | 'info' | 'success'>;
4
+ export type ConfirmColor = NonNullable<ButtonVariantProps['color']>;
5
+ export interface ConfirmOptions {
6
+ title?: string;
7
+ message?: string;
8
+ confirmText?: string;
9
+ cancelText?: string;
10
+ icon?: ConfirmIcon;
11
+ confirmColor?: ConfirmColor;
12
+ /** When false, only the confirm button is shown (alert-style). @default true */
13
+ showCancel?: boolean;
14
+ /** When false, backdrop click and Escape do not dismiss. @default true */
15
+ dismissible?: boolean;
16
+ inputPlaceholder?: string;
17
+ inputValue?: string;
18
+ inputType?: 'text' | 'password' | 'email' | 'number' | 'textarea';
19
+ inputChoices?: string[];
20
+ onConfirm?: (value?: string) => void;
21
+ onCancel?: () => void;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
2
+ export { confirmDialog, registerConfirmDialog } from './confirm.js';
3
+ export type { ConfirmOptions, ConfirmIcon, ConfirmColor } from './confirm.types.js';
4
+ export type { ConfirmDialogUtility } from './confirm.js';
@@ -0,0 +1,2 @@
1
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
2
+ export { confirmDialog, registerConfirmDialog } from './confirm.js';
@@ -3,7 +3,7 @@ export function createUrlSchema(validateFn) {
3
3
  '~standard': {
4
4
  version: 1,
5
5
  vendor: 'svelora',
6
- validate: validateFn,
6
+ validate: validateFn
7
7
  }
8
8
  };
9
9
  }
@@ -0,0 +1,54 @@
1
+ <script lang="ts" module>export {};
2
+ </script>
3
+
4
+ <script lang="ts" generics="T">import { twMerge } from "tailwind-merge";
5
+ import Icon from "../Icon/Icon.svelte";
6
+ import { useSortable } from "../../hooks/useSortable/index.js";
7
+ let { items = $bindable(), getKey, onReorder, axis = "vertical", handle = true, disabled = false, class: className, itemClass, children } = $props();
8
+ const sortable = useSortable({
9
+ getItems: () => items,
10
+ getId: (item) => getKey(item),
11
+ axis: () => axis,
12
+ handle: () => handle ? "[data-sortable-handle]" : undefined,
13
+ disabled: () => disabled,
14
+ onReorder(nextItems) {
15
+ items = nextItems;
16
+ onReorder?.(nextItems);
17
+ }
18
+ });
19
+ const listClass = $derived(twMerge(axis === "vertical" ? "flex flex-col gap-2" : axis === "horizontal" ? "flex flex-row flex-wrap gap-2" : "gap-3", className));
20
+ const itemClasses = $derived(twMerge("relative flex items-center gap-3 rounded-lg border border-outline-variant/60 bg-surface-container-low px-3 py-2 transition-shadow select-none", itemClass));
21
+ </script>
22
+
23
+ <sortable.Provider>
24
+ <div use:sortable.container class={listClass} role="list">
25
+ {#each items as item, index (getKey(item))}
26
+ {@const id = getKey(item)}
27
+ {@const dragging = sortable.draggingId === String(id)}
28
+ <div
29
+ use:sortable.item={{ index, item }}
30
+ role="listitem"
31
+ class={twMerge(itemClasses, dragging && 'z-10 opacity-95 shadow-md')}
32
+ aria-grabbed={dragging}
33
+ >
34
+ {#if handle}
35
+ <button
36
+ type="button"
37
+ data-sortable-handle
38
+ class="inline-flex shrink-0 cursor-grab rounded-md p-1 text-on-surface-variant hover:bg-surface-container-high active:cursor-grabbing"
39
+ aria-label="Drag to reorder"
40
+ >
41
+ <Icon name="lucide:grip-vertical" size="16" />
42
+ </button>
43
+ {/if}
44
+ {@render children({ item, index, dragging })}
45
+ </div>
46
+ {/each}
47
+ </div>
48
+ </sortable.Provider>
49
+
50
+ <style>
51
+ :global([data-sortable-active='true']) {
52
+ user-select: none;
53
+ }
54
+ </style>
@@ -0,0 +1,26 @@
1
+ export type { SortableListProps } from './sortable-list.types.js';
2
+ import type { SortableListProps } from './sortable-list.types.js';
3
+ declare function $$render<T>(): {
4
+ props: SortableListProps<T>;
5
+ exports: {};
6
+ bindings: "items";
7
+ slots: {};
8
+ events: {};
9
+ };
10
+ declare class __sveltets_Render<T> {
11
+ props(): ReturnType<typeof $$render<T>>['props'];
12
+ events(): ReturnType<typeof $$render<T>>['events'];
13
+ slots(): ReturnType<typeof $$render<T>>['slots'];
14
+ bindings(): "items";
15
+ exports(): {};
16
+ }
17
+ interface $$IsomorphicComponent {
18
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
19
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
20
+ } & ReturnType<__sveltets_Render<T>['exports']>;
21
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
22
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
23
+ }
24
+ declare const SortableList: $$IsomorphicComponent;
25
+ type SortableList<T> = InstanceType<typeof SortableList<T>>;
26
+ export default SortableList;
@@ -0,0 +1,2 @@
1
+ export { default as SortableList } from './SortableList.svelte';
2
+ export type { SortableListProps } from './sortable-list.types.js';
@@ -0,0 +1 @@
1
+ export { default as SortableList } from './SortableList.svelte';
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+ export interface SortableListProps<T> {
4
+ items: T[];
5
+ getKey: (item: T) => string | number;
6
+ onReorder?: (items: T[]) => void;
7
+ axis?: 'vertical' | 'horizontal' | 'grid';
8
+ handle?: boolean;
9
+ disabled?: boolean;
10
+ class?: ClassNameValue;
11
+ itemClass?: ClassNameValue;
12
+ children: Snippet<[{
13
+ item: T;
14
+ index: number;
15
+ dragging: boolean;
16
+ }]>;
17
+ }
@@ -11,7 +11,7 @@ import { autoGenerateColumns, computePinOffsets, filterByColumns, filterByGlobal
11
11
  import { tableDefaults, tableVariants } from "./table.variants.js";
12
12
  const config = getComponentConfig("table", tableDefaults);
13
13
  const icons = getComponentConfig("icons", iconsDefaults);
14
- let { ref = $bindable(null), as = "div", data = [], columns: columnsProp, rowKey, caption, sorting = $bindable([]), multiSort = false, manualSorting = false, onSortingChange, globalFilter = $bindable(""), globalFilterKeys, manualFiltering = false, onGlobalFilterChange, columnFilters = $bindable({}), onColumnFiltersChange, page = $bindable(0), pageSize = 10, manualPagination = false, onPageChange, selection, selectedRows = $bindable([]), onSelectionChange, columnVisibility = $bindable(), onColumnVisibilityChange, columnPinning = $bindable(), pinnedRows = $bindable([]), onPinnedRowsChange, expandedRows = $bindable([]), onExpandedChange, columnSizing = $bindable({}), onColumnSizingChange, loading = false, loadingColor = config.defaultVariants.loadingColor ?? "primary", loadingAnimation = config.defaultVariants.loadingAnimation ?? "carousel", empty = "No data.", striped = false, hoverable = config.defaultVariants.hoverable ?? true, sticky = false, action, onRowClick, onRowHover, onRowContextmenu, ui, class: className, captionSlot, emptySlot, loadingSlot, expandedSlot, bodyTopSlot, bodyBottomSlot, headerSlot, cellSlot, ...restProps } = $props();
14
+ let { ref = $bindable(null), as = "div", data = [], columns: columnsProp, rowKey, caption, sorting = $bindable([]), multiSort = false, manualSorting = false, onSortingChange, globalFilter = $bindable(""), globalFilterKeys, manualFiltering = false, onGlobalFilterChange, columnFilters = $bindable({}), onColumnFiltersChange, page = $bindable(0), pageSize = 10, manualPagination = false, onPageChange, selection, selectedRows = $bindable([]), onSelectionChange, columnVisibility = $bindable(), onColumnVisibilityChange, columnPinning = $bindable(), pinnedRows = $bindable([]), onPinnedRowsChange, expandedRows = $bindable([]), onExpandedChange, columnSizing = $bindable({}), onColumnSizingChange, loading = false, loadingColor = config.defaultVariants.loadingColor ?? "primary", loadingAnimation = config.defaultVariants.loadingAnimation ?? "carousel", size = config.defaultVariants.size ?? "md", empty = "No data.", striped = false, hoverable = config.defaultVariants.hoverable ?? true, sticky = false, action, onRowClick, onRowHover, onRowContextmenu, ui, class: className, captionSlot, emptySlot, loadingSlot, expandedSlot, bodyTopSlot, bodyBottomSlot, headerSlot, cellSlot, ...restProps } = $props();
15
15
  // =========================================================================
16
16
  // Apply Svelte action on root element
17
17
  // =========================================================================
@@ -75,6 +75,7 @@ const resolvedColumns = $derived.by(() => {
75
75
  });
76
76
  const visibleColumns = $derived(resolveVisibleColumns(resolvedColumns, columnVisibility, columnPinning));
77
77
  const hasFooter = $derived(visibleColumns.some((col) => col.footer));
78
+ const hasColumnFilterRow = $derived(visibleColumns.some((col) => col.filterable === true));
78
79
  const totalColspan = $derived(visibleColumns.length + (selection === "multiple" ? 1 : 0));
79
80
  // =========================================================================
80
81
  // Data Pipeline: filter → sort → paginate
@@ -304,6 +305,7 @@ function getAlignClass(align) {
304
305
  // Variant Classes
305
306
  // =========================================================================
306
307
  const variantSlots = $derived(tableVariants({
308
+ size,
307
309
  hoverable: hoverable || undefined,
308
310
  striped: striped || undefined,
309
311
  sticky: sticky || undefined,
@@ -523,6 +525,60 @@ function tdClass(key) {
523
525
  </th>
524
526
  {/each}
525
527
  </tr>
528
+
529
+ {#if hasColumnFilterRow}
530
+ <tr class="{classes.tr} border-t border-outline-variant/30">
531
+ {#if selection === 'multiple'}
532
+ <th class={classes.th} style="width: 48px"></th>
533
+ {/if}
534
+ {#each visibleColumns as col (col.key)}
535
+ <th
536
+ class="px-2 py-1.5"
537
+ data-pinned={isPinned(col.key) || undefined}
538
+ style={getPinStyle(col.key)}
539
+ >
540
+ {#if col.filterable}
541
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
542
+ <div class="relative">
543
+ <input
544
+ type="text"
545
+ value={columnFilters[col.key] ?? ''}
546
+ placeholder="Filter…"
547
+ aria-label="Filter {col.label ?? col.key}"
548
+ oninput={(e) => {
549
+ const val = (e.currentTarget as HTMLInputElement).value;
550
+ if (val) {
551
+ columnFilters = { ...columnFilters, [col.key]: val };
552
+ } else {
553
+ const { [col.key]: _removed, ...rest } = columnFilters;
554
+ columnFilters = rest;
555
+ }
556
+ onColumnFiltersChange?.(columnFilters);
557
+ page = 0;
558
+ }}
559
+ class="w-full rounded-md border border-outline-variant/40 bg-surface-container-low px-2 py-1 text-xs text-on-surface placeholder:text-on-surface-variant/40 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors"
560
+ />
561
+ {#if columnFilters[col.key]}
562
+ <button
563
+ type="button"
564
+ aria-label="Clear filter for {col.label ?? col.key}"
565
+ class="absolute right-1.5 top-1/2 -translate-y-1/2 text-on-surface-variant/50 hover:text-on-surface transition-colors"
566
+ onclick={() => {
567
+ const { [col.key]: _removed, ...rest } = columnFilters;
568
+ columnFilters = rest;
569
+ onColumnFiltersChange?.(columnFilters);
570
+ page = 0;
571
+ }}
572
+ >
573
+ <svg xmlns="http://www.w3.org/2000/svg" class="size-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
574
+ </button>
575
+ {/if}
576
+ </div>
577
+ {/if}
578
+ </th>
579
+ {/each}
580
+ </tr>
581
+ {/if}
526
582
  </thead>
527
583
 
528
584
  <!-- TBODY -->
@@ -160,6 +160,8 @@ export interface TableProps<T extends Record<string, any> = Record<string, any>>
160
160
  loadingColor?: NonNullable<TableVariantProps['loadingColor']>;
161
161
  /** Loading bar animation. @default 'carousel' */
162
162
  loadingAnimation?: NonNullable<TableVariantProps['loadingAnimation']>;
163
+ /** Table density preset. @default 'md' */
164
+ size?: NonNullable<TableVariantProps['size']>;
163
165
  /** Text shown when data is empty. @default 'No data.' */
164
166
  empty?: string;
165
167
  /** Alternate row background colors. @default false */
@@ -1,5 +1,16 @@
1
1
  import { type VariantProps } from 'tailwind-variants';
2
2
  export declare const tableVariants: import("tailwind-variants").TVReturnType<{
3
+ size: {
4
+ sm: {
5
+ th: string;
6
+ td: string;
7
+ };
8
+ md: {};
9
+ lg: {
10
+ th: string;
11
+ td: string;
12
+ };
13
+ };
3
14
  pinned: {
4
15
  true: {
5
16
  th: string;
@@ -63,6 +74,17 @@ export declare const tableVariants: import("tailwind-variants").TVReturnType<{
63
74
  empty: string;
64
75
  loading: string;
65
76
  }, undefined, {
77
+ size: {
78
+ sm: {
79
+ th: string;
80
+ td: string;
81
+ };
82
+ md: {};
83
+ lg: {
84
+ th: string;
85
+ td: string;
86
+ };
87
+ };
66
88
  pinned: {
67
89
  true: {
68
90
  th: string;
@@ -126,6 +148,17 @@ export declare const tableVariants: import("tailwind-variants").TVReturnType<{
126
148
  empty: string;
127
149
  loading: string;
128
150
  }, import("tailwind-variants").TVReturnType<{
151
+ size: {
152
+ sm: {
153
+ th: string;
154
+ td: string;
155
+ };
156
+ md: {};
157
+ lg: {
158
+ th: string;
159
+ td: string;
160
+ };
161
+ };
129
162
  pinned: {
130
163
  true: {
131
164
  th: string;
@@ -193,6 +226,7 @@ export type TableVariantProps = VariantProps<typeof tableVariants>;
193
226
  export type TableSlots = keyof ReturnType<typeof tableVariants>;
194
227
  export declare const tableDefaults: {
195
228
  defaultVariants: {
229
+ size: "md";
196
230
  hoverable: true;
197
231
  loadingColor: "primary";
198
232
  loadingAnimation: "carousel";
@@ -27,6 +27,17 @@ export const tableVariants = tv({
27
27
  loading: 'py-10 text-center'
28
28
  },
29
29
  variants: {
30
+ size: {
31
+ sm: {
32
+ th: 'px-3 py-2 text-[11px]',
33
+ td: 'px-3 py-2 text-xs'
34
+ },
35
+ md: {},
36
+ lg: {
37
+ th: 'px-5 py-3.5 text-sm',
38
+ td: 'px-5 py-4 text-base'
39
+ }
40
+ },
30
41
  pinned: {
31
42
  true: {
32
43
  th: 'sticky bg-surface-container-low/95 backdrop-blur-sm z-1',
@@ -110,6 +121,7 @@ export const tableVariants = tv({
110
121
  }
111
122
  ],
112
123
  defaultVariants: {
124
+ size: 'md',
113
125
  hoverable: true,
114
126
  loadingColor: 'primary',
115
127
  loadingAnimation: 'carousel'
@@ -118,6 +130,7 @@ export const tableVariants = tv({
118
130
  export const tableDefaults = {
119
131
  defaultVariants: {
120
132
  ...tableVariants.defaultVariants,
133
+ size: 'md',
121
134
  hoverable: true,
122
135
  loadingColor: 'primary',
123
136
  loadingAnimation: 'carousel'