@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.
Files changed (176) hide show
  1. package/README.md +0 -5
  2. package/dist/components/ai/AiMessage.svelte +2 -3
  3. package/dist/components/ai/AiMessage.svelte.d.ts.map +1 -1
  4. package/dist/components/ai/Chat.svelte +2 -3
  5. package/dist/components/ai/Chat.svelte.d.ts.map +1 -1
  6. package/dist/components/ai/Markdown.svelte +4 -7
  7. package/dist/components/ai/Markdown.svelte.d.ts.map +1 -1
  8. package/dist/components/ai/UserMessage.svelte +2 -3
  9. package/dist/components/ai/UserMessage.svelte.d.ts.map +1 -1
  10. package/dist/components/basic/checkbox/Checkbox.svelte +13 -11
  11. package/dist/components/basic/checkbox/Checkbox.svelte.d.ts.map +1 -1
  12. package/dist/components/basic/toggle/Toggle.svelte +5 -8
  13. package/dist/components/basic/toggle/Toggle.svelte.d.ts.map +1 -1
  14. package/dist/components/inputs/CheckboxInput.svelte +39 -0
  15. package/dist/components/inputs/CheckboxInput.svelte.d.ts +12 -0
  16. package/dist/components/inputs/CheckboxInput.svelte.d.ts.map +1 -0
  17. package/dist/components/inputs/ColorInput.svelte +25 -0
  18. package/dist/components/inputs/ColorInput.svelte.d.ts +5 -0
  19. package/dist/components/inputs/ColorInput.svelte.d.ts.map +1 -0
  20. package/dist/components/inputs/DateInput.svelte +11 -0
  21. package/dist/components/inputs/DateInput.svelte.d.ts +5 -0
  22. package/dist/components/inputs/DateInput.svelte.d.ts.map +1 -0
  23. package/dist/components/inputs/EmailInput.svelte +16 -0
  24. package/dist/components/inputs/EmailInput.svelte.d.ts +5 -0
  25. package/dist/components/inputs/EmailInput.svelte.d.ts.map +1 -0
  26. package/dist/components/inputs/FileInput.svelte +131 -0
  27. package/dist/components/inputs/FileInput.svelte.d.ts +11 -0
  28. package/dist/components/inputs/FileInput.svelte.d.ts.map +1 -0
  29. package/dist/components/inputs/Input.svelte +101 -0
  30. package/dist/components/inputs/Input.svelte.d.ts +48 -0
  31. package/dist/components/inputs/Input.svelte.d.ts.map +1 -0
  32. package/dist/components/inputs/NumberInput.svelte +17 -0
  33. package/dist/components/inputs/NumberInput.svelte.d.ts +10 -0
  34. package/dist/components/inputs/NumberInput.svelte.d.ts.map +1 -0
  35. package/dist/components/inputs/PasswordCreateInput.svelte +60 -0
  36. package/dist/components/inputs/PasswordCreateInput.svelte.d.ts +12 -0
  37. package/dist/components/inputs/PasswordCreateInput.svelte.d.ts.map +1 -0
  38. package/dist/components/inputs/PasswordInput.svelte +27 -0
  39. package/dist/components/inputs/PasswordInput.svelte.d.ts +10 -0
  40. package/dist/components/inputs/PasswordInput.svelte.d.ts.map +1 -0
  41. package/dist/components/inputs/TextInput.svelte +16 -0
  42. package/dist/components/inputs/TextInput.svelte.d.ts +7 -0
  43. package/dist/components/inputs/TextInput.svelte.d.ts.map +1 -0
  44. package/dist/components/inputs/ToggleInput.svelte +24 -0
  45. package/dist/components/inputs/ToggleInput.svelte.d.ts +5 -0
  46. package/dist/components/inputs/ToggleInput.svelte.d.ts.map +1 -0
  47. package/dist/components/inputs/index.d.ts +13 -0
  48. package/dist/components/inputs/index.d.ts.map +1 -1
  49. package/dist/components/inputs/index.js +13 -0
  50. package/dist/components/inputs/issues/FormIssues.svelte +35 -0
  51. package/dist/components/inputs/issues/FormIssues.svelte.d.ts +10 -0
  52. package/dist/components/inputs/issues/FormIssues.svelte.d.ts.map +1 -0
  53. package/dist/components/inputs/select/Select.svelte +69 -0
  54. package/dist/components/inputs/select/Select.svelte.d.ts +15 -0
  55. package/dist/components/inputs/select/Select.svelte.d.ts.map +1 -0
  56. package/dist/components/inputs/select/SelectOption.svelte +34 -0
  57. package/dist/components/inputs/select/SelectOption.svelte.d.ts +11 -0
  58. package/dist/components/inputs/select/SelectOption.svelte.d.ts.map +1 -0
  59. package/dist/components/layout/Heading.svelte +5 -4
  60. package/dist/components/layout/Heading.svelte.d.ts.map +1 -1
  61. package/dist/components/layout/dialog/Dialog.svelte +5 -8
  62. package/dist/components/layout/dialog/Dialog.svelte.d.ts.map +1 -1
  63. package/dist/components/layout/drawer/Drawer.svelte +7 -10
  64. package/dist/components/layout/drawer/Drawer.svelte.d.ts.map +1 -1
  65. package/dist/components/layout/index.d.ts +1 -2
  66. package/dist/components/layout/index.d.ts.map +1 -1
  67. package/dist/components/layout/index.js +0 -1
  68. package/dist/components/layout/modal/Modal.svelte +10 -15
  69. package/dist/components/layout/modal/Modal.svelte.d.ts.map +1 -1
  70. package/dist/components/layout/popover/Popover.svelte +96 -79
  71. package/dist/components/layout/popover/Popover.svelte.d.ts +8 -22
  72. package/dist/components/layout/popover/Popover.svelte.d.ts.map +1 -1
  73. package/dist/components/layout/tabs/Tab.svelte +5 -8
  74. package/dist/components/layout/tabs/Tab.svelte.d.ts.map +1 -1
  75. package/dist/components/layout/tooltip/Tooltip.svelte +4 -7
  76. package/dist/components/layout/tooltip/Tooltip.svelte.d.ts.map +1 -1
  77. package/dist/components/table/Column.svelte +7 -10
  78. package/dist/components/table/Column.svelte.d.ts.map +1 -1
  79. package/dist/components/table/ColumnHead.svelte +2 -2
  80. package/dist/components/table/Table.svelte +5 -8
  81. package/dist/components/table/Table.svelte.d.ts.map +1 -1
  82. package/dist/components/table/VirtualList.svelte +3 -8
  83. package/dist/components/table/VirtualList.svelte.d.ts.map +1 -1
  84. package/dist/theme.svelte.d.ts +18 -0
  85. package/dist/theme.svelte.d.ts.map +1 -1
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/utils/{actions → attachments}/clickOutside.d.ts +2 -4
  88. package/dist/utils/attachments/clickOutside.d.ts.map +1 -0
  89. package/dist/utils/{actions → attachments}/clickOutside.js +4 -7
  90. package/dist/utils/attachments/focusTrap.d.ts +3 -0
  91. package/dist/utils/attachments/focusTrap.d.ts.map +1 -0
  92. package/dist/utils/{actions → attachments}/focusTrap.js +5 -14
  93. package/dist/utils/{actions → attachments}/index.d.ts +0 -1
  94. package/dist/utils/attachments/index.d.ts.map +1 -0
  95. package/dist/utils/{actions → attachments}/index.js +0 -1
  96. package/dist/utils/attachments/resize.d.ts +6 -0
  97. package/dist/utils/attachments/resize.d.ts.map +1 -0
  98. package/dist/utils/{actions → attachments}/resize.js +3 -5
  99. package/dist/utils/attachments/shortcut.svelte.d.ts.map +1 -0
  100. package/dist/utils/attachments/visible.d.ts +6 -0
  101. package/dist/utils/attachments/visible.d.ts.map +1 -0
  102. package/dist/utils/attachments/visible.js +12 -0
  103. package/dist/utils/merge.d.ts +4 -0
  104. package/dist/utils/merge.d.ts.map +1 -0
  105. package/dist/utils/merge.js +6 -0
  106. package/package.json +7 -5
  107. package/src/lib/components/ai/AiMessage.svelte +2 -3
  108. package/src/lib/components/ai/Chat.svelte +2 -3
  109. package/src/lib/components/ai/Markdown.svelte +4 -7
  110. package/src/lib/components/ai/UserMessage.svelte +2 -3
  111. package/src/lib/components/basic/checkbox/Checkbox.svelte +13 -11
  112. package/src/lib/components/basic/toggle/Toggle.svelte +5 -8
  113. package/src/lib/components/inputs/CheckboxInput.svelte +39 -0
  114. package/src/lib/components/inputs/ColorInput.svelte +25 -0
  115. package/src/lib/components/inputs/DateInput.svelte +11 -0
  116. package/src/lib/components/inputs/EmailInput.svelte +16 -0
  117. package/src/lib/components/inputs/FileInput.svelte +131 -0
  118. package/src/lib/components/inputs/Input.svelte +101 -0
  119. package/src/lib/components/inputs/NumberInput.svelte +17 -0
  120. package/src/lib/components/inputs/PasswordCreateInput.svelte +60 -0
  121. package/src/lib/components/inputs/PasswordInput.svelte +27 -0
  122. package/src/lib/components/inputs/TextInput.svelte +16 -0
  123. package/src/lib/components/inputs/ToggleInput.svelte +24 -0
  124. package/src/lib/components/inputs/index.ts +17 -0
  125. package/src/lib/components/inputs/issues/FormIssues.svelte +35 -0
  126. package/src/lib/components/inputs/select/Select.svelte +69 -0
  127. package/src/lib/components/inputs/select/SelectOption.svelte +34 -0
  128. package/src/lib/components/layout/Heading.svelte +5 -4
  129. package/src/lib/components/layout/dialog/Dialog.svelte +5 -8
  130. package/src/lib/components/layout/drawer/Drawer.svelte +7 -10
  131. package/src/lib/components/layout/index.ts +1 -5
  132. package/src/lib/components/layout/modal/Modal.svelte +10 -15
  133. package/src/lib/components/layout/popover/Popover.svelte +96 -79
  134. package/src/lib/components/layout/tabs/Tab.svelte +5 -8
  135. package/src/lib/components/layout/tooltip/Tooltip.svelte +4 -7
  136. package/src/lib/components/table/Column.svelte +7 -10
  137. package/src/lib/components/table/ColumnHead.svelte +2 -2
  138. package/src/lib/components/table/Table.svelte +5 -8
  139. package/src/lib/components/table/VirtualList.svelte +3 -8
  140. package/src/lib/theme.svelte.ts +18 -0
  141. package/src/lib/types.ts +3 -2
  142. package/src/lib/utils/attachments/clickOutside.ts +36 -0
  143. package/src/lib/utils/attachments/focusTrap.ts +67 -0
  144. package/src/lib/utils/{actions → attachments}/index.ts +0 -1
  145. package/src/lib/utils/attachments/resize.ts +38 -0
  146. package/src/lib/utils/attachments/visible.ts +22 -0
  147. package/src/lib/utils/merge.ts +7 -0
  148. package/dist/components/layout/portal/Portal.svelte +0 -39
  149. package/dist/components/layout/portal/Portal.svelte.d.ts +0 -21
  150. package/dist/components/layout/portal/Portal.svelte.d.ts.map +0 -1
  151. package/dist/components/layout/portal/index.d.ts +0 -6
  152. package/dist/components/layout/portal/index.d.ts.map +0 -1
  153. package/dist/components/layout/portal/index.js +0 -5
  154. package/dist/utils/actions/clickOutside.d.ts.map +0 -1
  155. package/dist/utils/actions/focusTrap.d.ts +0 -5
  156. package/dist/utils/actions/focusTrap.d.ts.map +0 -1
  157. package/dist/utils/actions/index.d.ts.map +0 -1
  158. package/dist/utils/actions/portal.d.ts +0 -10
  159. package/dist/utils/actions/portal.d.ts.map +0 -1
  160. package/dist/utils/actions/portal.js +0 -39
  161. package/dist/utils/actions/resize.d.ts +0 -6
  162. package/dist/utils/actions/resize.d.ts.map +0 -1
  163. package/dist/utils/actions/shortcut.svelte.d.ts.map +0 -1
  164. package/dist/utils/actions/visible.d.ts +0 -6
  165. package/dist/utils/actions/visible.d.ts.map +0 -1
  166. package/dist/utils/actions/visible.js +0 -14
  167. package/src/lib/components/layout/portal/Portal.svelte +0 -39
  168. package/src/lib/components/layout/portal/index.ts +0 -7
  169. package/src/lib/utils/actions/clickOutside.ts +0 -38
  170. package/src/lib/utils/actions/focusTrap.ts +0 -65
  171. package/src/lib/utils/actions/portal.ts +0 -43
  172. package/src/lib/utils/actions/resize.ts +0 -35
  173. package/src/lib/utils/actions/visible.ts +0 -28
  174. /package/dist/utils/{actions → attachments}/shortcut.svelte.d.ts +0 -0
  175. /package/dist/utils/{actions → attachments}/shortcut.svelte.js +0 -0
  176. /package/src/lib/utils/{actions → attachments}/shortcut.svelte.ts +0 -0
