@ims360/svelte-ivory 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENCE +23 -0
- package/README.md +15 -0
- package/dist/components/ai/AiMessage.svelte +115 -0
- package/dist/components/ai/AiMessage.svelte.d.ts +18 -0
- package/dist/components/ai/AttachedFile.svelte +28 -0
- package/dist/components/ai/AttachedFile.svelte.d.ts +9 -0
- package/dist/components/ai/Chat.svelte +150 -0
- package/dist/components/ai/Chat.svelte.d.ts +40 -0
- package/dist/components/ai/Markdown.svelte +59 -0
- package/dist/components/ai/Markdown.svelte.d.ts +16 -0
- package/dist/components/ai/UserMessage.svelte +53 -0
- package/dist/components/ai/UserMessage.svelte.d.ts +16 -0
- package/dist/components/ai/index.d.ts +5 -0
- package/dist/components/ai/index.js +5 -0
- package/dist/components/basic/checkbox/Checkbox.svelte +79 -0
- package/dist/components/basic/checkbox/Checkbox.svelte.d.ts +18 -0
- package/dist/components/basic/index.d.ts +2 -0
- package/dist/components/basic/index.js +2 -0
- package/dist/components/basic/toggle/Toggle.svelte +47 -0
- package/dist/components/basic/toggle/Toggle.svelte.d.ts +12 -0
- package/dist/components/buttons/CopyToClipboardButton.svelte +33 -0
- package/dist/components/buttons/CopyToClipboardButton.svelte.d.ts +9 -0
- package/dist/components/index.d.ts +0 -0
- package/dist/components/index.js +1 -0
- package/dist/components/layout/heading/Heading.svelte +33 -0
- package/dist/components/layout/heading/Heading.svelte.d.ts +16 -0
- package/dist/components/layout/heading/index.d.ts +5 -0
- package/dist/components/layout/heading/index.js +5 -0
- package/dist/components/layout/hiddenBackground/HiddenBackground.svelte +48 -0
- package/dist/components/layout/hiddenBackground/HiddenBackground.svelte.d.ts +13 -0
- package/dist/components/layout/hiddenBackground/index.js +6 -0
- package/dist/components/layout/index.d.ts +7 -0
- package/dist/components/layout/index.js +7 -0
- package/dist/components/layout/modal/Modal.svelte +114 -0
- package/dist/components/layout/modal/Modal.svelte.d.ts +30 -0
- package/dist/components/layout/modal/ModalTest.svelte +16 -0
- package/dist/components/layout/modal/ModalTest.svelte.d.ts +9 -0
- package/dist/components/layout/popover/Popover.svelte +108 -0
- package/dist/components/layout/popover/Popover.svelte.d.ts +36 -0
- package/dist/components/layout/portal/Portal.svelte +23 -0
- package/dist/components/layout/portal/Portal.svelte.d.ts +15 -0
- package/dist/components/layout/tabs/Tab.svelte +80 -0
- package/dist/components/layout/tabs/Tab.svelte.d.ts +21 -0
- package/dist/components/layout/tabs/TabPanel.svelte +23 -0
- package/dist/components/layout/tabs/TabPanel.svelte.d.ts +10 -0
- package/dist/components/layout/tabs/Tabs.svelte +86 -0
- package/dist/components/layout/tabs/Tabs.svelte.d.ts +21 -0
- package/dist/components/layout/tabs/index.d.ts +26 -0
- package/dist/components/layout/tabs/index.js +8 -0
- package/dist/components/layout/tooltip/Tooltip.svelte +111 -0
- package/dist/components/layout/tooltip/Tooltip.svelte.d.ts +32 -0
- package/dist/components/toast/Toast.svelte +100 -0
- package/dist/components/toast/Toast.svelte.d.ts +16 -0
- package/dist/components/toast/index.d.ts +2 -0
- package/dist/components/toast/index.js +2 -0
- package/dist/components/toast/toasts.svelte.d.ts +26 -0
- package/dist/components/toast/toasts.svelte.js +67 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +2 -0
- package/dist/utils/actions/clickOutside.d.ts +11 -0
- package/dist/utils/actions/clickOutside.js +23 -0
- package/dist/utils/actions/focusTrap.d.ts +4 -0
- package/dist/utils/actions/focusTrap.js +64 -0
- package/dist/utils/actions/index.d.ts +5 -0
- package/dist/utils/actions/index.js +5 -0
- package/dist/utils/actions/portal.d.ts +9 -0
- package/dist/utils/actions/portal.js +39 -0
- package/dist/utils/actions/shortcut.d.ts +10 -0
- package/dist/utils/actions/shortcut.js +25 -0
- package/dist/utils/actions/visible.d.ts +5 -0
- package/dist/utils/actions/visible.js +14 -0
- package/dist/utils/functions/cookie.d.ts +12 -0
- package/dist/utils/functions/cookie.js +36 -0
- package/dist/utils/functions/index.d.ts +3 -0
- package/dist/utils/functions/index.js +3 -0
- package/dist/utils/functions/pseudoRandomId.d.ts +1 -0
- package/dist/utils/functions/pseudoRandomId.js +3 -0
- package/dist/utils/functions/queryParams.d.ts +1 -0
- package/dist/utils/functions/queryParams.js +14 -0
- package/package.json +107 -0
- package/src/lib/components/ai/AiMessage.svelte +115 -0
- package/src/lib/components/ai/AttachedFile.svelte +28 -0
- package/src/lib/components/ai/Chat.svelte +150 -0
- package/src/lib/components/ai/Markdown.svelte +59 -0
- package/src/lib/components/ai/UserMessage.svelte +53 -0
- package/src/lib/components/ai/index.ts +5 -0
- package/src/lib/components/basic/checkbox/Checkbox.svelte +79 -0
- package/src/lib/components/basic/checkbox/checkbox.svelte.spec.ts +39 -0
- package/src/lib/components/basic/index.ts +2 -0
- package/src/lib/components/basic/toggle/Toggle.svelte +47 -0
- package/src/lib/components/basic/toggle/toggle.svelte.spec.ts +19 -0
- package/src/lib/components/buttons/CopyToClipboardButton.svelte +33 -0
- package/src/lib/components/index.ts +0 -0
- package/src/lib/components/layout/heading/Heading.svelte +33 -0
- package/src/lib/components/layout/heading/index.ts +7 -0
- package/src/lib/components/layout/hiddenBackground/HiddenBackground.svelte +48 -0
- package/src/lib/components/layout/hiddenBackground/index.ts +8 -0
- package/src/lib/components/layout/index.ts +7 -0
- package/src/lib/components/layout/modal/Modal.svelte +114 -0
- package/src/lib/components/layout/modal/ModalTest.svelte +16 -0
- package/src/lib/components/layout/modal/modal.svelte.spec.ts +39 -0
- package/src/lib/components/layout/popover/Popover.svelte +108 -0
- package/src/lib/components/layout/portal/Portal.svelte +23 -0
- package/src/lib/components/layout/tabs/Tab.svelte +80 -0
- package/src/lib/components/layout/tabs/TabPanel.svelte +23 -0
- package/src/lib/components/layout/tabs/Tabs.svelte +86 -0
- package/src/lib/components/layout/tabs/Tabs.test.svelte +5 -0
- package/src/lib/components/layout/tabs/index.ts +10 -0
- package/src/lib/components/layout/tooltip/Tooltip.svelte +111 -0
- package/src/lib/components/toast/Toast.svelte +100 -0
- package/src/lib/components/toast/index.ts +2 -0
- package/src/lib/components/toast/toasts.svelte.ts +89 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/utils/actions/clickOutside.svelte.spec.ts +67 -0
- package/src/lib/utils/actions/clickOutside.ts +38 -0
- package/src/lib/utils/actions/focusTrap.ts +65 -0
- package/src/lib/utils/actions/index.ts +5 -0
- package/src/lib/utils/actions/portal.ts +43 -0
- package/src/lib/utils/actions/shortcut.svelte.spec.ts +19 -0
- package/src/lib/utils/actions/shortcut.ts +35 -0
- package/src/lib/utils/actions/visible.ts +28 -0
- package/src/lib/utils/functions/cookie.svelte.spec.ts +55 -0
- package/src/lib/utils/functions/cookie.ts +46 -0
- package/src/lib/utils/functions/index.ts +3 -0
- package/src/lib/utils/functions/pseudoRandomId.spec.ts +19 -0
- package/src/lib/utils/functions/pseudoRandomId.ts +4 -0
- package/src/lib/utils/functions/queryParams.spec.ts +25 -0
- package/src/lib/utils/functions/queryParams.ts +15 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/state';
|
|
3
|
+
import { pseudoRandomId } from '../../../utils/functions/index';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { onMount, type Snippet } from 'svelte';
|
|
6
|
+
import type { ClassValue } from 'svelte/elements';
|
|
7
|
+
import { twMerge } from 'tailwind-merge';
|
|
8
|
+
import { getTabContext } from './Tabs.svelte';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
class?: (selected: boolean) => ClassValue;
|
|
12
|
+
id?: string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* If this is set the element will be a link.
|
|
15
|
+
*
|
|
16
|
+
* This is useful if your tabs are in a `+layout.svelte` and the Panels are seperate pages.
|
|
17
|
+
*/
|
|
18
|
+
href?: string | undefined;
|
|
19
|
+
children: Snippet<[{ selected: boolean }]>;
|
|
20
|
+
testId?: string;
|
|
21
|
+
/** If `href` is set, this can be used to highlight an active tab */
|
|
22
|
+
active?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
class: clazz = (selected: boolean) => [selected && 'text-primary-500 underline'],
|
|
27
|
+
id,
|
|
28
|
+
href,
|
|
29
|
+
children,
|
|
30
|
+
testId,
|
|
31
|
+
active
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
|
|
34
|
+
const tab = pseudoRandomId('tab-');
|
|
35
|
+
const tabs = getTabContext();
|
|
36
|
+
|
|
37
|
+
const tabId = id || tab;
|
|
38
|
+
|
|
39
|
+
const selected = $derived.by(() => {
|
|
40
|
+
if (typeof active === 'boolean') {
|
|
41
|
+
return active;
|
|
42
|
+
} else if (href) {
|
|
43
|
+
return page.url.pathname.startsWith(href);
|
|
44
|
+
} else if (tabs && tabs.selectedTab === tabId) {
|
|
45
|
+
return true;
|
|
46
|
+
} else {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
onMount(() => {
|
|
52
|
+
if (href) return;
|
|
53
|
+
tabs.registerTab(tabId);
|
|
54
|
+
});
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<svelte:element
|
|
58
|
+
this={href ? 'a' : 'button'}
|
|
59
|
+
class={twMerge(
|
|
60
|
+
clsx(
|
|
61
|
+
'btn flex h-fit w-fit items-center justify-center px-0 text-xl font-bold',
|
|
62
|
+
clazz(selected)
|
|
63
|
+
)
|
|
64
|
+
)}
|
|
65
|
+
onclick={href
|
|
66
|
+
? undefined
|
|
67
|
+
: () => {
|
|
68
|
+
tabs.selectedTab = tabId;
|
|
69
|
+
}}
|
|
70
|
+
type={href ? undefined : 'button'}
|
|
71
|
+
{href}
|
|
72
|
+
data-testid={testId}
|
|
73
|
+
role="tab"
|
|
74
|
+
tabindex="0"
|
|
75
|
+
aria-selected={selected}
|
|
76
|
+
>
|
|
77
|
+
{@render children({
|
|
78
|
+
selected
|
|
79
|
+
})}
|
|
80
|
+
</svelte:element>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { ClassValue } from 'svelte/elements';
|
|
3
|
+
type Props = {
|
|
4
|
+
class?: (selected: boolean) => ClassValue;
|
|
5
|
+
id?: string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* If this is set the element will be a link.
|
|
8
|
+
*
|
|
9
|
+
* This is useful if your tabs are in a `+layout.svelte` and the Panels are seperate pages.
|
|
10
|
+
*/
|
|
11
|
+
href?: string | undefined;
|
|
12
|
+
children: Snippet<[{
|
|
13
|
+
selected: boolean;
|
|
14
|
+
}]>;
|
|
15
|
+
testId?: string;
|
|
16
|
+
/** If `href` is set, this can be used to highlight an active tab */
|
|
17
|
+
active?: boolean;
|
|
18
|
+
};
|
|
19
|
+
declare const Tab: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type Tab = ReturnType<typeof Tab>;
|
|
21
|
+
export default Tab;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { pseudoRandomId } from '../../../utils/functions/index';
|
|
3
|
+
import { type Snippet } from 'svelte';
|
|
4
|
+
import { getTabContext } from './Tabs.svelte';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
keepMounted?: boolean;
|
|
8
|
+
children: Snippet<[{ visible: boolean }]>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let { keepMounted = false, children }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const panel = pseudoRandomId('tab-panel-');
|
|
14
|
+
const tabs = getTabContext();
|
|
15
|
+
|
|
16
|
+
tabs.registerPanel(panel);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if keepMounted}
|
|
20
|
+
{@render children({ visible: tabs.selectedPanel === panel })}
|
|
21
|
+
{:else if tabs.selectedPanel === panel}
|
|
22
|
+
{@render children({ visible: true })}
|
|
23
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
type Props = {
|
|
3
|
+
keepMounted?: boolean;
|
|
4
|
+
children: Snippet<[{
|
|
5
|
+
visible: boolean;
|
|
6
|
+
}]>;
|
|
7
|
+
};
|
|
8
|
+
declare const TabPanel: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type TabPanel = ReturnType<typeof TabPanel>;
|
|
10
|
+
export default TabPanel;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { getContext, onDestroy, setContext, type Snippet } from 'svelte';
|
|
3
|
+
import type { ClassValue } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
interface TabContext {
|
|
6
|
+
registerTab: (tab: string) => void;
|
|
7
|
+
registerPanel: (panel: string) => void;
|
|
8
|
+
selectedTab: string | undefined;
|
|
9
|
+
tabs: string[];
|
|
10
|
+
selectedPanel: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
const TABS = {};
|
|
13
|
+
|
|
14
|
+
function setTabContext(context: TabContext) {
|
|
15
|
+
setContext<TabContext>(TABS, context);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getTabContext() {
|
|
19
|
+
return getContext<TabContext>(TABS);
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
type Props = {
|
|
25
|
+
class?: ClassValue;
|
|
26
|
+
children: Snippet;
|
|
27
|
+
b_index?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let { class: clazz = '', children, b_index = $bindable(0) }: Props = $props();
|
|
31
|
+
|
|
32
|
+
let allTabs: string[] = $state([]);
|
|
33
|
+
let panels: string[] = $state([]);
|
|
34
|
+
|
|
35
|
+
export const forward = () => {
|
|
36
|
+
if (b_index >= panels.length - 1) {
|
|
37
|
+
b_index = 0;
|
|
38
|
+
} else {
|
|
39
|
+
b_index++;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const back = () => {
|
|
44
|
+
if (b_index === 0) {
|
|
45
|
+
b_index = panels.length - 1;
|
|
46
|
+
} else {
|
|
47
|
+
b_index--;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
setTabContext({
|
|
52
|
+
registerTab: (tab: string) => {
|
|
53
|
+
allTabs.push(tab);
|
|
54
|
+
onDestroy(() => {
|
|
55
|
+
const i = allTabs.indexOf(tab);
|
|
56
|
+
allTabs.splice(i, 1);
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
registerPanel: (panel: string) => {
|
|
61
|
+
panels.push(panel);
|
|
62
|
+
b_index = b_index;
|
|
63
|
+
onDestroy(() => {
|
|
64
|
+
panels.filter((p) => p !== panel);
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
get selectedTab() {
|
|
69
|
+
return allTabs[b_index];
|
|
70
|
+
},
|
|
71
|
+
set selectedTab(tab: string) {
|
|
72
|
+
b_index = allTabs.indexOf(tab);
|
|
73
|
+
},
|
|
74
|
+
get selectedPanel() {
|
|
75
|
+
return panels[b_index];
|
|
76
|
+
},
|
|
77
|
+
set selectedPanel(panel: string) {
|
|
78
|
+
b_index = panels.indexOf(panel);
|
|
79
|
+
},
|
|
80
|
+
tabs: allTabs
|
|
81
|
+
});
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<div class={[clazz]}>
|
|
85
|
+
{@render children()}
|
|
86
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { ClassValue } from 'svelte/elements';
|
|
3
|
+
interface TabContext {
|
|
4
|
+
registerTab: (tab: string) => void;
|
|
5
|
+
registerPanel: (panel: string) => void;
|
|
6
|
+
selectedTab: string | undefined;
|
|
7
|
+
tabs: string[];
|
|
8
|
+
selectedPanel: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function getTabContext(): TabContext;
|
|
11
|
+
type Props = {
|
|
12
|
+
class?: ClassValue;
|
|
13
|
+
children: Snippet;
|
|
14
|
+
b_index?: number;
|
|
15
|
+
};
|
|
16
|
+
declare const Tabs: import("svelte").Component<Props, {
|
|
17
|
+
forward: () => void;
|
|
18
|
+
back: () => void;
|
|
19
|
+
}, "b_index">;
|
|
20
|
+
type Tabs = ReturnType<typeof Tabs>;
|
|
21
|
+
export default Tabs;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare const Tabs: import("svelte").Component<{
|
|
2
|
+
class?: import("svelte/elements").ClassValue;
|
|
3
|
+
children: import("svelte").Snippet;
|
|
4
|
+
b_index?: number;
|
|
5
|
+
}, {
|
|
6
|
+
forward: () => void;
|
|
7
|
+
back: () => void;
|
|
8
|
+
}, "b_index"> & {
|
|
9
|
+
Tab: import("svelte").Component<{
|
|
10
|
+
class?: (selected: boolean) => import("svelte/elements").ClassValue;
|
|
11
|
+
id?: string | undefined;
|
|
12
|
+
href?: string | undefined;
|
|
13
|
+
children: import("svelte").Snippet<[{
|
|
14
|
+
selected: boolean;
|
|
15
|
+
}]>;
|
|
16
|
+
testId?: string;
|
|
17
|
+
active?: boolean;
|
|
18
|
+
}, {}, "">;
|
|
19
|
+
Panel: import("svelte").Component<{
|
|
20
|
+
keepMounted?: boolean;
|
|
21
|
+
children: import("svelte").Snippet<[{
|
|
22
|
+
visible: boolean;
|
|
23
|
+
}]>;
|
|
24
|
+
}, {}, "">;
|
|
25
|
+
};
|
|
26
|
+
export default Tabs;
|
|
@@ -0,0 +1,111 @@
|
|
|
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 Popover, { type PopoverPlacement } from '../popover/Popover.svelte';
|
|
7
|
+
import Portal from '../portal/Portal.svelte';
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
/** The content of the tooltip */
|
|
12
|
+
tooltip: string | Snippet;
|
|
13
|
+
/** The class of the element that triggers the tooltip */
|
|
14
|
+
class?: ClassValue;
|
|
15
|
+
/** The class of the tooltip itself */
|
|
16
|
+
tooltipClass?: ClassValue;
|
|
17
|
+
style?: string;
|
|
18
|
+
onclick?: (e: Event) => void;
|
|
19
|
+
/** If the href is set, the resulting element will be a link to the href */
|
|
20
|
+
href?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The delay before the tooltip is shown in ms.
|
|
23
|
+
*
|
|
24
|
+
* default: `500`
|
|
25
|
+
*/
|
|
26
|
+
timeout?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Where the tooltip should be placed
|
|
29
|
+
*
|
|
30
|
+
* default: `top`
|
|
31
|
+
*/
|
|
32
|
+
placement?: PopoverPlacement;
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
let {
|
|
38
|
+
children,
|
|
39
|
+
tooltip,
|
|
40
|
+
class: clazz,
|
|
41
|
+
style,
|
|
42
|
+
onclick,
|
|
43
|
+
href,
|
|
44
|
+
timeout = 500,
|
|
45
|
+
tooltipClass
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
let target = $state<HTMLElement>();
|
|
49
|
+
|
|
50
|
+
let open = $state(false);
|
|
51
|
+
|
|
52
|
+
let showTimeout: number;
|
|
53
|
+
function onpointerenter() {
|
|
54
|
+
clearTimeout(timeout);
|
|
55
|
+
if (timeout === 0) {
|
|
56
|
+
open = true;
|
|
57
|
+
} else {
|
|
58
|
+
showTimeout = setTimeout(() => {
|
|
59
|
+
open = true;
|
|
60
|
+
}, timeout) as unknown as number;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onpointerleave() {
|
|
65
|
+
clearTimeout(timeout);
|
|
66
|
+
open = false;
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<!--
|
|
71
|
+
@component
|
|
72
|
+
Shows additional information when hovering over an element.
|
|
73
|
+
-->
|
|
74
|
+
|
|
75
|
+
<!-- Ignoring this error is fine since it's a false positive -->
|
|
76
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
77
|
+
<svelte:element
|
|
78
|
+
this={href ? 'a' : onclick ? 'button' : 'div'}
|
|
79
|
+
{href}
|
|
80
|
+
type={onclick ? 'button' : undefined}
|
|
81
|
+
class={clazz}
|
|
82
|
+
bind:this={target}
|
|
83
|
+
{onpointerenter}
|
|
84
|
+
{onpointerleave}
|
|
85
|
+
{style}
|
|
86
|
+
{onclick}
|
|
87
|
+
>
|
|
88
|
+
{@render children?.()}
|
|
89
|
+
</svelte:element>
|
|
90
|
+
|
|
91
|
+
{#if open}
|
|
92
|
+
<Portal>
|
|
93
|
+
<Popover
|
|
94
|
+
bind:b_open={open}
|
|
95
|
+
{target}
|
|
96
|
+
placement="top"
|
|
97
|
+
class={twMerge(
|
|
98
|
+
clsx(
|
|
99
|
+
'bg-surface-100-800-token max-w-96 -translate-y-0.5 rounded px-4 py-1 shadow-lg',
|
|
100
|
+
tooltipClass
|
|
101
|
+
)
|
|
102
|
+
)}
|
|
103
|
+
>
|
|
104
|
+
{#if typeof tooltip === 'string'}
|
|
105
|
+
{tooltip}
|
|
106
|
+
{:else}
|
|
107
|
+
{@render tooltip()}
|
|
108
|
+
{/if}
|
|
109
|
+
</Popover>
|
|
110
|
+
</Portal>
|
|
111
|
+
{/if}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ClassValue } from 'svelte/elements';
|
|
3
|
+
import { type PopoverPlacement } from '../popover/Popover.svelte';
|
|
4
|
+
export interface Props {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
/** The content of the tooltip */
|
|
7
|
+
tooltip: string | Snippet;
|
|
8
|
+
/** The class of the element that triggers the tooltip */
|
|
9
|
+
class?: ClassValue;
|
|
10
|
+
/** The class of the tooltip itself */
|
|
11
|
+
tooltipClass?: ClassValue;
|
|
12
|
+
style?: string;
|
|
13
|
+
onclick?: (e: Event) => void;
|
|
14
|
+
/** If the href is set, the resulting element will be a link to the href */
|
|
15
|
+
href?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The delay before the tooltip is shown in ms.
|
|
18
|
+
*
|
|
19
|
+
* default: `500`
|
|
20
|
+
*/
|
|
21
|
+
timeout?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Where the tooltip should be placed
|
|
24
|
+
*
|
|
25
|
+
* default: `top`
|
|
26
|
+
*/
|
|
27
|
+
placement?: PopoverPlacement;
|
|
28
|
+
}
|
|
29
|
+
/** Shows additional information when hovering over an element. */
|
|
30
|
+
declare const Tooltip: import("svelte").Component<Props, {}, "">;
|
|
31
|
+
type Tooltip = ReturnType<typeof Tooltip>;
|
|
32
|
+
export default Tooltip;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Renders the toasts that have been triggered by the `Toasts` store.
|
|
4
|
+
Make sure to include this component in your root `+layout.svelte` file.
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { Icon } from '@lucide/svelte';
|
|
8
|
+
import { Check, CircleAlert, Info, TriangleAlert, X } from '@lucide/svelte';
|
|
9
|
+
import type { Snippet } from 'svelte';
|
|
10
|
+
import { flip } from 'svelte/animate';
|
|
11
|
+
import type { ClassValue } from 'svelte/elements';
|
|
12
|
+
import { fly, scale } from 'svelte/transition';
|
|
13
|
+
import { Toasts, type ToastSettings } from './toasts.svelte';
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
children?: Snippet<[toast: ToastSettings & { close: () => void }]>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let { class: clazz = 'px-2 pb-2', children }: Props = $props();
|
|
21
|
+
|
|
22
|
+
function getIcon(
|
|
23
|
+
variant: 'info' | 'success' | 'warning' | 'error',
|
|
24
|
+
icon?: typeof Icon
|
|
25
|
+
): typeof Icon {
|
|
26
|
+
if (icon) return icon;
|
|
27
|
+
switch (variant) {
|
|
28
|
+
case 'info':
|
|
29
|
+
return Info;
|
|
30
|
+
case 'success':
|
|
31
|
+
return Check;
|
|
32
|
+
case 'warning':
|
|
33
|
+
return CircleAlert;
|
|
34
|
+
case 'error':
|
|
35
|
+
return TriangleAlert;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<div
|
|
41
|
+
class="pointer-events-none absolute top-0 left-0 z-50 flex h-full w-full flex-col items-center justify-start"
|
|
42
|
+
>
|
|
43
|
+
<div
|
|
44
|
+
class={[
|
|
45
|
+
'flex h-full max-h-full flex-col-reverse items-center justify-end gap-2 overflow-hidden',
|
|
46
|
+
clazz
|
|
47
|
+
]}
|
|
48
|
+
>
|
|
49
|
+
{#each Toasts.toasts as toast (toast.id)}
|
|
50
|
+
{@const VariantIcon = getIcon(toast.variant, toast.icon)}
|
|
51
|
+
|
|
52
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
53
|
+
<div
|
|
54
|
+
in:fly={{ y: '-100%', duration: 300 }}
|
|
55
|
+
out:scale={{ duration: 300 }}
|
|
56
|
+
animate:flip={{ duration: 300 }}
|
|
57
|
+
onpointerenter={() => {
|
|
58
|
+
Toasts.freeze(toast.id);
|
|
59
|
+
}}
|
|
60
|
+
onpointerleave={() => {
|
|
61
|
+
Toasts.unfreeze(toast.id);
|
|
62
|
+
}}
|
|
63
|
+
class="group pointer-events-auto flex h-fit w-fit flex-row items-center"
|
|
64
|
+
>
|
|
65
|
+
{#if children}
|
|
66
|
+
{@render children({
|
|
67
|
+
...toast,
|
|
68
|
+
close: () => Toasts.close(toast.id)
|
|
69
|
+
})}
|
|
70
|
+
{:else}
|
|
71
|
+
<div
|
|
72
|
+
class={[
|
|
73
|
+
'bg-opacity-85 flex flex-row items-center gap-4 rounded px-4 py-2 shadow-lg group-last:rounded-t-none',
|
|
74
|
+
toast.variant === 'info' && 'preset-filled-primary-500',
|
|
75
|
+
toast.variant === 'success' && 'preset-filled-success-500',
|
|
76
|
+
toast.variant === 'warning' && 'preset-filled-warning-500',
|
|
77
|
+
toast.variant === 'error' && 'preset-filled-error-500'
|
|
78
|
+
]}
|
|
79
|
+
>
|
|
80
|
+
<VariantIcon />
|
|
81
|
+
<p class="font-bold">
|
|
82
|
+
{toast.message}
|
|
83
|
+
</p>
|
|
84
|
+
{#if !toast.hideDismiss}
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
onclick={() => {
|
|
88
|
+
Toasts.close(toast.id);
|
|
89
|
+
}}
|
|
90
|
+
class="transition-transform hover:rotate-90"
|
|
91
|
+
>
|
|
92
|
+
<X />
|
|
93
|
+
</button>
|
|
94
|
+
{/if}
|
|
95
|
+
</div>
|
|
96
|
+
{/if}
|
|
97
|
+
</div>
|
|
98
|
+
{/each}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ClassValue } from 'svelte/elements';
|
|
3
|
+
import { type ToastSettings } from './toasts.svelte';
|
|
4
|
+
type Props = {
|
|
5
|
+
class?: ClassValue;
|
|
6
|
+
children?: Snippet<[toast: ToastSettings & {
|
|
7
|
+
close: () => void;
|
|
8
|
+
}]>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Renders the toasts that have been triggered by the `Toasts` store.
|
|
12
|
+
* Make sure to include this component in your root `+layout.svelte` file.
|
|
13
|
+
*/
|
|
14
|
+
declare const Toast: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type Toast = ReturnType<typeof Toast>;
|
|
16
|
+
export default Toast;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Icon } from '@lucide/svelte';
|
|
2
|
+
export type ToastSettings = {
|
|
3
|
+
message: string;
|
|
4
|
+
autohide?: boolean;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
hideDismiss?: boolean;
|
|
7
|
+
icon?: typeof Icon;
|
|
8
|
+
variant: 'info' | 'success' | 'warning' | 'error';
|
|
9
|
+
};
|
|
10
|
+
type Toast = ToastSettings & {
|
|
11
|
+
id: string;
|
|
12
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
13
|
+
};
|
|
14
|
+
declare class ToastStore {
|
|
15
|
+
toasts: Toast[];
|
|
16
|
+
trigger(toast: ToastSettings, id?: string): string;
|
|
17
|
+
close(id: string): void;
|
|
18
|
+
update(id: string, toast: ToastSettings): void;
|
|
19
|
+
freeze(id: string): void;
|
|
20
|
+
unfreeze(id: string): void;
|
|
21
|
+
private startAutoHide;
|
|
22
|
+
private stopAutoHide;
|
|
23
|
+
}
|
|
24
|
+
/** Exposes functions to manage toasts, contains currently active toasts */
|
|
25
|
+
export declare const Toasts: ToastStore;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const TOAST_DEFAULTS = {
|
|
2
|
+
message: 'Missing Toast Message',
|
|
3
|
+
autohide: true,
|
|
4
|
+
timeout: 3000,
|
|
5
|
+
variant: 'info'
|
|
6
|
+
};
|
|
7
|
+
class ToastStore {
|
|
8
|
+
toasts = $state([]);
|
|
9
|
+
trigger(toast, id = crypto.randomUUID()) {
|
|
10
|
+
const mergedToasts = { ...TOAST_DEFAULTS, ...toast, id };
|
|
11
|
+
// start the autohide timeout
|
|
12
|
+
this.startAutoHide(mergedToasts);
|
|
13
|
+
// add the toasts to the list
|
|
14
|
+
this.toasts.push(mergedToasts);
|
|
15
|
+
return id;
|
|
16
|
+
}
|
|
17
|
+
close(id) {
|
|
18
|
+
const toast = this.toasts.find((t) => t.id === id);
|
|
19
|
+
if (!toast)
|
|
20
|
+
return;
|
|
21
|
+
this.stopAutoHide(toast);
|
|
22
|
+
this.toasts = this.toasts.filter((t) => t.id !== id);
|
|
23
|
+
}
|
|
24
|
+
update(id, toast) {
|
|
25
|
+
const existingToast = this.toasts.find((t) => t.id === id);
|
|
26
|
+
if (!existingToast) {
|
|
27
|
+
this.trigger(toast, id);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const mergedToast = {
|
|
31
|
+
...TOAST_DEFAULTS,
|
|
32
|
+
...existingToast,
|
|
33
|
+
...toast
|
|
34
|
+
};
|
|
35
|
+
// clear the existing timeout and restart the autohide
|
|
36
|
+
this.stopAutoHide(existingToast);
|
|
37
|
+
this.startAutoHide(mergedToast);
|
|
38
|
+
this.toasts.splice(this.toasts.indexOf(existingToast), 1, mergedToast);
|
|
39
|
+
}
|
|
40
|
+
freeze(id) {
|
|
41
|
+
const toast = this.toasts.find((t) => t.id === id);
|
|
42
|
+
if (!toast)
|
|
43
|
+
return;
|
|
44
|
+
this.stopAutoHide(toast);
|
|
45
|
+
}
|
|
46
|
+
unfreeze(id) {
|
|
47
|
+
const toast = this.toasts.find((t) => t.id === id);
|
|
48
|
+
if (!toast)
|
|
49
|
+
return;
|
|
50
|
+
this.startAutoHide(toast);
|
|
51
|
+
}
|
|
52
|
+
startAutoHide(toast) {
|
|
53
|
+
if (!toast.autohide)
|
|
54
|
+
return;
|
|
55
|
+
toast.timeoutId = setTimeout(() => {
|
|
56
|
+
this.close(toast.id);
|
|
57
|
+
}, toast.timeout);
|
|
58
|
+
}
|
|
59
|
+
stopAutoHide(toast) {
|
|
60
|
+
if (!toast.timeoutId)
|
|
61
|
+
return;
|
|
62
|
+
clearTimeout(toast.timeoutId);
|
|
63
|
+
delete toast.timeoutId;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Exposes functions to manage toasts, contains currently active toasts */
|
|
67
|
+
export const Toasts = new ToastStore();
|
package/dist/index.d.ts
ADDED
|
File without changes
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ClickOutsideParams {
|
|
2
|
+
/** Callback to be called when clicking outside of node */
|
|
3
|
+
callback: (e: MouseEvent) => void;
|
|
4
|
+
/** Callback is also not fired if the click target is inside this element */
|
|
5
|
+
target?: Element;
|
|
6
|
+
}
|
|
7
|
+
/** Dispatch event on click outside of node */
|
|
8
|
+
export declare function clickOutside(node: Element, params: ((e: MouseEvent) => void) | ClickOutsideParams): {
|
|
9
|
+
destroy(): void;
|
|
10
|
+
};
|
|
11
|
+
export {};
|