@ims360/svelte-ivory 0.1.15 → 0.2.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 +0 -5
- package/dist/components/ai/AiMessage.svelte +2 -3
- package/dist/components/ai/AiMessage.svelte.d.ts.map +1 -1
- package/dist/components/ai/Chat.svelte +2 -3
- package/dist/components/ai/Chat.svelte.d.ts.map +1 -1
- package/dist/components/ai/Markdown.svelte +4 -7
- package/dist/components/ai/Markdown.svelte.d.ts.map +1 -1
- package/dist/components/ai/UserMessage.svelte +2 -3
- package/dist/components/ai/UserMessage.svelte.d.ts.map +1 -1
- package/dist/components/basic/checkbox/Checkbox.svelte +13 -11
- package/dist/components/basic/checkbox/Checkbox.svelte.d.ts.map +1 -1
- package/dist/components/basic/toggle/Toggle.svelte +5 -8
- package/dist/components/basic/toggle/Toggle.svelte.d.ts.map +1 -1
- package/dist/components/inputs/CheckboxInput.svelte +39 -0
- package/dist/components/inputs/CheckboxInput.svelte.d.ts +12 -0
- package/dist/components/inputs/CheckboxInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/ColorInput.svelte +25 -0
- package/dist/components/inputs/ColorInput.svelte.d.ts +5 -0
- package/dist/components/inputs/ColorInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/DateInput.svelte +11 -0
- package/dist/components/inputs/DateInput.svelte.d.ts +5 -0
- package/dist/components/inputs/DateInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/EmailInput.svelte +16 -0
- package/dist/components/inputs/EmailInput.svelte.d.ts +5 -0
- package/dist/components/inputs/EmailInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/FileInput.svelte +131 -0
- package/dist/components/inputs/FileInput.svelte.d.ts +11 -0
- package/dist/components/inputs/FileInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/Input.svelte +101 -0
- package/dist/components/inputs/Input.svelte.d.ts +48 -0
- package/dist/components/inputs/Input.svelte.d.ts.map +1 -0
- package/dist/components/inputs/NumberInput.svelte +17 -0
- package/dist/components/inputs/NumberInput.svelte.d.ts +10 -0
- package/dist/components/inputs/NumberInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/PasswordCreateInput.svelte +60 -0
- package/dist/components/inputs/PasswordCreateInput.svelte.d.ts +12 -0
- package/dist/components/inputs/PasswordCreateInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/PasswordInput.svelte +27 -0
- package/dist/components/inputs/PasswordInput.svelte.d.ts +10 -0
- package/dist/components/inputs/PasswordInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/TextInput.svelte +16 -0
- package/dist/components/inputs/TextInput.svelte.d.ts +7 -0
- package/dist/components/inputs/TextInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/ToggleInput.svelte +24 -0
- package/dist/components/inputs/ToggleInput.svelte.d.ts +5 -0
- package/dist/components/inputs/ToggleInput.svelte.d.ts.map +1 -0
- package/dist/components/inputs/index.d.ts +13 -0
- package/dist/components/inputs/index.d.ts.map +1 -1
- package/dist/components/inputs/index.js +13 -0
- package/dist/components/inputs/issues/FormIssues.svelte +35 -0
- package/dist/components/inputs/issues/FormIssues.svelte.d.ts +10 -0
- package/dist/components/inputs/issues/FormIssues.svelte.d.ts.map +1 -0
- package/dist/components/inputs/select/Select.svelte +69 -0
- package/dist/components/inputs/select/Select.svelte.d.ts +15 -0
- package/dist/components/inputs/select/Select.svelte.d.ts.map +1 -0
- package/dist/components/inputs/select/SelectOption.svelte +34 -0
- package/dist/components/inputs/select/SelectOption.svelte.d.ts +11 -0
- package/dist/components/inputs/select/SelectOption.svelte.d.ts.map +1 -0
- package/dist/components/layout/Heading.svelte +5 -4
- package/dist/components/layout/Heading.svelte.d.ts.map +1 -1
- package/dist/components/layout/dialog/Dialog.svelte +5 -8
- package/dist/components/layout/dialog/Dialog.svelte.d.ts.map +1 -1
- package/dist/components/layout/drawer/Drawer.svelte +7 -10
- package/dist/components/layout/drawer/Drawer.svelte.d.ts.map +1 -1
- package/dist/components/layout/index.d.ts +1 -2
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +0 -1
- package/dist/components/layout/modal/Modal.svelte +10 -15
- package/dist/components/layout/modal/Modal.svelte.d.ts.map +1 -1
- package/dist/components/layout/popover/Popover.svelte +96 -79
- package/dist/components/layout/popover/Popover.svelte.d.ts +8 -22
- package/dist/components/layout/popover/Popover.svelte.d.ts.map +1 -1
- package/dist/components/layout/tabs/Tab.svelte +5 -8
- package/dist/components/layout/tabs/Tab.svelte.d.ts.map +1 -1
- package/dist/components/layout/tooltip/Tooltip.svelte +4 -7
- package/dist/components/layout/tooltip/Tooltip.svelte.d.ts.map +1 -1
- package/dist/components/table/Column.svelte +7 -10
- package/dist/components/table/Column.svelte.d.ts.map +1 -1
- package/dist/components/table/ColumnHead.svelte +2 -2
- package/dist/components/table/Table.svelte +5 -8
- package/dist/components/table/Table.svelte.d.ts.map +1 -1
- package/dist/components/table/VirtualList.svelte +3 -8
- package/dist/components/table/VirtualList.svelte.d.ts.map +1 -1
- package/dist/theme.svelte.d.ts +18 -0
- package/dist/theme.svelte.d.ts.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/{actions → attachments}/clickOutside.d.ts +2 -4
- package/dist/utils/attachments/clickOutside.d.ts.map +1 -0
- package/dist/utils/{actions → attachments}/clickOutside.js +4 -7
- package/dist/utils/attachments/focusTrap.d.ts +3 -0
- package/dist/utils/attachments/focusTrap.d.ts.map +1 -0
- package/dist/utils/{actions → attachments}/focusTrap.js +5 -14
- package/dist/utils/{actions → attachments}/index.d.ts +0 -1
- package/dist/utils/attachments/index.d.ts.map +1 -0
- package/dist/utils/{actions → attachments}/index.js +0 -1
- package/dist/utils/attachments/resize.d.ts +6 -0
- package/dist/utils/attachments/resize.d.ts.map +1 -0
- package/dist/utils/{actions → attachments}/resize.js +3 -5
- package/dist/utils/attachments/shortcut.svelte.d.ts.map +1 -0
- package/dist/utils/attachments/visible.d.ts +6 -0
- package/dist/utils/attachments/visible.d.ts.map +1 -0
- package/dist/utils/attachments/visible.js +12 -0
- package/dist/utils/merge.d.ts +4 -0
- package/dist/utils/merge.d.ts.map +1 -0
- package/dist/utils/merge.js +6 -0
- package/package.json +7 -5
- package/src/lib/components/ai/AiMessage.svelte +2 -3
- package/src/lib/components/ai/Chat.svelte +2 -3
- package/src/lib/components/ai/Markdown.svelte +4 -7
- package/src/lib/components/ai/UserMessage.svelte +2 -3
- package/src/lib/components/basic/checkbox/Checkbox.svelte +13 -11
- package/src/lib/components/basic/toggle/Toggle.svelte +5 -8
- package/src/lib/components/inputs/CheckboxInput.svelte +39 -0
- package/src/lib/components/inputs/ColorInput.svelte +25 -0
- package/src/lib/components/inputs/DateInput.svelte +11 -0
- package/src/lib/components/inputs/EmailInput.svelte +16 -0
- package/src/lib/components/inputs/FileInput.svelte +131 -0
- package/src/lib/components/inputs/Input.svelte +101 -0
- package/src/lib/components/inputs/NumberInput.svelte +17 -0
- package/src/lib/components/inputs/PasswordCreateInput.svelte +60 -0
- package/src/lib/components/inputs/PasswordInput.svelte +27 -0
- package/src/lib/components/inputs/TextInput.svelte +16 -0
- package/src/lib/components/inputs/ToggleInput.svelte +24 -0
- package/src/lib/components/inputs/index.ts +17 -0
- package/src/lib/components/inputs/issues/FormIssues.svelte +35 -0
- package/src/lib/components/inputs/select/Select.svelte +69 -0
- package/src/lib/components/inputs/select/SelectOption.svelte +34 -0
- package/src/lib/components/layout/Heading.svelte +5 -4
- package/src/lib/components/layout/dialog/Dialog.svelte +5 -8
- package/src/lib/components/layout/drawer/Drawer.svelte +7 -10
- package/src/lib/components/layout/index.ts +1 -5
- package/src/lib/components/layout/modal/Modal.svelte +10 -15
- package/src/lib/components/layout/popover/Popover.svelte +96 -79
- package/src/lib/components/layout/tabs/Tab.svelte +5 -8
- package/src/lib/components/layout/tooltip/Tooltip.svelte +4 -7
- package/src/lib/components/table/Column.svelte +7 -10
- package/src/lib/components/table/ColumnHead.svelte +2 -2
- package/src/lib/components/table/Table.svelte +5 -8
- package/src/lib/components/table/VirtualList.svelte +3 -8
- package/src/lib/theme.svelte.ts +18 -0
- package/src/lib/types.ts +3 -2
- package/src/lib/utils/attachments/clickOutside.ts +36 -0
- package/src/lib/utils/attachments/focusTrap.ts +67 -0
- package/src/lib/utils/{actions → attachments}/index.ts +0 -1
- package/src/lib/utils/attachments/resize.ts +38 -0
- package/src/lib/utils/attachments/visible.ts +22 -0
- package/src/lib/utils/merge.ts +7 -0
- package/dist/components/layout/portal/Portal.svelte +0 -39
- package/dist/components/layout/portal/Portal.svelte.d.ts +0 -21
- package/dist/components/layout/portal/Portal.svelte.d.ts.map +0 -1
- package/dist/components/layout/portal/index.d.ts +0 -6
- package/dist/components/layout/portal/index.d.ts.map +0 -1
- package/dist/components/layout/portal/index.js +0 -5
- package/dist/utils/actions/clickOutside.d.ts.map +0 -1
- package/dist/utils/actions/focusTrap.d.ts +0 -5
- package/dist/utils/actions/focusTrap.d.ts.map +0 -1
- package/dist/utils/actions/index.d.ts.map +0 -1
- package/dist/utils/actions/portal.d.ts +0 -10
- package/dist/utils/actions/portal.d.ts.map +0 -1
- package/dist/utils/actions/portal.js +0 -39
- package/dist/utils/actions/resize.d.ts +0 -6
- package/dist/utils/actions/resize.d.ts.map +0 -1
- package/dist/utils/actions/shortcut.svelte.d.ts.map +0 -1
- package/dist/utils/actions/visible.d.ts +0 -6
- package/dist/utils/actions/visible.d.ts.map +0 -1
- package/dist/utils/actions/visible.js +0 -14
- package/src/lib/components/layout/portal/Portal.svelte +0 -39
- package/src/lib/components/layout/portal/index.ts +0 -7
- package/src/lib/utils/actions/clickOutside.ts +0 -38
- package/src/lib/utils/actions/focusTrap.ts +0 -65
- package/src/lib/utils/actions/portal.ts +0 -43
- package/src/lib/utils/actions/resize.ts +0 -35
- package/src/lib/utils/actions/visible.ts +0 -28
- /package/dist/utils/{actions → attachments}/shortcut.svelte.d.ts +0 -0
- /package/dist/utils/{actions → attachments}/shortcut.svelte.js +0 -0
- /package/src/lib/utils/{actions → attachments}/shortcut.svelte.ts +0 -0
|
@@ -1,113 +1,130 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
import { browser } from '$app/environment';
|
|
3
2
|
import { theme } from '$lib/theme.svelte';
|
|
4
3
|
import type { IvoryComponent } from '$lib/types';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import { twMerge } from 'tailwind-merge';
|
|
15
|
-
import { clickOutside } from '../../../utils/actions/clickOutside';
|
|
16
|
-
import { Dialog } from '../dialog';
|
|
17
|
-
|
|
18
|
-
/** Possible placements for the popover */
|
|
4
|
+
import { pseudoRandomId } from '$lib/utils/functions';
|
|
5
|
+
import { merge } from '$lib/utils/merge';
|
|
6
|
+
import { type ComputePositionConfig } from '@floating-ui/dom';
|
|
7
|
+
import polyfill from '@oddbird/css-anchor-positioning/fn';
|
|
8
|
+
|
|
9
|
+
// ... (Your existing types remain unchanged)
|
|
10
|
+
type Alignment = 'start' | 'end';
|
|
11
|
+
type Side = 'top' | 'bottom' | 'left' | 'right';
|
|
12
|
+
type AlignedPlacement = `${Side}-${Alignment}` | Side;
|
|
19
13
|
export type PopoverPlacement = ComputePositionConfig['placement'];
|
|
20
14
|
|
|
15
|
+
const ANCHOR_STYLES: Record<string, string> = {
|
|
16
|
+
// Bottom Placements
|
|
17
|
+
'bottom-start': 'top: anchor(bottom); left: anchor(left);',
|
|
18
|
+
bottom: 'top: anchor(bottom); left: anchor(center); translate: -50% 0;',
|
|
19
|
+
'bottom-end': 'top: anchor(bottom); right: anchor(right);',
|
|
20
|
+
|
|
21
|
+
// Top Placements
|
|
22
|
+
'top-start': 'bottom: anchor(top); left: anchor(left);',
|
|
23
|
+
top: 'bottom: anchor(top); left: anchor(center); translate: -50% 0;',
|
|
24
|
+
'top-end': 'bottom: anchor(top); right: anchor(right);',
|
|
25
|
+
|
|
26
|
+
// Left Placements
|
|
27
|
+
'left-start': 'right: anchor(left); top: anchor(top);',
|
|
28
|
+
left: 'right: anchor(left); top: anchor(center); translate: 0 -50%;',
|
|
29
|
+
'left-end': 'right: anchor(left); bottom: anchor(bottom);',
|
|
30
|
+
|
|
31
|
+
// Right Placements
|
|
32
|
+
'right-start': 'left: anchor(right); top: anchor(top);',
|
|
33
|
+
right: 'left: anchor(right); top: anchor(center); translate: 0 -50%;',
|
|
34
|
+
'right-end': 'left: anchor(right); bottom: anchor(bottom);'
|
|
35
|
+
};
|
|
36
|
+
|
|
21
37
|
export interface PopoverProps extends IvoryComponent<HTMLDivElement> {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Where the popover should be positioned relative to the target.
|
|
26
|
-
*
|
|
27
|
-
* default: `bottom-start`
|
|
28
|
-
*/
|
|
29
|
-
placement?: PopoverPlacement;
|
|
30
|
-
/**
|
|
31
|
-
* Callback that is called when the user clicks outside the popover or the target element.
|
|
32
|
-
*/
|
|
33
|
-
onClickOutside?: (e: MouseEvent) => void;
|
|
34
|
-
/**
|
|
35
|
-
* Whether to place the popover automatically
|
|
36
|
-
*
|
|
37
|
-
* [Further reading](https://floating-ui.com/docs/autoPlacement)
|
|
38
|
-
*/
|
|
38
|
+
target: HTMLElement | undefined;
|
|
39
|
+
placement?: AlignedPlacement;
|
|
39
40
|
autoplacement?: boolean;
|
|
40
41
|
}
|
|
41
42
|
</script>
|
|
42
43
|
|
|
43
44
|
<script lang="ts">
|
|
45
|
+
import { onMount, tick } from 'svelte';
|
|
46
|
+
|
|
44
47
|
let {
|
|
45
48
|
class: clazz,
|
|
46
49
|
style: externalStyle,
|
|
47
50
|
target,
|
|
48
51
|
placement = 'bottom-start',
|
|
49
|
-
onClickOutside = close,
|
|
50
52
|
children,
|
|
51
|
-
|
|
53
|
+
popover = 'auto',
|
|
54
|
+
id = pseudoRandomId(),
|
|
52
55
|
...rest
|
|
53
56
|
}: PopoverProps = $props();
|
|
54
57
|
|
|
55
|
-
let
|
|
56
|
-
let
|
|
58
|
+
let popoverEl: HTMLDivElement | undefined = $state();
|
|
59
|
+
let currentlyOpen = $state(false);
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
// 1. Load Polyfill
|
|
62
|
+
// We import the 'fn' version to manually control execution,
|
|
63
|
+
// ensuring it runs after the DOM is ready.
|
|
64
|
+
onMount(async () => {
|
|
65
|
+
if (!CSS.supports('position-anchor', '--foo')) {
|
|
66
|
+
await polyfill();
|
|
67
|
+
console.log('loaded polyfill');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
const anchorName = $derived(`--anchor-${id}`);
|
|
72
|
+
|
|
73
|
+
// 2. Anchor Association
|
|
74
|
+
$effect(() => {
|
|
75
|
+
if (!target) return;
|
|
76
|
+
const currentStyle = target.getAttribute('style') || '';
|
|
77
|
+
if (!currentStyle.includes(anchorName)) {
|
|
78
|
+
target.setAttribute('style', `anchor-name: ${anchorName}; ${currentStyle}`);
|
|
79
|
+
}
|
|
80
|
+
tick().then(() => polyfill());
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
$effect(() => {
|
|
84
|
+
if (!popoverEl) return;
|
|
85
|
+
|
|
86
|
+
// Use the explicit coordinates instead of position-area
|
|
87
|
+
const coords = ANCHOR_STYLES[placement] ?? ANCHOR_STYLES['bottom-start'];
|
|
88
|
+
|
|
89
|
+
// Important: We ensure position-area is NOT present
|
|
90
|
+
const polyfillStyles = `
|
|
91
|
+
position-anchor: ${anchorName};
|
|
92
|
+
${coords}
|
|
93
|
+
`;
|
|
68
94
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
95
|
+
const combinedStyle = `${externalStyle ? externalStyle + '; ' : ''}${polyfillStyles}`;
|
|
96
|
+
popoverEl.setAttribute('style', combinedStyle);
|
|
97
|
+
|
|
98
|
+
tick().then(() => polyfill());
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export async function close() {
|
|
102
|
+
popoverEl?.hidePopover();
|
|
73
103
|
}
|
|
74
104
|
|
|
75
|
-
export function open() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
cleanup = autoUpdate(target, popover, () => postion(true));
|
|
105
|
+
export async function open() {
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
(popoverEl?.showPopover as any)();
|
|
79
108
|
}
|
|
80
109
|
|
|
81
|
-
export function toggle() {
|
|
82
|
-
|
|
110
|
+
export async function toggle() {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
112
|
+
(popoverEl?.togglePopover as any)();
|
|
83
113
|
}
|
|
84
114
|
|
|
85
115
|
export function isOpen() {
|
|
86
116
|
return currentlyOpen;
|
|
87
117
|
}
|
|
88
|
-
|
|
89
|
-
// TODO: this is kinda hacky
|
|
90
|
-
$effect(() => {
|
|
91
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
92
|
-
[popover, target];
|
|
93
|
-
postion(currentlyOpen);
|
|
94
|
-
});
|
|
95
118
|
</script>
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{...rest}
|
|
109
|
-
>
|
|
110
|
-
{@render children?.()}
|
|
111
|
-
</div>
|
|
112
|
-
</Dialog>
|
|
113
|
-
{/if}
|
|
120
|
+
<div
|
|
121
|
+
{popover}
|
|
122
|
+
class={merge('absolute m-0', theme.current.popover?.class, clazz)}
|
|
123
|
+
bind:this={popoverEl}
|
|
124
|
+
ontoggle={(e) => {
|
|
125
|
+
currentlyOpen = e.newState === 'open';
|
|
126
|
+
}}
|
|
127
|
+
{...rest}
|
|
128
|
+
>
|
|
129
|
+
{@render children?.()}
|
|
130
|
+
</div>
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
import { theme } from '$lib/theme.svelte';
|
|
4
4
|
import type { IvoryComponent } from '$lib/types';
|
|
5
5
|
import { pseudoRandomId } from '$lib/utils/functions/index';
|
|
6
|
-
import
|
|
6
|
+
import { merge } from '$lib/utils/merge';
|
|
7
7
|
import { onMount, type Snippet } from 'svelte';
|
|
8
8
|
import type { ClassValue } from 'svelte/elements';
|
|
9
|
-
import { twMerge } from 'tailwind-merge';
|
|
10
9
|
import { getTabContext } from './Tabs.svelte';
|
|
11
10
|
|
|
12
11
|
export interface TabProps extends Omit<IvoryComponent<HTMLElement>, 'children'> {
|
|
@@ -52,12 +51,10 @@
|
|
|
52
51
|
|
|
53
52
|
<svelte:element
|
|
54
53
|
this={href ? 'a' : 'button'}
|
|
55
|
-
class={
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
typeof clazz === 'function' ? clazz(selected) : clazz
|
|
60
|
-
)
|
|
54
|
+
class={merge(
|
|
55
|
+
'btn flex h-fit w-fit shrink-0 items-center justify-center px-0 text-xl font-bold select-none',
|
|
56
|
+
theme.current.tabs?.tab?.class?.(selected),
|
|
57
|
+
typeof clazz === 'function' ? clazz(selected) : clazz
|
|
61
58
|
)}
|
|
62
59
|
onclick={href
|
|
63
60
|
? undefined
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
import
|
|
2
|
+
import { merge } from '$lib/utils/merge';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
import type { ClassValue, MouseEventHandler } from 'svelte/elements';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
5
|
import Popover, { type PopoverPlacement } from '../popover/Popover.svelte';
|
|
7
6
|
|
|
8
7
|
export interface TooltipProps {
|
|
@@ -84,11 +83,9 @@
|
|
|
84
83
|
bind:this={popover}
|
|
85
84
|
{target}
|
|
86
85
|
{placement}
|
|
87
|
-
class={
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
tooltipClass
|
|
91
|
-
)
|
|
86
|
+
class={merge(
|
|
87
|
+
'bg-surface-50-950 max-w-96 -translate-y-0.5 rounded px-4 py-1 shadow-lg',
|
|
88
|
+
tooltipClass
|
|
92
89
|
)}
|
|
93
90
|
>
|
|
94
91
|
{#if typeof tooltip === 'string'}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import { theme } from '$lib/theme.svelte';
|
|
3
|
-
import
|
|
3
|
+
import { merge } from '$lib/utils/merge';
|
|
4
4
|
import { type Snippet } from 'svelte';
|
|
5
5
|
import type { ClassValue } from 'svelte/elements';
|
|
6
|
-
import { twMerge } from 'tailwind-merge';
|
|
7
6
|
import type { ColumnConfig } from './columnController.svelte';
|
|
8
7
|
import { getRowContext } from './Row.svelte';
|
|
9
8
|
import { getTableContext } from './Table.svelte';
|
|
@@ -67,14 +66,12 @@
|
|
|
67
66
|
href={!allowClicking ? rowContext.href : undefined}
|
|
68
67
|
type={allowClicking ? 'button' : undefined}
|
|
69
68
|
style={ignoreWidth ? '' : `width: ${widthStyle}`}
|
|
70
|
-
class={
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
])
|
|
77
|
-
)}
|
|
69
|
+
class={merge([
|
|
70
|
+
'box-border flex h-full shrink-0 flex-row items-center justify-start gap-1 truncate',
|
|
71
|
+
column.width !== 0 && 'border-r-[calc(var(--spacing)*2)] border-transparent',
|
|
72
|
+
theme.current.table?.column?.class,
|
|
73
|
+
clazz
|
|
74
|
+
])}
|
|
78
75
|
>
|
|
79
76
|
{@render children()}
|
|
80
77
|
</svelte:element>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import { getContext, setContext, type Snippet } from 'svelte';
|
|
3
|
-
import { resize } from '../../utils/
|
|
3
|
+
import { resize } from '../../utils/attachments';
|
|
4
4
|
import type { ColumnController } from './columnController.svelte';
|
|
5
5
|
|
|
6
6
|
const CONTEXT = {};
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
{@render children?.()}
|
|
54
54
|
{#if column.resizable}
|
|
55
55
|
<button
|
|
56
|
+
{@attach resize({ resized: onResize, dragging: onDragging })}
|
|
56
57
|
type="button"
|
|
57
58
|
class={[
|
|
58
59
|
'relative ml-auto h-full w-4 shrink-0 cursor-col-resize bg-inherit after:absolute after:top-0 after:right-2 after:h-full after:w-px',
|
|
59
60
|
dragging ? 'after:bg-primary-400-600' : 'group-hover:after:bg-surface-300-700'
|
|
60
61
|
]}
|
|
61
|
-
use:resize={{ resized: onResize, dragging: onDragging }}
|
|
62
62
|
onmouseenter={onHoverStart}
|
|
63
63
|
onmouseleave={onHoverEnd}
|
|
64
64
|
onfocusin={onHoverStart}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
+
import { merge } from '$lib/utils/merge';
|
|
2
3
|
import { ChevronRight } from '@lucide/svelte';
|
|
3
|
-
import clsx from 'clsx';
|
|
4
4
|
import { getContext, setContext, untrack, type Snippet } from 'svelte';
|
|
5
5
|
import type { ClassValue } from 'svelte/elements';
|
|
6
6
|
import { SvelteSet } from 'svelte/reactivity';
|
|
7
|
-
import { twMerge } from 'tailwind-merge';
|
|
8
7
|
import ColumnComponent from './Column.svelte';
|
|
9
8
|
import { ColumnController, type ColumnConfig } from './columnController.svelte';
|
|
10
9
|
import ColumnHead from './ColumnHead.svelte';
|
|
@@ -200,17 +199,15 @@
|
|
|
200
199
|
bind:this={list}
|
|
201
200
|
bind:b_scrollTop
|
|
202
201
|
data={results.entries}
|
|
203
|
-
class={
|
|
202
|
+
class={merge(['flex flex-col overflow-hidden border-transparent', clazz])}
|
|
204
203
|
rowClass={['pl-2 pr-4', rowClass]}
|
|
205
204
|
{rowHeight}
|
|
206
205
|
>
|
|
207
206
|
{#snippet header()}
|
|
208
207
|
<div
|
|
209
|
-
class={
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
headerClass
|
|
213
|
-
)
|
|
208
|
+
class={merge(
|
|
209
|
+
'flex w-fit min-w-full flex-row border-b border-inherit pr-4 pl-2',
|
|
210
|
+
headerClass
|
|
214
211
|
)}
|
|
215
212
|
>
|
|
216
213
|
{#if results.someHaveChildren}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends { id: string }">
|
|
2
|
-
import
|
|
2
|
+
import { merge } from '$lib/utils/merge';
|
|
3
3
|
import { onMount, tick, untrack, type Snippet } from 'svelte';
|
|
4
4
|
import type { ClassValue } from 'svelte/elements';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
5
|
|
|
7
6
|
type Props<T> = {
|
|
8
7
|
class?: ClassValue;
|
|
@@ -27,7 +26,7 @@
|
|
|
27
26
|
}: Props<T> = $props();
|
|
28
27
|
|
|
29
28
|
const finalRowClass = $derived(
|
|
30
|
-
|
|
29
|
+
merge(['flex w-full shrink-0 grow flex-row items-center overflow-hidden', rowClass])
|
|
31
30
|
);
|
|
32
31
|
|
|
33
32
|
let viewportReactivity = $state(0);
|
|
@@ -93,11 +92,7 @@
|
|
|
93
92
|
});
|
|
94
93
|
</script>
|
|
95
94
|
|
|
96
|
-
<div
|
|
97
|
-
class={twMerge(
|
|
98
|
-
clsx(['scroll relative flex grow flex-col overflow-hidden border-inherit', clazz])
|
|
99
|
-
)}
|
|
100
|
-
>
|
|
95
|
+
<div class={merge(['scroll relative flex grow flex-col overflow-hidden border-inherit', clazz])}>
|
|
101
96
|
{#if header}
|
|
102
97
|
<div class="h-fit w-full border-inherit">
|
|
103
98
|
<div
|
package/src/lib/theme.svelte.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import type { RemoteFormIssue } from '@sveltejs/kit';
|
|
1
2
|
import type { ClassValue } from 'svelte/elements';
|
|
2
3
|
|
|
3
4
|
export interface Theme {
|
|
5
|
+
checkbox?: {
|
|
6
|
+
class?: (checked: boolean, partial: boolean) => ClassValue;
|
|
7
|
+
};
|
|
4
8
|
heading?: {
|
|
5
9
|
class?: ClassValue;
|
|
6
10
|
};
|
|
@@ -27,6 +31,20 @@ export interface Theme {
|
|
|
27
31
|
popover?: {
|
|
28
32
|
class?: ClassValue;
|
|
29
33
|
};
|
|
34
|
+
input?: {
|
|
35
|
+
outerClass?: ClassValue;
|
|
36
|
+
class?: (value: boolean, issues?: boolean) => ClassValue;
|
|
37
|
+
label?: {
|
|
38
|
+
class?: (value: boolean, issues?: boolean) => ClassValue;
|
|
39
|
+
};
|
|
40
|
+
issues?: {
|
|
41
|
+
issue?: {
|
|
42
|
+
label?: (issue: RemoteFormIssue) => string;
|
|
43
|
+
class?: ClassValue;
|
|
44
|
+
};
|
|
45
|
+
class?: ClassValue;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
function createTheme() {
|
package/src/lib/types.ts
CHANGED
|
@@ -2,8 +2,9 @@ import type { HTMLAttributes } from 'svelte/elements';
|
|
|
2
2
|
import type { TransitionConfig } from 'svelte/transition';
|
|
3
3
|
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
5
|
-
export interface IvoryComponent<
|
|
6
|
-
extends
|
|
5
|
+
export interface IvoryComponent<
|
|
6
|
+
RootElement extends EventTarget
|
|
7
|
+
> extends HTMLAttributes<RootElement> {}
|
|
7
8
|
|
|
8
9
|
export interface TransitionProps {
|
|
9
10
|
inTransition?: (node: Element) => TransitionConfig;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
|
|
3
|
+
interface ClickOutsideParams {
|
|
4
|
+
/** Callback to be called when clicking outside of node */
|
|
5
|
+
callback: (e: MouseEvent) => void;
|
|
6
|
+
/** Callback is also not fired if the click target is inside this element */
|
|
7
|
+
target?: Element;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const clickOutside =
|
|
11
|
+
(params: ((e: MouseEvent) => void) | ClickOutsideParams): Attachment =>
|
|
12
|
+
(node) => {
|
|
13
|
+
function handleClick(event: MouseEvent) {
|
|
14
|
+
if (
|
|
15
|
+
!(event.target instanceof Node) ||
|
|
16
|
+
!node ||
|
|
17
|
+
node.contains(event.target) ||
|
|
18
|
+
event.defaultPrevented
|
|
19
|
+
)
|
|
20
|
+
return;
|
|
21
|
+
|
|
22
|
+
if (typeof params === 'function') {
|
|
23
|
+
params(event);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (params.target?.contains(event.target)) return;
|
|
28
|
+
params.callback(event);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
document.addEventListener('click', handleClick, true);
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
document.removeEventListener('click', handleClick, true);
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
|
|
3
|
+
export const focusTrap =
|
|
4
|
+
(enabled: boolean): Attachment =>
|
|
5
|
+
(node) => {
|
|
6
|
+
const elementWhitelist =
|
|
7
|
+
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
|
|
8
|
+
let firstElement: HTMLElement;
|
|
9
|
+
let lastElement: HTMLElement;
|
|
10
|
+
|
|
11
|
+
function onFirstElemKeydown(e: KeyboardEvent): void {
|
|
12
|
+
if (e.shiftKey && e.code === 'Tab') {
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
lastElement.focus();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function onLastElemKeydown(e: KeyboardEvent): void {
|
|
19
|
+
if (!e.shiftKey && e.code === 'Tab') {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
firstElement.focus();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function scanElements(fromObserver: boolean) {
|
|
26
|
+
if (enabled === false) return;
|
|
27
|
+
const focusableElems: HTMLElement[] = Array.from(
|
|
28
|
+
node.querySelectorAll(elementWhitelist)
|
|
29
|
+
);
|
|
30
|
+
if (focusableElems.length) {
|
|
31
|
+
// Set first/last focusable elements
|
|
32
|
+
firstElement = focusableElems[0];
|
|
33
|
+
lastElement = focusableElems[focusableElems.length - 1];
|
|
34
|
+
// Auto-focus first focusable element only when not called from mutation observer
|
|
35
|
+
if (!fromObserver) firstElement.focus();
|
|
36
|
+
// Listen for keydown on first & last element
|
|
37
|
+
firstElement.addEventListener('keydown', onFirstElemKeydown);
|
|
38
|
+
lastElement.addEventListener('keydown', onLastElemKeydown);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
scanElements(false);
|
|
42
|
+
|
|
43
|
+
function cleanUp(): void {
|
|
44
|
+
if (firstElement) firstElement.removeEventListener('keydown', onFirstElemKeydown);
|
|
45
|
+
if (lastElement) lastElement.removeEventListener('keydown', onLastElemKeydown);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Re-determine focusable elements when mutations are observed
|
|
49
|
+
const onObservationChange = (
|
|
50
|
+
mutationRecords: MutationRecord[],
|
|
51
|
+
observer: MutationObserver
|
|
52
|
+
) => {
|
|
53
|
+
if (mutationRecords.length) {
|
|
54
|
+
cleanUp();
|
|
55
|
+
scanElements(true);
|
|
56
|
+
}
|
|
57
|
+
return observer;
|
|
58
|
+
};
|
|
59
|
+
const observer = new MutationObserver(onObservationChange);
|
|
60
|
+
observer.observe(node, { childList: true, subtree: true });
|
|
61
|
+
|
|
62
|
+
// Lifecycle
|
|
63
|
+
return () => {
|
|
64
|
+
cleanUp();
|
|
65
|
+
observer.disconnect();
|
|
66
|
+
};
|
|
67
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
|
|
3
|
+
export const resize =
|
|
4
|
+
({
|
|
5
|
+
resized,
|
|
6
|
+
dragging
|
|
7
|
+
}: {
|
|
8
|
+
resized: (mouseX: number) => void;
|
|
9
|
+
dragging: (dragging: boolean) => void;
|
|
10
|
+
}): Attachment =>
|
|
11
|
+
(node) => {
|
|
12
|
+
const onResize = (e: PointerEvent) => {
|
|
13
|
+
resized(e.clientX);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const onResizeEnd = () => {
|
|
17
|
+
dragging(false);
|
|
18
|
+
window.removeEventListener('pointermove', onResize);
|
|
19
|
+
window.removeEventListener('pointerup', onResizeEnd);
|
|
20
|
+
window.removeEventListener('pointercancel', onResizeEnd);
|
|
21
|
+
|
|
22
|
+
document.getElementsByTagName('html')[0].style.userSelect = '';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const onResizeStart = () => {
|
|
26
|
+
dragging(true);
|
|
27
|
+
window.addEventListener('pointermove', onResize);
|
|
28
|
+
window.addEventListener('pointerup', onResizeEnd);
|
|
29
|
+
window.addEventListener('pointercancel', onResizeEnd);
|
|
30
|
+
|
|
31
|
+
document.getElementsByTagName('html')[0].style.userSelect = 'none';
|
|
32
|
+
};
|
|
33
|
+
node.addEventListener('pointerdown', onResizeStart);
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
node.removeEventListener('pointerdown', onResizeStart);
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
|
|
3
|
+
export const onFirstVisible =
|
|
4
|
+
(params: { callback: () => void; options?: Partial<IntersectionObserverInit> }): Attachment =>
|
|
5
|
+
(node) => {
|
|
6
|
+
const observer = new IntersectionObserver(
|
|
7
|
+
(e) => {
|
|
8
|
+
if (!e[0].isIntersecting) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
params.callback();
|
|
13
|
+
},
|
|
14
|
+
{ root: null, threshold: 1, ...params.options }
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
observer.observe(node);
|
|
18
|
+
|
|
19
|
+
return () => {
|
|
20
|
+
observer.disconnect();
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { IvoryComponent } from '../../../types';
|
|
3
|
-
import type { Snippet } from 'svelte';
|
|
4
|
-
import { portal } from '../../../utils/actions/index';
|
|
5
|
-
|
|
6
|
-
export interface PortalProps extends IvoryComponent<HTMLDivElement> {
|
|
7
|
-
children: Snippet;
|
|
8
|
-
target?: string | HTMLElement;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface PortalConfig {
|
|
12
|
-
defaultTarget: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const defaultConfig: PortalConfig = {
|
|
16
|
-
defaultTarget: 'body'
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
let config = $state<PortalConfig>(defaultConfig);
|
|
20
|
-
export function setConfig(newConfig: Partial<PortalConfig>) {
|
|
21
|
-
config = { ...defaultConfig, ...newConfig };
|
|
22
|
-
}
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<script lang="ts">
|
|
26
|
-
let { children, target }: PortalProps = $props();
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<!--
|
|
30
|
-
@component
|
|
31
|
-
A portal, renders its children in a different DOM element.
|
|
32
|
-
|
|
33
|
-
Wrapps the `use:portal` action.
|
|
34
|
-
|
|
35
|
-
**Use sparingy as it can make the DOM structure confusing**
|
|
36
|
-
-->
|
|
37
|
-
<div use:portal={target ?? config.defaultTarget} hidden>
|
|
38
|
-
{@render children()}
|
|
39
|
-
</div>
|