@@ -0,0 +1,101 @@
1
+ <script lang="ts" module>
2
+ import { theme } from '$lib/theme.svelte';
3
+ import { pseudoRandomId } from '$lib/utils/functions';
4
+ import { merge } from '$lib/utils/merge';
5
+ import type { RemoteFormField, RemoteFormFieldValue } from '@sveltejs/kit';
6
+ import type { Snippet } from 'svelte';
7
+ import type { ClassValue } from 'svelte/elements';
8
+ import { slide } from 'svelte/transition';
9
+ import FormIssues from './issues/FormIssues.svelte';
10
+
11
+ interface CommonProps {
12
+ id?: string;
13
+ class?: ClassValue;
14
+ style?: string;
15
+ label?: string;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export interface InputProps<T extends RemoteFormFieldValue> extends CommonProps {
20
+ form: RemoteFormField<T>;
21
+ }
22
+
23
+ export const INPUT_UNSET_OUTLINE =
24
+ 'border-none outline-none ring-transparent focus:outline-none';
25
+ </script>
26
+
27
+ <script lang="ts" generics="T extends RemoteFormFieldValue">
28
+ interface Props<K extends RemoteFormFieldValue> extends CommonProps {
29
+ children: Snippet<[{ class: string; id: string; disabled?: boolean }]>;
30
+ fixTitle?: boolean;
31
+ form: Pick<RemoteFormField<K>, 'value' | 'issues'>;
32
+ }
33
+
34
+ let {
35
+ class: clazz,
36
+ style,
37
+ label,
38
+ id = pseudoRandomId(),
39
+ children,
40
+ form,
41
+ fixTitle,
42
+ ...inputProps
43
+ }: Props<T> = $props();
44
+
45
+ const inputClass = `bg-transparent grow h-14 transition-all peer m-[1px] px-4 py-3 text-lg ${INPUT_UNSET_OUTLINE}`;
46
+
47
+ const hasIssues = $derived.by(() => {
48
+ const i = form.issues?.();
49
+ return i && i.length > 0;
50
+ });
51
+
52
+ const hasValue = $derived(!!fixTitle || !!form.value?.());
53
+ </script>
54
+
55
+ <div
56
+ class={[
57
+ 'accent-primary-500 relative flex h-fit flex-col pt-5',
58
+ inputProps.disabled && 'pointer-events-none opacity-70',
59
+ theme.current.input?.outerClass,
60
+ clazz
61
+ ]}
62
+ {style}
63
+ >
64
+ <div
65
+ class={merge(
66
+ 'group flex h-full grow flex-col overflow-hidden rounded border-2',
67
+ hasIssues
68
+ ? 'bg-error-500/20 border-error-500'
69
+ : 'focus-within:border-primary-500 focus-within:hover:border-primary-500 border-surface-300-700/25 hover:border-surface-300-700/50 transition-[border-color] duration-300',
70
+ theme.current.input?.class?.(hasValue, hasIssues)
71
+ )}
72
+ >
73
+ {@render children({ class: inputClass, id, ...inputProps })}
74
+ {#if label}
75
+ <label
76
+ class={merge([
77
+ 'pointer-events-none absolute cursor-text px-1 transition-all select-none group-focus-within:top-0 group-focus-within:left-0 group-focus-within:text-sm focus:cursor-default',
78
+ hasValue ? 'top-0 left-0 cursor-default text-sm' : 'top-10 left-3',
79
+ hasIssues ? 'text-error-500' : 'text-surface-700-300',
80
+ theme.current.input?.label?.class?.(hasValue, hasIssues)
81
+ ])}
82
+ for={id}
83
+ >
84
+ {label}
85
+ </label>
86
+ {/if}
87
+ </div>
88
+ {#if form.issues?.()}
89
+ <div class="h-fit w-full">
90
+ <div
91
+ class={merge(
92
+ 'flex w-full flex-col overflow-hidden',
93
+ theme.current.input?.issues?.class
94
+ )}
95
+ transition:slide
96
+ >
97
+ <FormIssues issues={form.issues?.()} class="my-0.5 pr-2 first:mt-1 last:mb-1" />
98
+ </div>
99
+ </div>
100
+ {/if}
101
+ </div>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import InputBase, { type InputProps } from './Input.svelte';
3
+
4
+ type Props = InputProps<number> & {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ };
9
+
10
+ let { min = undefined, step = 1, max, ...props }: Props = $props();
11
+ </script>
12
+
13
+ <InputBase {...props}>
14
+ {#snippet children(inputProps)}
15
+ <input {...inputProps} {...props.form.as?.('number')} {min} {max} {step} />
16
+ {/snippet}
17
+ </InputBase>
@@ -0,0 +1,60 @@
1
+ <script lang="ts" module>
2
+ import { CircleAlert, CircleCheck } from '@lucide/svelte';
3
+ import { Popover } from '../layout';
4
+ import type { InputProps } from './Input.svelte';
5
+ import PasswordInput from './PasswordInput.svelte';
6
+
7
+ export interface PasswordRequirement {
8
+ re: RegExp;
9
+ label: string;
10
+ }
11
+
12
+ export interface PasswordCreateInputProps extends InputProps<string> {
13
+ requirements: PasswordRequirement[];
14
+ }
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ let { class: clazz = '', requirements, ...props }: PasswordCreateInputProps = $props();
19
+
20
+ let popover = $state<Popover>();
21
+ let target = $state<HTMLElement>();
22
+
23
+ const value = $derived(props.form.value());
24
+ </script>
25
+
26
+ <div bind:this={target} class={['bg-inherit ', clazz]}>
27
+ <PasswordInput {...props} newPassword onfocusin={popover?.open} onfocusout={popover?.close} />
28
+ </div>
29
+
30
+ <Popover
31
+ bind:this={popover}
32
+ {target}
33
+ style="min-width: {target?.getBoundingClientRect().width}px;"
34
+ class="py-3"
35
+ placement="top"
36
+ >
37
+ <div class="bg-surface-50-950 flex w-full flex-col gap-2 rounded p-4 shadow-lg">
38
+ {#each requirements as requirement (requirement.re)}
39
+ {@render pwRequirement(requirement.re.test(value), requirement.label)}
40
+ {/each}
41
+ </div>
42
+ </Popover>
43
+
44
+ {#snippet pwRequirement(matches: boolean, label: string)}
45
+ <div
46
+ class={[
47
+ 'flex flex-row items-center gap-2',
48
+ matches ? 'text-success-700-300' : 'text-error-500'
49
+ ]}
50
+ >
51
+ {#if matches}
52
+ <CircleCheck />
53
+ {:else}
54
+ <CircleAlert />
55
+ {/if}
56
+ <p>
57
+ {label}
58
+ </p>
59
+ </div>
60
+ {/snippet}
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { InputProps } from './Input.svelte';
3
+ import Input from './Input.svelte';
4
+
5
+ type Props = InputProps<string> & {
6
+ newPassword?: boolean;
7
+ onfocusin?: () => void;
8
+ onfocusout?: () => void;
9
+ };
10
+
11
+ let { newPassword = false, onfocusin, onfocusout, ...props }: Props = $props();
12
+ </script>
13
+
14
+ <Input {...props}>
15
+ {#snippet children(inputProps)}
16
+ <input
17
+ {...inputProps}
18
+ {...props.form.as?.('password')}
19
+ type="password"
20
+ autocapitalize="off"
21
+ autocomplete={newPassword ? 'new-password' : 'current-password'}
22
+ required
23
+ {onfocusin}
24
+ {onfocusout}
25
+ />
26
+ {/snippet}
27
+ </Input>
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import Input, { type InputProps } from './Input.svelte';
3
+
4
+ let { ...props }: InputProps<string> = $props();
5
+
6
+ let inputElement = $state<HTMLInputElement>();
7
+ export function focus() {
8
+ inputElement?.focus();
9
+ }
10
+ </script>
11
+
12
+ <Input {...props}>
13
+ {#snippet children(inputProps)}
14
+ <input {...inputProps} {...props.form.as?.('text')} bind:this={inputElement} />
15
+ {/snippet}
16
+ </Input>
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import { Toggle } from '../basic';
3
+ import type { InputProps } from './Input.svelte';
4
+ import FormIssues from './issues/FormIssues.svelte';
5
+
6
+ let { form, class: clazz, label, ...props }: InputProps<boolean> = $props();
7
+
8
+ const value = $derived(form.value());
9
+ </script>
10
+
11
+ <div class={['flex flex-col gap-2', clazz]} {...props}>
12
+ <input {...form.as('checkbox')} class="hidden" {value} />
13
+ <button
14
+ type="button"
15
+ class="flex flex-row items-center gap-2"
16
+ onclick={() => {
17
+ form.set(!value);
18
+ }}
19
+ >
20
+ <Toggle {value} />
21
+ {label}
22
+ </button>
23
+ <FormIssues issues={form.issues()} />
24
+ </div>
@@ -1,4 +1,21 @@
1
+ export { default as CheckboxInput } from './CheckboxInput.svelte';
2
+ export { default as ColorInput } from './ColorInput.svelte';
3
+ export { default as DateInput } from './DateInput.svelte';
4
+ export { default as EmailInput } from './EmailInput.svelte';
5
+ export { default as FileInput, type FileInputProps } from './FileInput.svelte';
1
6
  export {
2
7
  default as FullscreenDropzone,
3
8
  type FullscreenDropzoneProps
4
9
  } from './FullscreenDropzone.svelte';
10
+ export { default as Input, type InputProps } from './Input.svelte';
11
+ export { default as NumberInput } from './NumberInput.svelte';
12
+ export {
13
+ default as PasswordCreateInput,
14
+ type PasswordCreateInputProps,
15
+ type PasswordRequirement
16
+ } from './PasswordCreateInput.svelte';
17
+ export { default as PasswordInput } from './PasswordInput.svelte';
18
+ export { default as Select } from './select/Select.svelte';
19
+ export { default as SelectOption } from './select/SelectOption.svelte';
20
+ export { default as TextInput } from './TextInput.svelte';
21
+ export { default as ToggleInput } from './ToggleInput.svelte';
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import { theme } from '$lib/theme.svelte';
3
+ import { merge } from '$lib/utils/merge';
4
+ import { TriangleAlert } from '@lucide/svelte';
5
+ import type { RemoteFormIssue } from '@sveltejs/kit';
6
+ import type { ClassValue } from 'svelte/elements';
7
+ import { slide } from 'svelte/transition';
8
+
9
+ interface Props {
10
+ issues?: RemoteFormIssue[];
11
+ class?: ClassValue;
12
+ }
13
+
14
+ let { issues = [], class: clazz }: Props = $props();
15
+ </script>
16
+
17
+ {#each issues as err, i (i)}
18
+ <div
19
+ class={merge(
20
+ 'text-error-500 flex h-fit w-full flex-row items-start justify-center gap-2 overflow-hidden pl-1',
21
+ theme.current.input?.issues?.issue?.class,
22
+ clazz
23
+ )}
24
+ transition:slide={{ axis: 'y' }}
25
+ >
26
+ <TriangleAlert size="18" class="my-auto" />
27
+ <p class="flex grow">
28
+ {#if theme.current.input?.issues?.issue?.label}
29
+ {theme.current.input?.issues?.issue?.label(err)}
30
+ {:else}
31
+ {err.message}
32
+ {/if}
33
+ </p>
34
+ </div>
35
+ {/each}
@@ -0,0 +1,69 @@
1
+ <script lang="ts" module>
2
+ import { Popover } from '$lib/components/layout';
3
+ import { ChevronDown } from '@lucide/svelte';
4
+ import { createContext, type Snippet } from 'svelte';
5
+ import Input, { type InputProps } from '../Input.svelte';
6
+
7
+ export interface SelectContext {
8
+ select: (value: string, snippet: Snippet) => void;
9
+ value: string | undefined | null;
10
+ }
11
+
12
+ export const [getSelectContext, setSelectContext] = createContext<SelectContext>();
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ interface Props extends InputProps<string> {
17
+ placeholder?: string;
18
+ children: Snippet;
19
+ }
20
+
21
+ let { placeholder = '', children, ...props }: Props = $props();
22
+
23
+ let button = $state<HTMLButtonElement | undefined>();
24
+ let buttonWidth = $state<number>(200);
25
+ let popover = $state<Popover>();
26
+ let selectedSnippet = $state<Snippet>();
27
+
28
+ const value = $derived(props.form.value());
29
+
30
+ function select(newValue: string, snippet: Snippet) {
31
+ selectedSnippet = snippet;
32
+ if (newValue === value) return;
33
+ props.form.set(newValue);
34
+ popover?.close();
35
+ }
36
+
37
+ setSelectContext({
38
+ select,
39
+ get value() {
40
+ return value;
41
+ }
42
+ });
43
+ </script>
44
+
45
+ <Input {...props}>
46
+ <button
47
+ type="button"
48
+ class="flex h-14 w-full flex-row items-center justify-start gap-2 px-4 py-2"
49
+ onclick={popover?.toggle}
50
+ bind:this={button}
51
+ bind:clientWidth={buttonWidth}
52
+ >
53
+ {#if selectedSnippet}
54
+ {@render selectedSnippet()}
55
+ {:else}
56
+ {placeholder}
57
+ {/if}
58
+ <ChevronDown class={['ml-auto transition-all', popover?.isOpen() && 'rotate-180']} />
59
+ </button>
60
+ </Input>
61
+
62
+ <Popover
63
+ bind:this={popover}
64
+ class="bg-surface-50-950 mt-2 flex flex-col gap-2 rounded p-2 shadow"
65
+ style={`min-width: ${buttonWidth}px`}
66
+ target={button}
67
+ >
68
+ {@render children()}
69
+ </Popover>
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { type Snippet } from 'svelte';
3
+ import type { ClassValue } from 'svelte/elements';
4
+ import { getSelectContext } from './Select.svelte';
5
+
6
+ interface Props {
7
+ class?: ClassValue;
8
+ value: string;
9
+ children: Snippet;
10
+ }
11
+
12
+ let {
13
+ class: clazz = 'flex flex-row justify-start items-center gap-2',
14
+ value,
15
+ children
16
+ }: Props = $props();
17
+
18
+ const context = getSelectContext();
19
+
20
+ // this needs to run in an effect so we can update the value from outside the component
21
+ $effect(function setValue() {
22
+ if (context.value === value) context.select(value, children);
23
+ });
24
+ </script>
25
+
26
+ <button
27
+ type="button"
28
+ class={[clazz]}
29
+ onclick={() => {
30
+ context.select(value, children);
31
+ }}
32
+ >
33
+ {@render children()}
34
+ </button>
@@ -1,9 +1,8 @@
1
1
  <script lang="ts" module>
2
2
  import { theme } from '$lib/theme.svelte';
3
3
  import type { IvoryComponent } from '$lib/types';
4
- import clsx from 'clsx';
4
+ import { merge } from '$lib/utils/merge';
5
5
  import type { ClassValue } from 'svelte/elements';
6
- import { twMerge } from 'tailwind-merge';
7
6
 
8
7
  export interface HeadingProps extends IvoryComponent<HTMLHeadingElement> {
9
8
  class?: ClassValue;
@@ -15,8 +14,10 @@
15
14
  </script>
16
15
 
17
16
  <h2
18
- class={twMerge(
19
- clsx('shrink-0 truncate text-lg font-bold select-none', theme.current.heading?.class, clazz)
17
+ class={merge(
18
+ 'shrink-0 truncate text-lg font-bold select-none',
19
+ theme.current.heading?.class,
20
+ clazz
20
21
  )}
21
22
  {...rest}
22
23
  >
@@ -1,11 +1,10 @@
1
1
  <script lang="ts" module>
2
2
  import { theme } from '$lib/theme.svelte';
3
3
  import type { IvoryComponent, TransitionProps } from '$lib/types';
4
- import clsx from 'clsx';
4
+ import { merge } from '$lib/utils/merge';
5
5
  import { onMount, tick } from 'svelte';
6
6
  import type { MouseEventHandler } from 'svelte/elements';
7
7
  import { fade } from 'svelte/transition';
8
- import { twMerge } from 'tailwind-merge';
9
8
 
10
9
  export interface DialogProps extends IvoryComponent<HTMLElement>, TransitionProps {
11
10
  /** Gets called when the dialog requests to close (Escape, backdrop click) */
@@ -61,12 +60,10 @@
61
60
  onclick={handleBackdropClick}
62
61
  oncancel={requestClose}
63
62
  onclose={close}
64
- class={twMerge(
65
- clsx(
66
- 'h-full max-h-none w-screen max-w-full bg-transparent backdrop:bg-transparent',
67
- theme.current.dialog?.class,
68
- clazz
69
- )
63
+ class={merge(
64
+ 'h-full max-h-none w-screen max-w-full bg-transparent backdrop:bg-transparent',
65
+ theme.current.dialog?.class,
66
+ clazz
70
67
  )}
71
68
  in:inTransition
72
69
  out:outTransition
@@ -1,10 +1,9 @@
1
1
  <script lang="ts" module>
2
2
  import type { TransitionProps } from '$lib/types';
3
+ import { merge } from '$lib/utils/merge';
3
4
  import { X } from '@lucide/svelte';
4
- import clsx from 'clsx';
5
5
  import type { Snippet } from 'svelte';
6
6
  import { fly } from 'svelte/transition';
7
- import { twMerge } from 'tailwind-merge';
8
7
  import { HiddenBackground } from '..';
9
8
  import Heading from '../Heading.svelte';
10
9
 
@@ -58,14 +57,12 @@
58
57
  }}
59
58
  >
60
59
  <div
61
- class={twMerge(
62
- clsx([
63
- 'bg-surface-50-950 absolute top-0 flex h-full flex-col gap-4 p-4',
64
- placement === 'left' && 'left-0',
65
- placement === 'right' && 'right-0',
66
- clazz
67
- ])
68
- )}
60
+ class={merge([
61
+ 'bg-surface-50-950 absolute top-0 flex h-full flex-col gap-4 p-4',
62
+ placement === 'left' && 'left-0',
63
+ placement === 'right' && 'right-0',
64
+ clazz
65
+ ])}
69
66
  onclick={(e) => e.stopPropagation()}
70
67
  in:inTransition|global
71
68
  out:outTransition|global
@@ -1,16 +1,12 @@
1
1
  export { default as Drawer } from './drawer/Drawer.svelte';
2
2
  export { default as Heading } from './Heading.svelte';
3
- export {
4
- default as HiddenBackground,
5
- type HiddenBackgroundProps
6
- } from './hiddenBackground/HiddenBackground.svelte';
3
+ export { default as HiddenBackground } from './hiddenBackground/HiddenBackground.svelte';
7
4
  export { default as Modal, type ModalProps, type ModalVariant } from './modal/Modal.svelte';
8
5
  export {
9
6
  default as Popover,
10
7
  type PopoverPlacement,
11
8
  type PopoverProps
12
9
  } from './popover/Popover.svelte';
13
- export { default as Portal } from './portal';
14
10
  export {
15
11
  getTabContext,
16
12
  Tab,
@@ -1,12 +1,11 @@
1
1
  <script lang="ts" module>
2
2
  import { theme } from '$lib/theme.svelte';
3
3
  import type { TransitionProps } from '$lib/types';
4
+ import { merge } from '$lib/utils/merge';
4
5
  import { X } from '@lucide/svelte';
5
- import clsx from 'clsx';
6
6
  import { type Snippet } from 'svelte';
7
7
  import type { ClassValue, MouseEventHandler } from 'svelte/elements';
8
8
  import { fade } from 'svelte/transition';
9
- import { twMerge } from 'tailwind-merge';
10
9
  import { Heading, HiddenBackground } from '..';
11
10
 
12
11
  /** Props for the modal, expose if you overwrite the defaults in a custom component */
@@ -95,13 +94,11 @@
95
94
  </div>
96
95
  {:else}
97
96
  <div
98
- class={twMerge(
99
- clsx([
100
- 'bg-surface-50-950 relative flex max-h-full max-w-full flex-col overflow-hidden rounded',
101
- theme.current.modal?.class,
102
- clazz
103
- ])
104
- )}
97
+ class={merge([
98
+ 'bg-surface-50-950 flex max-h-full max-w-full flex-col overflow-hidden rounded',
99
+ theme.current.modal?.class,
100
+ clazz
101
+ ])}
105
102
  {...rest}
106
103
  {onclick}
107
104
  in:inTransition|global
@@ -131,12 +128,10 @@
131
128
  </button>
132
129
  </div>
133
130
  <div
134
- class={twMerge(
135
- clsx(
136
- 'flex grow flex-col gap-4 overflow-hidden bg-inherit p-4 pt-3',
137
- theme.current.modal?.innerClass,
138
- innerClass
139
- )
131
+ class={merge(
132
+ 'flex grow flex-col gap-4 overflow-hidden bg-inherit p-4 pt-3',
133
+ theme.current.modal?.innerClass,
134
+ innerClass
140
135
  )}
141
136
  >
142
137
  {@render children?.()}