@ims360/svelte-ivory 0.0.4 → 0.0.7
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 +7 -0
- package/dist/components/ai/AiMessage.svelte.d.ts +1 -0
- package/dist/components/ai/AiMessage.svelte.d.ts.map +1 -0
- package/dist/components/ai/AttachedFile.svelte.d.ts +1 -0
- package/dist/components/ai/AttachedFile.svelte.d.ts.map +1 -0
- package/dist/components/ai/Chat.svelte.d.ts +1 -0
- package/dist/components/ai/Chat.svelte.d.ts.map +1 -0
- package/dist/components/ai/Markdown.svelte.d.ts +1 -0
- package/dist/components/ai/Markdown.svelte.d.ts.map +1 -0
- package/dist/components/ai/UserMessage.svelte.d.ts +1 -0
- package/dist/components/ai/UserMessage.svelte.d.ts.map +1 -0
- package/dist/components/ai/index.d.ts +1 -0
- package/dist/components/ai/index.d.ts.map +1 -0
- package/dist/components/basic/checkbox/Checkbox.svelte.d.ts +1 -0
- package/dist/components/basic/checkbox/Checkbox.svelte.d.ts.map +1 -0
- package/dist/components/basic/index.d.ts +1 -0
- package/dist/components/basic/index.d.ts.map +1 -0
- package/dist/components/basic/toggle/Toggle.svelte +5 -3
- package/dist/components/basic/toggle/Toggle.svelte.d.ts +1 -0
- package/dist/components/basic/toggle/Toggle.svelte.d.ts.map +1 -0
- package/dist/components/buttons/CopyToClipboardButton.svelte.d.ts +1 -0
- package/dist/components/buttons/CopyToClipboardButton.svelte.d.ts.map +1 -0
- package/dist/components/layout/drawer/Drawer.svelte.d.ts +1 -0
- package/dist/components/layout/drawer/Drawer.svelte.d.ts.map +1 -0
- package/dist/components/layout/heading/Heading.svelte +1 -1
- package/dist/components/layout/heading/Heading.svelte.d.ts +1 -0
- package/dist/components/layout/heading/Heading.svelte.d.ts.map +1 -0
- package/dist/components/layout/heading/index.d.ts +1 -0
- package/dist/components/layout/heading/index.d.ts.map +1 -0
- package/dist/components/layout/hiddenBackground/HiddenBackground.svelte +3 -3
- package/dist/components/layout/hiddenBackground/HiddenBackground.svelte.d.ts +2 -1
- package/dist/components/layout/hiddenBackground/HiddenBackground.svelte.d.ts.map +1 -0
- package/dist/components/layout/hiddenBackground/index.d.ts +7 -0
- package/dist/components/layout/hiddenBackground/index.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +2 -1
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/layout/modal/Modal.svelte +60 -48
- package/dist/components/layout/modal/Modal.svelte.d.ts +1 -0
- package/dist/components/layout/modal/Modal.svelte.d.ts.map +1 -0
- package/dist/components/layout/modal/ModalTest.svelte.d.ts +1 -0
- package/dist/components/layout/modal/ModalTest.svelte.d.ts.map +1 -0
- package/dist/components/layout/popover/Popover.svelte.d.ts +1 -0
- package/dist/components/layout/popover/Popover.svelte.d.ts.map +1 -0
- package/dist/components/layout/portal/Portal.svelte.d.ts +1 -0
- package/dist/components/layout/portal/Portal.svelte.d.ts.map +1 -0
- package/dist/components/layout/tabs/Tab.svelte +3 -3
- package/dist/components/layout/tabs/Tab.svelte.d.ts +2 -1
- package/dist/components/layout/tabs/Tab.svelte.d.ts.map +1 -0
- package/dist/components/layout/tabs/TabPanel.svelte.d.ts +1 -0
- package/dist/components/layout/tabs/TabPanel.svelte.d.ts.map +1 -0
- package/dist/components/layout/tabs/Tabs.svelte.d.ts +1 -0
- package/dist/components/layout/tabs/Tabs.svelte.d.ts.map +1 -0
- package/dist/components/layout/tabs/index.d.ts +2 -1
- package/dist/components/layout/tabs/index.d.ts.map +1 -0
- package/dist/components/layout/tooltip/Tooltip.svelte +12 -11
- package/dist/components/layout/tooltip/Tooltip.svelte.d.ts +1 -0
- package/dist/components/layout/tooltip/Tooltip.svelte.d.ts.map +1 -0
- package/dist/components/table/index.d.ts +6 -0
- package/dist/components/table/index.d.ts.map +1 -0
- package/dist/components/table/index.js +5 -0
- package/dist/components/table/plugins/expandAll.svelte.d.ts +7 -0
- package/dist/components/table/plugins/expandAll.svelte.d.ts.map +1 -0
- package/dist/components/table/plugins/expandAll.svelte.js +24 -0
- package/dist/components/table/plugins/search.svelte.d.ts +13 -0
- package/dist/components/table/plugins/search.svelte.d.ts.map +1 -0
- package/dist/components/table/plugins/search.svelte.js +52 -0
- package/dist/components/table/table/Column.svelte +78 -0
- package/dist/components/table/table/Column.svelte.d.ts +16 -0
- package/dist/components/table/table/Column.svelte.d.ts.map +1 -0
- package/dist/components/table/table/ColumnHead.svelte +73 -0
- package/dist/components/table/table/ColumnHead.svelte.d.ts +11 -0
- package/dist/components/table/table/ColumnHead.svelte.d.ts.map +1 -0
- package/dist/components/table/table/Row.svelte +67 -0
- package/dist/components/table/table/Row.svelte.d.ts +13 -0
- package/dist/components/table/table/Row.svelte.d.ts.map +1 -0
- package/dist/components/table/table/Table.svelte +137 -0
- package/dist/components/table/table/Table.svelte.d.ts +52 -0
- package/dist/components/table/table/Table.svelte.d.ts.map +1 -0
- package/dist/components/table/table/VirtualList.svelte +101 -0
- package/dist/components/table/table/VirtualList.svelte.d.ts +41 -0
- package/dist/components/table/table/VirtualList.svelte.d.ts.map +1 -0
- package/dist/components/table/table/column.svelte.d.ts +21 -0
- package/dist/components/table/table/column.svelte.d.ts.map +1 -0
- package/dist/components/table/table/column.svelte.js +47 -0
- package/dist/components/table/table/index.js +11 -0
- package/dist/components/table/table/table.svelte.d.ts +36 -0
- package/dist/components/table/table/table.svelte.d.ts.map +1 -0
- package/dist/components/table/table/table.svelte.js +92 -0
- package/dist/components/toast/Toast.svelte.d.ts +1 -0
- package/dist/components/toast/Toast.svelte.d.ts.map +1 -0
- package/dist/components/toast/index.d.ts +1 -0
- package/dist/components/toast/index.d.ts.map +1 -0
- package/dist/components/toast/toasts.svelte.d.ts +1 -0
- package/dist/components/toast/toasts.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/utils/actions/clickOutside.d.ts +1 -0
- package/dist/utils/actions/clickOutside.d.ts.map +1 -0
- package/dist/utils/actions/focusTrap.d.ts +1 -0
- package/dist/utils/actions/focusTrap.d.ts.map +1 -0
- package/dist/utils/actions/index.d.ts +2 -0
- package/dist/utils/actions/index.d.ts.map +1 -0
- package/dist/utils/actions/index.js +1 -0
- package/dist/utils/actions/portal.d.ts +1 -0
- package/dist/utils/actions/portal.d.ts.map +1 -0
- package/dist/utils/actions/resize.d.ts +6 -0
- package/dist/utils/actions/resize.d.ts.map +1 -0
- package/dist/utils/actions/resize.js +25 -0
- package/dist/utils/actions/shortcut.d.ts +1 -0
- package/dist/utils/actions/shortcut.d.ts.map +1 -0
- package/dist/utils/actions/visible.d.ts +1 -0
- package/dist/utils/actions/visible.d.ts.map +1 -0
- package/dist/utils/functions/cookie.d.ts +1 -0
- package/dist/utils/functions/cookie.d.ts.map +1 -0
- package/dist/utils/functions/index.d.ts +1 -0
- package/dist/utils/functions/index.d.ts.map +1 -0
- package/dist/utils/functions/pseudoRandomId.d.ts +1 -0
- package/dist/utils/functions/pseudoRandomId.d.ts.map +1 -0
- package/dist/utils/functions/queryParams.d.ts +1 -0
- package/dist/utils/functions/queryParams.d.ts.map +1 -0
- package/package.json +9 -2
- package/src/lib/components/basic/toggle/Toggle.svelte +5 -3
- package/src/lib/components/layout/heading/Heading.svelte +1 -1
- package/src/lib/components/layout/hiddenBackground/HiddenBackground.svelte +3 -3
- package/src/lib/components/layout/index.ts +1 -1
- package/src/lib/components/layout/modal/Modal.svelte +60 -48
- package/src/lib/components/layout/tabs/Tab.svelte +3 -3
- package/src/lib/components/layout/tooltip/Tooltip.svelte +12 -11
- package/src/lib/components/table/index.ts +5 -0
- package/src/lib/components/table/plugins/expandAll.svelte.ts +34 -0
- package/src/lib/components/table/plugins/search.svelte.ts +75 -0
- package/src/lib/components/table/table/Column.svelte +78 -0
- package/src/lib/components/table/table/ColumnHead.svelte +73 -0
- package/src/lib/components/table/table/Row.svelte +67 -0
- package/src/lib/components/table/table/Table.svelte +137 -0
- package/src/lib/components/table/table/VirtualList.svelte +101 -0
- package/src/lib/components/table/table/column.svelte.ts +59 -0
- package/src/lib/components/table/table/index.ts +15 -0
- package/src/lib/components/table/table/table.svelte.ts +124 -0
- package/src/lib/utils/actions/index.ts +1 -0
- package/src/lib/utils/actions/resize.ts +35 -0
- package/dist/components/index.d.ts +0 -0
- package/dist/components/index.js +0 -1
- package/src/lib/components/basic/checkbox/checkbox.svelte.spec.ts +0 -39
- package/src/lib/components/basic/toggle/toggle.svelte.spec.ts +0 -19
- package/src/lib/components/index.ts +0 -0
- package/src/lib/components/layout/modal/modal.svelte.spec.ts +0 -39
- package/src/lib/components/layout/tabs/Tabs.test.svelte +0 -5
- package/src/lib/utils/actions/clickOutside.svelte.spec.ts +0 -67
- package/src/lib/utils/actions/shortcut.svelte.spec.ts +0 -19
- package/src/lib/utils/functions/cookie.svelte.spec.ts +0 -55
- package/src/lib/utils/functions/pseudoRandomId.spec.ts +0 -19
- package/src/lib/utils/functions/queryParams.spec.ts +0 -25
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
5
5
|
import type { ClassValue } from 'svelte/elements';
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
|
-
import Heading from '
|
|
8
|
-
import HiddenBackground from '../hiddenBackground/HiddenBackground.svelte';
|
|
7
|
+
import { Heading, HiddenBackground, Portal } from '..';
|
|
9
8
|
|
|
10
9
|
/** Props for the modal, expose if you overwrite the defaults in a custom component */
|
|
11
10
|
export interface ModalProps {
|
|
@@ -60,58 +59,71 @@
|
|
|
60
59
|
A modal, comes with a title, close button and different variants per default.
|
|
61
60
|
-->
|
|
62
61
|
{#if b_open}
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{@render modal()}
|
|
72
|
-
</div>
|
|
73
|
-
{:else}
|
|
74
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
75
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
76
|
-
<div
|
|
77
|
-
class={twMerge(
|
|
78
|
-
clsx([
|
|
79
|
-
'bg-surface-50-950 relative flex max-h-full max-w-full flex-col overflow-hidden rounded',
|
|
80
|
-
clazz
|
|
81
|
-
])
|
|
82
|
-
)}
|
|
83
|
-
{style}
|
|
84
|
-
onclick={(e) => e.stopPropagation()}
|
|
85
|
-
data-testid={testId}
|
|
86
|
-
>
|
|
62
|
+
<Portal>
|
|
63
|
+
<HiddenBackground
|
|
64
|
+
onclose={close}
|
|
65
|
+
class="flex h-full w-full flex-col items-center justify-start p-16"
|
|
66
|
+
>
|
|
67
|
+
{#if modal}
|
|
68
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
69
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
87
70
|
<div
|
|
88
|
-
class={
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
variant === 'warning' && 'preset-tonal-warning',
|
|
93
|
-
variant === 'error' && 'preset-tonal-error',
|
|
94
|
-
variant === 'info' && 'preset-tonal-primary'
|
|
95
|
-
]}
|
|
71
|
+
class={clazz}
|
|
72
|
+
onclick={(e) => e.stopPropagation()}
|
|
73
|
+
data-testid={testId}
|
|
74
|
+
{style}
|
|
96
75
|
>
|
|
97
|
-
{
|
|
98
|
-
<Heading>{title}</Heading>
|
|
99
|
-
{/if}
|
|
100
|
-
<button class="group ml-auto flex justify-end" type="button" onclick={close}>
|
|
101
|
-
<X class="h-full w-auto transition-[stroke-width] group-hover:stroke-3" />
|
|
102
|
-
</button>
|
|
76
|
+
{@render modal()}
|
|
103
77
|
</div>
|
|
78
|
+
{:else}
|
|
79
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
80
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
104
81
|
<div
|
|
105
82
|
class={twMerge(
|
|
106
|
-
clsx(
|
|
107
|
-
'
|
|
108
|
-
|
|
109
|
-
)
|
|
83
|
+
clsx([
|
|
84
|
+
'bg-surface-50-950 relative flex max-h-full max-w-full flex-col overflow-hidden rounded',
|
|
85
|
+
clazz
|
|
86
|
+
])
|
|
110
87
|
)}
|
|
88
|
+
{style}
|
|
89
|
+
onclick={(e) => e.stopPropagation()}
|
|
90
|
+
data-testid={testId}
|
|
111
91
|
>
|
|
112
|
-
|
|
92
|
+
<div
|
|
93
|
+
class={[
|
|
94
|
+
'flex flex-row items-center justify-between gap-4 px-4 py-3',
|
|
95
|
+
!variant && 'pb-0',
|
|
96
|
+
variant === 'success' && 'preset-tonal-success',
|
|
97
|
+
variant === 'warning' && 'preset-tonal-warning',
|
|
98
|
+
variant === 'error' && 'preset-tonal-error',
|
|
99
|
+
variant === 'info' && 'preset-tonal-primary'
|
|
100
|
+
]}
|
|
101
|
+
>
|
|
102
|
+
{#if title}
|
|
103
|
+
<Heading>{title}</Heading>
|
|
104
|
+
{/if}
|
|
105
|
+
<button
|
|
106
|
+
class="group ml-auto flex justify-end"
|
|
107
|
+
type="button"
|
|
108
|
+
onclick={close}
|
|
109
|
+
>
|
|
110
|
+
<X
|
|
111
|
+
class="h-full w-auto transition-[stroke-width] group-hover:stroke-3"
|
|
112
|
+
/>
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
<div
|
|
116
|
+
class={twMerge(
|
|
117
|
+
clsx(
|
|
118
|
+
'flex grow flex-col gap-4 overflow-hidden bg-inherit p-4 pt-3',
|
|
119
|
+
innerClass
|
|
120
|
+
)
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{@render children?.()}
|
|
124
|
+
</div>
|
|
113
125
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</
|
|
126
|
+
{/if}
|
|
127
|
+
</HiddenBackground>
|
|
128
|
+
</Portal>
|
|
117
129
|
{/if}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { getTabContext } from './Tabs.svelte';
|
|
9
9
|
|
|
10
10
|
type Props = {
|
|
11
|
-
class?: (selected: boolean) => ClassValue;
|
|
11
|
+
class?: ClassValue | ((selected: boolean) => ClassValue);
|
|
12
12
|
id?: string | undefined;
|
|
13
13
|
/**
|
|
14
14
|
* If this is set the element will be a link.
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
this={href ? 'a' : 'button'}
|
|
59
59
|
class={twMerge(
|
|
60
60
|
clsx(
|
|
61
|
-
'btn flex h-fit w-fit items-center justify-center px-0 text-xl font-bold',
|
|
62
|
-
clazz(selected)
|
|
61
|
+
'btn flex h-fit w-fit shrink-0 items-center justify-center px-0 text-xl font-bold select-none',
|
|
62
|
+
typeof clazz === 'function' ? clazz(selected) : clazz
|
|
63
63
|
)
|
|
64
64
|
)}
|
|
65
65
|
onclick={href
|
|
@@ -42,27 +42,28 @@
|
|
|
42
42
|
onclick,
|
|
43
43
|
href,
|
|
44
44
|
timeout = 500,
|
|
45
|
-
tooltipClass
|
|
45
|
+
tooltipClass,
|
|
46
|
+
placement = 'top'
|
|
46
47
|
}: Props = $props();
|
|
47
48
|
|
|
48
49
|
let target = $state<HTMLElement>();
|
|
49
50
|
|
|
50
51
|
let open = $state(false);
|
|
51
52
|
|
|
52
|
-
let
|
|
53
|
-
function
|
|
54
|
-
clearTimeout(
|
|
53
|
+
let currentTimeout: number;
|
|
54
|
+
function onmouseenter() {
|
|
55
|
+
clearTimeout(currentTimeout);
|
|
55
56
|
if (timeout === 0) {
|
|
56
57
|
open = true;
|
|
57
58
|
} else {
|
|
58
|
-
|
|
59
|
+
currentTimeout = setTimeout(() => {
|
|
59
60
|
open = true;
|
|
60
61
|
}, timeout) as unknown as number;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
function
|
|
65
|
-
clearTimeout(
|
|
65
|
+
function onmouseleave() {
|
|
66
|
+
clearTimeout(currentTimeout);
|
|
66
67
|
open = false;
|
|
67
68
|
}
|
|
68
69
|
</script>
|
|
@@ -80,8 +81,8 @@
|
|
|
80
81
|
type={onclick ? 'button' : undefined}
|
|
81
82
|
class={clazz}
|
|
82
83
|
bind:this={target}
|
|
83
|
-
{
|
|
84
|
-
{
|
|
84
|
+
{onmouseenter}
|
|
85
|
+
{onmouseleave}
|
|
85
86
|
{style}
|
|
86
87
|
{onclick}
|
|
87
88
|
>
|
|
@@ -93,10 +94,10 @@
|
|
|
93
94
|
<Popover
|
|
94
95
|
bind:b_open={open}
|
|
95
96
|
{target}
|
|
96
|
-
placement
|
|
97
|
+
{placement}
|
|
97
98
|
class={twMerge(
|
|
98
99
|
clsx(
|
|
99
|
-
'bg-surface-
|
|
100
|
+
'bg-surface-50-950 max-w-96 -translate-y-0.5 rounded px-4 py-1 shadow-lg',
|
|
100
101
|
tooltipClass
|
|
101
102
|
)
|
|
102
103
|
)}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { expandAllPlugin } from './plugins/expandAll.svelte';
|
|
2
|
+
export { searchPlugin } from './plugins/search.svelte';
|
|
3
|
+
export { default as Table } from './table';
|
|
4
|
+
export { default as Column, type ColumnProps } from './table/Column.svelte';
|
|
5
|
+
export { getColumnHeadContext } from './table/ColumnHead.svelte';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
2
|
+
import { getAllIds, type TablePlugin, type TableRow } from '../table/table.svelte';
|
|
3
|
+
|
|
4
|
+
interface ExpandAllConfig {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const DEFAULT_CONFIG: ExpandAllConfig = {
|
|
9
|
+
enabled: true
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function expandAllPlugin<T extends TableRow<T>>(
|
|
13
|
+
conf: Partial<ExpandAllConfig>
|
|
14
|
+
): TablePlugin<T> {
|
|
15
|
+
let initialized = false;
|
|
16
|
+
|
|
17
|
+
const config: ExpandAllConfig = {
|
|
18
|
+
...DEFAULT_CONFIG,
|
|
19
|
+
...conf
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const middleware: TablePlugin<T> = (state) => {
|
|
23
|
+
if (initialized || !config.enabled) return state;
|
|
24
|
+
initialized = true;
|
|
25
|
+
const allIds = getAllIds(...state.data);
|
|
26
|
+
const newExpanded = new SvelteSet(allIds);
|
|
27
|
+
return {
|
|
28
|
+
data: state.data,
|
|
29
|
+
expanded: newExpanded
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return middleware;
|
|
34
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
2
|
+
import type { TablePlugin, TableRow } from '../table/table.svelte';
|
|
3
|
+
|
|
4
|
+
interface SearchConfig<T extends TableRow<T>> {
|
|
5
|
+
search: string;
|
|
6
|
+
matches: (row: T) => boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function searchPlugin<T extends TableRow<T>>(conf: SearchConfig<T>): TablePlugin<T> {
|
|
10
|
+
let prevSearch: string | undefined = undefined;
|
|
11
|
+
let expandedBeforeSearch: Set<string> | undefined = undefined;
|
|
12
|
+
|
|
13
|
+
const middleware: TablePlugin<T> = (state) => {
|
|
14
|
+
// ensure that the state before the search is saved and restored when the user types
|
|
15
|
+
if (prevSearch && !conf.search && expandedBeforeSearch) {
|
|
16
|
+
prevSearch = conf.search;
|
|
17
|
+
return {
|
|
18
|
+
...state,
|
|
19
|
+
expanded: expandedBeforeSearch
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!conf.search) return state;
|
|
24
|
+
|
|
25
|
+
// ensure we store the state before the we started searching
|
|
26
|
+
if (conf.search && !prevSearch) {
|
|
27
|
+
expandedBeforeSearch = state.expanded;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// figure out which nodes to expand and hide
|
|
31
|
+
const { expanded, hidden } = search(state.data, conf.search, conf.matches);
|
|
32
|
+
|
|
33
|
+
prevSearch = conf.search;
|
|
34
|
+
return {
|
|
35
|
+
data: state.data.filter((d) => !hidden.has(d.id)),
|
|
36
|
+
expanded: new SvelteSet(expanded)
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
return middleware;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** collapses everything that doesnt match the searchString expands direct search hit */
|
|
43
|
+
export const search = <T extends TableRow<T>>(
|
|
44
|
+
nodes: T[],
|
|
45
|
+
searchString: string,
|
|
46
|
+
stringsMatch: (a: T, b: string) => boolean
|
|
47
|
+
) => {
|
|
48
|
+
const search = searchString.trim().toLowerCase();
|
|
49
|
+
const hidden = new Set<string>();
|
|
50
|
+
const expanded = new Set<string>();
|
|
51
|
+
|
|
52
|
+
const recursor = (node: T, childOfMatch = false): boolean => {
|
|
53
|
+
const matches = stringsMatch(node, search);
|
|
54
|
+
|
|
55
|
+
const intermediateNode =
|
|
56
|
+
node.children?.some((c) => recursor(c, matches || childOfMatch)) ?? false;
|
|
57
|
+
|
|
58
|
+
if (intermediateNode) {
|
|
59
|
+
console.log('intermediateNode', node);
|
|
60
|
+
|
|
61
|
+
expanded.add(node.id);
|
|
62
|
+
} else if (!childOfMatch) {
|
|
63
|
+
hidden.add(node.id);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return matches || intermediateNode;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
nodes.forEach((n) => recursor(n));
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
hidden,
|
|
73
|
+
expanded
|
|
74
|
+
};
|
|
75
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { type Snippet } from 'svelte';
|
|
4
|
+
import type { ClassValue } from 'svelte/elements';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
import type { ColumnConfig } from './column.svelte';
|
|
7
|
+
import { getTableContext } from './Table.svelte';
|
|
8
|
+
|
|
9
|
+
let defaultClasses = $state<ClassValue>();
|
|
10
|
+
|
|
11
|
+
export function setClasses(c: ClassValue) {
|
|
12
|
+
defaultClasses = c;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ColumnProps extends ColumnConfig {
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
/** If the type is incorrect pass the "row" property with the right type */
|
|
18
|
+
children: Snippet;
|
|
19
|
+
onclick?: (e: Event) => void | Promise<void>;
|
|
20
|
+
/** Cannot be used with resizable columns*/
|
|
21
|
+
ignoreWidth?: boolean;
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
let {
|
|
27
|
+
class: clazz = 'py-2 flex flex-row items-center',
|
|
28
|
+
children,
|
|
29
|
+
onclick,
|
|
30
|
+
ignoreWidth = false,
|
|
31
|
+
// ColumnConfig
|
|
32
|
+
resizable = true,
|
|
33
|
+
...props
|
|
34
|
+
}: ColumnProps = $props();
|
|
35
|
+
|
|
36
|
+
// Register the new column if this is the first table row that was rendered
|
|
37
|
+
const table = getTableContext();
|
|
38
|
+
const column = table.registerColumn({ resizable, ...props });
|
|
39
|
+
const allowClicking = $derived(!!onclick);
|
|
40
|
+
|
|
41
|
+
// passes updated props to the column
|
|
42
|
+
$effect(() => {
|
|
43
|
+
column.updateConfig({ resizable, ...props });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// this must be separate to the above effect, since otherwise the width would be reset on every scroll
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (!column.resizable) column.resize(props.width);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
function onClick(e: MouseEvent) {
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
onclick?.(e);
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
59
|
+
<svelte:element
|
|
60
|
+
this={allowClicking ? 'button' : 'div'}
|
|
61
|
+
onclick={allowClicking ? onClick : undefined}
|
|
62
|
+
type={onclick ? 'button' : undefined}
|
|
63
|
+
style={ignoreWidth ? '' : `width: ${column.width}px !important;`}
|
|
64
|
+
class={twMerge(
|
|
65
|
+
clsx(
|
|
66
|
+
'flex shrink-0 flex-row items-stretch justify-start truncate',
|
|
67
|
+
!ignoreWidth && [
|
|
68
|
+
'border-r border-transparent',
|
|
69
|
+
column.dragging && 'border-primary-400-600',
|
|
70
|
+
!column.dragging && column.hovering && 'border-surface-300-700'
|
|
71
|
+
],
|
|
72
|
+
defaultClasses,
|
|
73
|
+
clazz
|
|
74
|
+
)
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
{@render children()}
|
|
78
|
+
</svelte:element>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { getContext, setContext, type Snippet } from 'svelte';
|
|
3
|
+
import { resize } from '../../../utils/actions';
|
|
4
|
+
import type { Column } from './column.svelte';
|
|
5
|
+
|
|
6
|
+
const CONTEXT = {};
|
|
7
|
+
function setColumnHeadContext(column: Column) {
|
|
8
|
+
setContext(CONTEXT, column);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getColumnHeadContext(): Column {
|
|
12
|
+
return getContext(CONTEXT);
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
type Props = {
|
|
18
|
+
column: Column;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let { column, children }: Props = $props();
|
|
23
|
+
setColumnHeadContext(column);
|
|
24
|
+
|
|
25
|
+
let target = $state<HTMLElement | undefined>();
|
|
26
|
+
let dragging = $state(false);
|
|
27
|
+
|
|
28
|
+
const onHoverStart = () => {
|
|
29
|
+
column.hovering = true;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const onHoverEnd = () => {
|
|
33
|
+
column.hovering = false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const onResize = (mouseX: number) => {
|
|
37
|
+
if (!target) return;
|
|
38
|
+
let newWidth = mouseX - target.getBoundingClientRect().left;
|
|
39
|
+
column.resize(newWidth + 2);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const onDragging = (d: boolean) => {
|
|
43
|
+
dragging = d;
|
|
44
|
+
column.dragging = d;
|
|
45
|
+
};
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<div
|
|
49
|
+
class={['group flex shrink-0 flex-row justify-start']}
|
|
50
|
+
bind:this={target}
|
|
51
|
+
style="width: {column.width}px;"
|
|
52
|
+
>
|
|
53
|
+
{@render children()}
|
|
54
|
+
{#if column.resizable}
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
class={[
|
|
58
|
+
'ml-auto flex h-full w-4 shrink-0 translate-x-px cursor-col-resize justify-center border-r bg-inherit',
|
|
59
|
+
dragging
|
|
60
|
+
? '!border-primary-400-600'
|
|
61
|
+
: 'group-hover:!border-surface-300-700 border-transparent'
|
|
62
|
+
]}
|
|
63
|
+
use:resize={{ resized: onResize, dragging: onDragging }}
|
|
64
|
+
onmouseenter={onHoverStart}
|
|
65
|
+
onmouseleave={onHoverEnd}
|
|
66
|
+
onfocusin={onHoverStart}
|
|
67
|
+
onfocusout={onHoverEnd}
|
|
68
|
+
tabindex="-1"
|
|
69
|
+
aria-label="Resize column"
|
|
70
|
+
>
|
|
71
|
+
</button>
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { type Snippet } from 'svelte';
|
|
4
|
+
import type { ClassValue } from 'svelte/elements';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
|
|
7
|
+
let defaultClasses = $state<ClassValue>();
|
|
8
|
+
|
|
9
|
+
export function setClasses(c: ClassValue) {
|
|
10
|
+
defaultClasses = c;
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
interface Props {
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
onclick?: () => void;
|
|
18
|
+
href?: string;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
class: clazz = 'hover:bg-surface-950-50/10 transition-colors',
|
|
24
|
+
onclick,
|
|
25
|
+
href,
|
|
26
|
+
children
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const elementProps: {
|
|
30
|
+
this: 'button' | 'a' | 'div';
|
|
31
|
+
type?: 'button';
|
|
32
|
+
onclick?: () => void;
|
|
33
|
+
href?: string;
|
|
34
|
+
} = $derived.by(() => {
|
|
35
|
+
if (onclick) {
|
|
36
|
+
return {
|
|
37
|
+
this: 'button',
|
|
38
|
+
type: 'button',
|
|
39
|
+
onclick
|
|
40
|
+
};
|
|
41
|
+
} else if (href) {
|
|
42
|
+
return {
|
|
43
|
+
this: 'a',
|
|
44
|
+
href: href
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
return {
|
|
48
|
+
this: 'div'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
55
|
+
<svelte:element
|
|
56
|
+
this={elementProps.this}
|
|
57
|
+
{...elementProps}
|
|
58
|
+
class={twMerge(
|
|
59
|
+
clsx(
|
|
60
|
+
'flex h-full min-w-full grow flex-row items-stretch gap-2 overflow-hidden pr-4 pl-2',
|
|
61
|
+
defaultClasses,
|
|
62
|
+
clazz
|
|
63
|
+
)
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{@render children()}
|
|
67
|
+
</svelte:element>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { ChevronRight } from '@lucide/svelte';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { getContext, setContext, type Snippet } from 'svelte';
|
|
5
|
+
import type { ClassValue } from 'svelte/elements';
|
|
6
|
+
import { twMerge } from 'tailwind-merge';
|
|
7
|
+
import Column from './Column.svelte';
|
|
8
|
+
import ColumnHead from './ColumnHead.svelte';
|
|
9
|
+
import Row from './Row.svelte';
|
|
10
|
+
import { TableController, type TablePlugin, type TableRow } from './table.svelte';
|
|
11
|
+
import VirtualList from './VirtualList.svelte';
|
|
12
|
+
|
|
13
|
+
export interface TableProps<T extends { id: string }> {
|
|
14
|
+
class?: ClassValue;
|
|
15
|
+
data: T[];
|
|
16
|
+
onclick?: (row: T) => void;
|
|
17
|
+
href?: (row: T) => string | undefined;
|
|
18
|
+
rowHeight?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const TABLE_CONTEXT = {};
|
|
22
|
+
function setTableContext<T extends { id: string; children?: T[] | undefined }>(
|
|
23
|
+
table: TableController<T>
|
|
24
|
+
) {
|
|
25
|
+
setContext(TABLE_CONTEXT, table);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getTableContext<
|
|
29
|
+
T extends { id: string; children?: T[] | undefined }
|
|
30
|
+
>(): TableController<T> {
|
|
31
|
+
return getContext<TableController<T>>(TABLE_CONTEXT);
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script lang="ts" generics="T extends TableRow<T>">
|
|
36
|
+
interface Props<TI extends { id: string }> extends TableProps<TI> {
|
|
37
|
+
/** Renders the rows */
|
|
38
|
+
children: Snippet<[{ row: TI; nestingLevel?: number; index: number }]>;
|
|
39
|
+
/** Add columns in front of the tree-indicator */
|
|
40
|
+
firstColumn?: Snippet<[{ row: TI }]>;
|
|
41
|
+
rowClass?: ClassValue;
|
|
42
|
+
headerClass?: ClassValue;
|
|
43
|
+
plugins?: TablePlugin<TI>[];
|
|
44
|
+
controller?: TableController<TI>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
class: clazz,
|
|
49
|
+
data,
|
|
50
|
+
children: passedChildren,
|
|
51
|
+
firstColumn,
|
|
52
|
+
rowClass,
|
|
53
|
+
headerClass,
|
|
54
|
+
rowHeight = 64,
|
|
55
|
+
onclick,
|
|
56
|
+
href,
|
|
57
|
+
plugins = [],
|
|
58
|
+
controller: table = new TableController()
|
|
59
|
+
}: Props<T> = $props();
|
|
60
|
+
|
|
61
|
+
$effect(() => {
|
|
62
|
+
table.refresh({
|
|
63
|
+
data,
|
|
64
|
+
plugins
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
setTableContext(table);
|
|
69
|
+
const treeIndicatorId = crypto.randomUUID();
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<VirtualList
|
|
73
|
+
data={table.results.entries}
|
|
74
|
+
class={['border-transparent', clazz, 'flex flex-col overflow-hidden']}
|
|
75
|
+
bind:b_scrollTop={table.scrollTop}
|
|
76
|
+
{rowHeight}
|
|
77
|
+
>
|
|
78
|
+
{#snippet header()}
|
|
79
|
+
<div
|
|
80
|
+
class={twMerge(
|
|
81
|
+
clsx(
|
|
82
|
+
'flex w-fit min-w-full flex-row gap-2 border-b border-inherit pr-4 pl-2',
|
|
83
|
+
headerClass
|
|
84
|
+
)
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{#each table.columns as column (column.id)}
|
|
88
|
+
<ColumnHead {column}>
|
|
89
|
+
{#if typeof column.header === 'function'}
|
|
90
|
+
{@render column.header()}
|
|
91
|
+
{:else}
|
|
92
|
+
<div
|
|
93
|
+
class="flex grow flex-row items-center justify-start gap-4 py-2 text-start select-none"
|
|
94
|
+
>
|
|
95
|
+
{column.header}
|
|
96
|
+
</div>
|
|
97
|
+
{/if}
|
|
98
|
+
</ColumnHead>
|
|
99
|
+
{/each}
|
|
100
|
+
</div>
|
|
101
|
+
{/snippet}
|
|
102
|
+
{#snippet children({ row: { node, id, nestingLevel }, index })}
|
|
103
|
+
<Row
|
|
104
|
+
onclick={onclick ? () => onclick(node) : undefined}
|
|
105
|
+
href={href?.(node)}
|
|
106
|
+
class={rowClass}
|
|
107
|
+
>
|
|
108
|
+
{@render firstColumn?.({ row: node })}
|
|
109
|
+
<Column
|
|
110
|
+
id={treeIndicatorId}
|
|
111
|
+
resizable={false}
|
|
112
|
+
header=""
|
|
113
|
+
onclick={() => {
|
|
114
|
+
table.toggleExpansion(node.id);
|
|
115
|
+
}}
|
|
116
|
+
ignoreWidth={table.results.someHaveChildren}
|
|
117
|
+
width={table.results.someHaveChildren ? 24 : 0}
|
|
118
|
+
minWidth={0}
|
|
119
|
+
>
|
|
120
|
+
<div
|
|
121
|
+
class="flex h-full items-center justify-end"
|
|
122
|
+
style="width: calc(var(--spacing) * {nestingLevel * 4} + 24px);"
|
|
123
|
+
>
|
|
124
|
+
{#if node.children}
|
|
125
|
+
<ChevronRight
|
|
126
|
+
class={[
|
|
127
|
+
'ml-auto aspect-square shrink-0 transition-transform duration-100',
|
|
128
|
+
table.expanded.has(id) && 'rotate-90'
|
|
129
|
+
]}
|
|
130
|
+
/>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
</Column>
|
|
134
|
+
{@render passedChildren?.({ row: node, nestingLevel, index })}
|
|
135
|
+
</Row>
|
|
136
|
+
{/snippet}
|
|
137
|
+
</VirtualList>
|