uisv 0.0.22 → 0.0.23

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 (41) hide show
  1. package/dist/components/accordion.svelte +2 -1
  2. package/dist/components/alert.svelte +2 -2
  3. package/dist/components/app.svelte +28 -22
  4. package/dist/components/app.svelte.d.ts +2 -9
  5. package/dist/components/banner.svelte +2 -2
  6. package/dist/components/breadcrumb.svelte +3 -4
  7. package/dist/components/breadcrumb.svelte.d.ts +1 -1
  8. package/dist/components/button.svelte +2 -2
  9. package/dist/components/checkbox.svelte +3 -2
  10. package/dist/components/collapsible.svelte +3 -1
  11. package/dist/components/index.d.ts +1 -0
  12. package/dist/components/index.js +1 -0
  13. package/dist/components/input-number.svelte +3 -2
  14. package/dist/components/input.svelte +8 -17
  15. package/dist/components/modal.svelte +2 -2
  16. package/dist/components/progress.svelte +42 -7
  17. package/dist/components/progress.svelte.d.ts +3 -2
  18. package/dist/components/select.svelte +2 -1
  19. package/dist/components/seperator.svelte +1 -1
  20. package/dist/components/switch.svelte +2 -1
  21. package/dist/components/toast.svelte +173 -0
  22. package/dist/components/toast.svelte.d.ts +8 -0
  23. package/dist/components/toast.svelte.js +11 -0
  24. package/dist/contexts.d.ts +10 -3
  25. package/dist/contexts.js +2 -2
  26. package/dist/index.d.ts +1 -2
  27. package/dist/index.js +1 -2
  28. package/dist/utilities/index.d.ts +5 -0
  29. package/dist/utilities/index.js +5 -0
  30. package/dist/utilities/isComponent.d.ts +7 -0
  31. package/dist/utilities/isComponent.js +10 -0
  32. package/dist/utilities/isSnippet.d.ts +7 -0
  33. package/dist/utilities/isSnippet.js +8 -0
  34. package/dist/utilities/useElementRects.svelte.d.ts +11 -0
  35. package/dist/{utilities.svelte.js → utilities/useElementRects.svelte.js} +0 -38
  36. package/dist/utilities/useRafFn.svelte.d.ts +43 -0
  37. package/dist/utilities/useRafFn.svelte.js +56 -0
  38. package/dist/utilities/useStyle.svelte.d.ts +8 -0
  39. package/dist/utilities/useStyle.svelte.js +21 -0
  40. package/package.json +11 -4
  41. package/dist/utilities.svelte.d.ts +0 -31
@@ -1,4 +1,5 @@
1
1
  <script module lang="ts">
2
+ import { getAppContext } from '../contexts.js';
2
3
  import { Accordion } from 'bits-ui';
3
4
  import type { Component, Snippet } from 'svelte';
4
5
  import { tv, type ClassValue } from 'tailwind-variants';
@@ -44,7 +45,7 @@
44
45
  collapsible = true,
45
46
  disabled,
46
47
  type = 'single',
47
- trailingicon = 'i-lucide-baret-down',
48
+ trailingicon = getAppContext().icons.chevrondown,
48
49
  leading,
49
50
  default: defau,
50
51
  trailing,
@@ -10,7 +10,7 @@
10
10
  import type { Component, Snippet } from 'svelte';
11
11
  import { tv, type ClassValue } from 'tailwind-variants';
12
12
  import { defu } from 'defu';
13
- import { app_icons } from '../contexts.js';
13
+ import { getAppContext } from '../contexts.js';
14
14
 
15
15
  export type AlertProps = {
16
16
  title?: string | Snippet;
@@ -47,7 +47,7 @@
47
47
 
48
48
  const close_props = $derived.by(() => {
49
49
  return defu(typeof close === 'boolean' ? {} : close, {
50
- icon: app_icons.get().close,
50
+ icon: getAppContext().icons.close,
51
51
  variant: 'link',
52
52
  color: variant === 'solid' ? 'surface' : color,
53
53
  ui: {
@@ -1,57 +1,63 @@
1
1
  <script module lang="ts">
2
- import { app_icons, DEFAULT_ICONS, type AppIcons } from '../contexts.js';
3
- import { ModeWatcher, type ModeWatcherProps } from '../mode.js';
2
+ import { DEFAULT_ICONS, setAppContext, type AppContext } from '../contexts.js';
3
+ import { mode, ModeWatcher } from '../mode.js';
4
4
  import { Tooltip } from 'bits-ui';
5
5
  import defu from 'defu';
6
6
  import { type Snippet } from 'svelte';
7
- import { Toaster, type ToasterProps } from 'svelte-sonner';
7
+ import { Toaster } from 'svelte-sonner';
8
8
  import { boxWith } from 'svelte-toolbelt';
9
9
  import { Icon } from './index.js';
10
10
 
11
- export type AppProps = {
11
+ export type AppProps = Partial<AppContext> & {
12
12
  children?: Snippet;
13
- modewatcher?: ModeWatcherProps;
14
- toaster?: ToasterProps;
15
- tooltip?: Tooltip.ProviderProps;
16
- icons?: Partial<Record<AppIcons, `i-${string}:${string}`>>;
17
13
  };
18
14
  </script>
19
15
 
20
16
  <script lang="ts">
21
- let { children, modewatcher, toaster, tooltip, icons = {} }: AppProps = $props();
22
-
23
- const _icons = boxWith(() => defu(icons, DEFAULT_ICONS));
24
-
25
- app_icons.set(_icons.current);
17
+ let { children, modewatcher = {}, toaster = {}, tooltip = {}, icons = {} }: AppProps = $props();
18
+
19
+ const context = boxWith(() =>
20
+ defu({ icons, toaster, modewatcher, tooltip }, <AppContext>{
21
+ icons: DEFAULT_ICONS,
22
+ toaster: {
23
+ visibleToasts: 5,
24
+ duration: 6000,
25
+ theme: mode.current,
26
+ },
27
+ modewatcher: {},
28
+ }),
29
+ );
30
+ setAppContext(context.current);
26
31
  </script>
27
32
 
28
- <ModeWatcher {...modewatcher} />
29
- <Toaster {...toaster}>
33
+ <ModeWatcher {...context.current.modewatcher} />
34
+
35
+ <Toaster {...context.current.toaster}>
30
36
  {#snippet infoIcon()}
31
- <Icon name={app_icons.get().info} />
37
+ <Icon name={context.current.icons.info} />
32
38
  {/snippet}
33
39
 
34
40
  {#snippet closeIcon()}
35
- <Icon name={app_icons.get().close} />
41
+ <Icon name={context.current.icons.close} />
36
42
  {/snippet}
37
43
 
38
44
  {#snippet errorIcon()}
39
- <Icon name={app_icons.get().error} />
45
+ <Icon name={context.current.icons.error} />
40
46
  {/snippet}
41
47
 
42
48
  {#snippet loadingIcon()}
43
- <Icon name={app_icons.get().loading} />
49
+ <Icon name={context.current.icons.loading} />
44
50
  {/snippet}
45
51
 
46
52
  {#snippet successIcon()}
47
- <Icon name={app_icons.get().success} />
53
+ <Icon name={context.current.icons.success} />
48
54
  {/snippet}
49
55
 
50
56
  {#snippet warningIcon()}
51
- <Icon name={app_icons.get().warning} />
57
+ <Icon name={context.current.icons.warning} />
52
58
  {/snippet}
53
59
  </Toaster>
54
60
 
55
- <Tooltip.Provider {...tooltip}>
61
+ <Tooltip.Provider {...context.current.tooltip}>
56
62
  {@render children?.()}
57
63
  </Tooltip.Provider>
@@ -1,14 +1,7 @@
1
- import { type AppIcons } from '../contexts.js';
2
- import { type ModeWatcherProps } from '../mode.js';
3
- import { Tooltip } from 'bits-ui';
1
+ import { type AppContext } from '../contexts.js';
4
2
  import { type Snippet } from 'svelte';
5
- import { type ToasterProps } from 'svelte-sonner';
6
- export type AppProps = {
3
+ export type AppProps = Partial<AppContext> & {
7
4
  children?: Snippet;
8
- modewatcher?: ModeWatcherProps;
9
- toaster?: ToasterProps;
10
- tooltip?: Tooltip.ProviderProps;
11
- icons?: Partial<Record<AppIcons, `i-${string}:${string}`>>;
12
5
  };
13
6
  declare const App: import("svelte").Component<AppProps, {}, "">;
14
7
  type App = ReturnType<typeof App>;
@@ -10,7 +10,7 @@
10
10
  import type { Component, Snippet } from 'svelte';
11
11
  import { tv, type ClassValue } from 'tailwind-variants';
12
12
  import { defu } from 'defu';
13
- import { app_icons } from '../contexts.js';
13
+ import { getAppContext } from '../contexts.js';
14
14
 
15
15
  export type BannerProps = {
16
16
  title: string | Snippet;
@@ -238,7 +238,7 @@
238
238
  <div>
239
239
  <Button
240
240
  {...defu<ButtonProps, [ButtonProps]>(typeof close === 'boolean' ? {} : close, {
241
- icon: app_icons.get().close,
241
+ icon: getAppContext().icons.close,
242
242
  variant: 'ghost',
243
243
  color: 'surface',
244
244
  ui: {
@@ -1,9 +1,8 @@
1
1
  <script module lang="ts">
2
2
  import type { Component, Snippet } from 'svelte';
3
- import { Button, Icon, type ButtonProps } from './index.js';
3
+ import { Button, Icon, type ButtonProps, isSnippet } from '../index.js';
4
4
  import { tv } from 'tailwind-variants';
5
- import { isSnippet } from '../utilities.svelte.js';
6
- import { app_icons } from '../contexts.js';
5
+ import { getAppContext } from '../contexts.js';
7
6
 
8
7
  export type BreadcrumbItem = Omit<ButtonProps, 'label'> & {
9
8
  label?: string;
@@ -24,7 +23,7 @@
24
23
  let {
25
24
  items,
26
25
  labelkey = 'label',
27
- seperator = app_icons.get().chevronright,
26
+ seperator = getAppContext().icons.chevronright,
28
27
  ...rest
29
28
  }: BreadcrumbProps = $props();
30
29
 
@@ -1,5 +1,5 @@
1
1
  import type { Component, Snippet } from 'svelte';
2
- import { type ButtonProps } from './index.js';
2
+ import { type ButtonProps } from '../index.js';
3
3
  export type BreadcrumbItem = Omit<ButtonProps, 'label'> & {
4
4
  label?: string;
5
5
  icon?: string | Component;
@@ -4,7 +4,7 @@
4
4
  // import { FORM_LOADING_CONTEXT_KEY } from '../utils/keys.js';
5
5
  import type { SvelteHTMLElements } from 'svelte/elements';
6
6
  import { tv, type ClassValue } from 'tailwind-variants';
7
- import { app_icons } from '../contexts.js';
7
+ import { getAppContext } from '../contexts.js';
8
8
 
9
9
  export type ButtonBaseProps = {
10
10
  /** The underlying DOM element being rendered. You can bind to this to get a reference to the element. */
@@ -80,7 +80,7 @@
80
80
  href,
81
81
  icon,
82
82
  loading,
83
- loadingicon = app_icons.get().loading,
83
+ loadingicon = getAppContext().icons.loading,
84
84
  type,
85
85
  trailingicon,
86
86
  leadingicon,
@@ -1,4 +1,5 @@
1
1
  <script module lang="ts">
2
+ import { getAppContext } from '../contexts.js';
2
3
  import { type PropColor, isComponent, isSnippet } from '../index.js';
3
4
  import type { Snippet } from 'svelte';
4
5
  import { tv, type ClassValue } from 'tailwind-variants';
@@ -32,8 +33,8 @@
32
33
  color = 'primary',
33
34
  size = 'md',
34
35
  disabled,
35
- icon = 'i-lucide-check',
36
- intermediateicon = 'i-lucide-minus',
36
+ icon = getAppContext().icons.check,
37
+ intermediateicon = getAppContext().icons.minus,
37
38
  label,
38
39
  description,
39
40
  required,
@@ -16,6 +16,8 @@
16
16
  </script>
17
17
 
18
18
  <script lang="ts">
19
+ import { getAppContext } from '../contexts.js';
20
+
19
21
  let {
20
22
  open = $bindable(false),
21
23
  children,
@@ -40,7 +42,7 @@
40
42
  {#snippet child({ props })}
41
43
  <Button
42
44
  {...props}
43
- trailingicon="i-lucide-chevron-down"
45
+ trailingicon={getAppContext().icons.chevrondown}
44
46
  {...rest}
45
47
  block
46
48
  ui={defu(ui, { trailingicon: 'ms-auto' })}
@@ -61,3 +61,4 @@ export * from './tooltip.svelte';
61
61
  export { default as Tooltip } from './tooltip.svelte';
62
62
  export * from './app.svelte';
63
63
  export { default as App } from './app.svelte';
64
+ export * from './toast.svelte.js';
@@ -61,3 +61,4 @@ export * from './tooltip.svelte';
61
61
  export { default as Tooltip } from './tooltip.svelte';
62
62
  export * from './app.svelte';
63
63
  export { default as App } from './app.svelte';
64
+ export * from './toast.svelte.js';
@@ -9,6 +9,7 @@
9
9
  import type { SvelteHTMLElements } from 'svelte/elements';
10
10
  import { tv, type ClassValue } from 'tailwind-variants';
11
11
  import { useId } from 'bits-ui';
12
+ import { getAppContext } from '../contexts.js';
12
13
 
13
14
  export type InputNumberProps = Omit<SvelteHTMLElements['input'], 'size' | 'value'> & {
14
15
  value?: number;
@@ -154,7 +155,7 @@
154
155
  {#if increment}
155
156
  <Button
156
157
  variant="link"
157
- icon="i-lucide:minus"
158
+ icon={getAppContext().icons.minus}
158
159
  onclick={() => {
159
160
  value = value === undefined ? 0 : value - 1;
160
161
  }}
@@ -164,7 +165,7 @@
164
165
  {#if increment}
165
166
  <Button
166
167
  variant="link"
167
- icon="i-lucide:plus"
168
+ icon={getAppContext().icons.plus}
168
169
  onclick={() => {
169
170
  value = value === undefined ? 0 : value + 1;
170
171
  }}
@@ -1,10 +1,11 @@
1
1
  <script module lang="ts">
2
- import { type PropColor, type PropVariant, isComponent, isSnippet } from '../index.js';
2
+ import { type PropColor, type PropVariant, isComponent, isSnippet, Icon } from '../index.js';
3
3
  import type { Component, Snippet } from 'svelte';
4
4
  import type { SvelteHTMLElements } from 'svelte/elements';
5
5
  import { maska } from 'maska/svelte';
6
6
  import { type MaskInputOptions } from 'maska';
7
7
  import { tv, type ClassValue } from 'tailwind-variants';
8
+ import { getAppContext } from '../contexts.js';
8
9
 
9
10
  export type InputProps = Omit<SvelteHTMLElements['input'], 'size'> & {
10
11
  name?: string;
@@ -56,8 +57,6 @@
56
57
  </script>
57
58
 
58
59
  <script lang="ts">
59
- import Icon from './icon.svelte';
60
-
61
60
  let {
62
61
  type,
63
62
  value = $bindable(),
@@ -70,7 +69,7 @@
70
69
  highlight,
71
70
  leading,
72
71
  loading,
73
- loadingicon = 'i-lucide-loader-circle',
72
+ loadingicon = getAppContext().icons.loading,
74
73
  required,
75
74
  trailing,
76
75
  mask,
@@ -223,7 +222,7 @@
223
222
 
224
223
  <div class={variants.root({ class: ui.root })}>
225
224
  {#if leading || (icon && iconposition === 'leading') || loading}
226
- {@const TrailingIcon = loading ? loadingicon : icon}
225
+ {@const leadingicon = loading ? loadingicon : icon}
227
226
 
228
227
  <span class={variants.leading({ class: ui.leading })}>
229
228
  {#if !!leading && !loading}
@@ -237,7 +236,7 @@
237
236
  {/if}
238
237
  {:else}
239
238
  <Icon
240
- name={TrailingIcon}
239
+ name={leadingicon}
241
240
  class={variants.icon({ class: [loading ? 'animate-spin' : ''] })}
242
241
  />
243
242
  {/if}
@@ -251,6 +250,7 @@
251
250
  class={variants.base({ class: [ui.base] })}
252
251
  {...rest}
253
252
  use:maska={mask}
253
+ bind:value
254
254
  />
255
255
 
256
256
  {#if trailing || (icon && iconposition === 'trailing')}
@@ -264,17 +264,8 @@
264
264
  {:else if isSnippet(trailing)}
265
265
  {@render trailing()}
266
266
  {/if}
267
- {:else if typeof icon === 'string'}
268
- <div
269
- class={variants.icon({
270
- class: [icon, ui.icon],
271
- })}
272
- ></div>
273
- {:else if isSnippet(icon)}
274
- {@render icon()}
275
- {:else if isComponent(icon)}
276
- {@const Icon = icon}
277
- <Icon class={variants.icon({ class: [ui.icon] })} />
267
+ {:else}
268
+ <Icon name={icon} class={variants.icon({ class: [ui.icon] })} />
278
269
  {/if}
279
270
  </span>
280
271
  {/if}
@@ -6,7 +6,7 @@
6
6
  import defu from 'defu';
7
7
  import { fade, scale } from 'svelte/transition';
8
8
  import { cubicIn } from 'svelte/easing';
9
- import { app_icons } from '../contexts.js';
9
+ import { getAppContext } from '../contexts.js';
10
10
 
11
11
  export type ModalProps = {
12
12
  open?: boolean;
@@ -160,7 +160,7 @@
160
160
  {...defu(typeof close === 'boolean' ? {} : close, <ButtonProps>{
161
161
  variant: 'ghost',
162
162
  color: 'surface',
163
- icon: app_icons.get().close,
163
+ icon: getAppContext().icons.close,
164
164
  onclick() {
165
165
  open = false;
166
166
  },
@@ -6,9 +6,9 @@
6
6
  value?: number;
7
7
  max?: number | string[];
8
8
  animation?: 'swing' | 'carousel' | 'carousel-inverse' | 'elastic';
9
- orientation?: 'horizontal' | 'veritcal';
9
+ orientation?: 'horizontal' | 'vertical';
10
10
  color?: PropColor;
11
- height?: number;
11
+ size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
12
12
  inverted?: boolean;
13
13
  status?: boolean;
14
14
  ui?: {
@@ -16,6 +16,7 @@
16
16
  header?: ClassValue;
17
17
  content?: ClassValue;
18
18
  footer?: ClassValue;
19
+ indicator?: ClassValue;
19
20
  };
20
21
  };
21
22
  </script>
@@ -26,10 +27,10 @@
26
27
  animation,
27
28
  inverted,
28
29
  status,
29
- value,
30
+ value = 0,
30
31
  orientation = 'horizontal',
31
32
  color = 'primary',
32
- height = 8,
33
+ size = 'md',
33
34
  ui = {},
34
35
  }: ProgressProps = $props();
35
36
 
@@ -75,6 +76,19 @@
75
76
  indicator: 'bg-error-500',
76
77
  },
77
78
  },
79
+ size: {
80
+ '2xs': '',
81
+ xs: '',
82
+ sm: '',
83
+ md: '',
84
+ lg: '',
85
+ xl: '',
86
+ '2xl': '',
87
+ },
88
+ orientation: {
89
+ vertical: { indicator: 'w-full top-0 h-(--ui-progress-percentage)' },
90
+ horizontal: { indicator: 'h-full left-0 w-(--ui-progress-percentage)' },
91
+ },
78
92
  animation: {
79
93
  swing: [indeterminate ? 'animate-[swing_2s_ease-in-out_infinite' : ''],
80
94
  carousel: [indeterminate ? '' : ''],
@@ -82,17 +96,38 @@
82
96
  elastic: [indeterminate ? '' : ''],
83
97
  },
84
98
  },
85
- compoundVariants: [],
99
+ compoundVariants: [
100
+ { orientation: 'horizontal', size: '2xs', class: { root: 'h-px' } },
101
+ { orientation: 'horizontal', size: 'xs', class: { root: 'h-0.5' } },
102
+ { orientation: 'horizontal', size: 'sm', class: { root: 'h-1' } },
103
+ { orientation: 'horizontal', size: 'md', class: { root: 'h-2' } },
104
+ { orientation: 'horizontal', size: 'lg', class: { root: 'h-3' } },
105
+ { orientation: 'horizontal', size: 'xl', class: { root: 'h-4' } },
106
+ { orientation: 'horizontal', size: '2xl', class: { root: 'h-5' } },
107
+
108
+ { orientation: 'vertical', size: '2xs', class: { root: 'w-px' } },
109
+ { orientation: 'vertical', size: 'xs', class: { root: 'w-0.5' } },
110
+ { orientation: 'vertical', size: 'sm', class: { root: 'w-1' } },
111
+ { orientation: 'vertical', size: 'md', class: { root: 'w-2' } },
112
+ { orientation: 'vertical', size: 'lg', class: { root: 'w-3' } },
113
+ { orientation: 'vertical', size: 'xl', class: { root: 'w-4' } },
114
+ { orientation: 'vertical', size: '2xl', class: { root: 'w-5' } },
115
+ ],
86
116
  })({
87
117
  color,
88
118
  animation: animation ?? 'swing',
119
+ size,
120
+ orientation,
89
121
  }),
90
122
  );
91
123
  </script>
92
124
 
93
125
  <div data-state-indeterminate={indeterminate}>
94
- <div class={variants.root({ class: [ui.base] })} style:height={`${height}px`}>
95
- <span class={variants.indicator({ class: ['h-full left-0'] })} style:width={`${percentage}%`}>
126
+ <div class={variants.root({ class: [ui.base] })}>
127
+ <span
128
+ class={variants.indicator({ class: ui.indicator })}
129
+ style:--ui-progress-percentage={`${percentage}%`}
130
+ >
96
131
  </span>
97
132
  </div>
98
133
 
@@ -4,9 +4,9 @@ export type ProgressProps = {
4
4
  value?: number;
5
5
  max?: number | string[];
6
6
  animation?: 'swing' | 'carousel' | 'carousel-inverse' | 'elastic';
7
- orientation?: 'horizontal' | 'veritcal';
7
+ orientation?: 'horizontal' | 'vertical';
8
8
  color?: PropColor;
9
- height?: number;
9
+ size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
10
10
  inverted?: boolean;
11
11
  status?: boolean;
12
12
  ui?: {
@@ -14,6 +14,7 @@ export type ProgressProps = {
14
14
  header?: ClassValue;
15
15
  content?: ClassValue;
16
16
  footer?: ClassValue;
17
+ indicator?: ClassValue;
17
18
  };
18
19
  };
19
20
  declare const Progress: import("svelte").Component<ProgressProps, {}, "">;
@@ -4,6 +4,7 @@
4
4
  import type { Component, Snippet } from 'svelte';
5
5
  import { tv, type ClassValue } from 'tailwind-variants';
6
6
  import Icon from './icon.svelte';
7
+ import { getAppContext } from '../contexts.js';
7
8
 
8
9
  export type SelectItem<T> =
9
10
  | T
@@ -69,7 +70,7 @@
69
70
  highlight,
70
71
  placeholder,
71
72
  ui = {},
72
- dropdownicon = 'i-lucide:chevron-down',
73
+ dropdownicon = getAppContext().icons.chevrondown,
73
74
  }: SelectProps<T> = $props();
74
75
 
75
76
  const variants = $derived(
@@ -198,7 +198,7 @@
198
198
  {:else if typeof label === 'string' && label.length > 0}
199
199
  <span class={variants.label({ class: ui.label })}>{label}</span>
200
200
  {:else if isSnippet(label)}
201
- {@render label()}
201
+ {@render label(variants.label({ class: ui.label }))}
202
202
  {:else if isComponent(label)}
203
203
  {@const Label = label}
204
204
  <Label class={variants.label({ class: ui.label })} />
@@ -1,4 +1,5 @@
1
1
  <script module lang="ts">
2
+ import { getAppContext } from '../contexts.js';
2
3
  import { type PropColor, Icon } from '../index.js';
3
4
  import type { Snippet, Component } from 'svelte';
4
5
  import { tv, type ClassValue } from 'tailwind-variants';
@@ -32,7 +33,7 @@
32
33
  size = 'md',
33
34
  disabled,
34
35
  loading,
35
- loadingicon = 'i-lucide-loader-circle',
36
+ loadingicon = getAppContext().icons.loading,
36
37
  uncheckedicon,
37
38
  checkedicon,
38
39
  label,
@@ -0,0 +1,173 @@
1
+ <script module lang="ts">
2
+ import {
3
+ Button,
4
+ Icon,
5
+ Progress,
6
+ type ButtonProps,
7
+ type ProgressProps,
8
+ type ToastOptions,
9
+ useRafFn,
10
+ } from '../index.js';
11
+ import { boxWith } from 'svelte-toolbelt';
12
+ import { tv } from 'tailwind-variants';
13
+ import defu from 'defu';
14
+ import { onDestroy, onMount } from 'svelte';
15
+ import { getAppContext } from '../contexts.js';
16
+
17
+ export type ToastProps = ToastOptions & {
18
+ closeToast?: () => void;
19
+ expanded?: boolean;
20
+ };
21
+ </script>
22
+
23
+ <script lang="ts">
24
+ let {
25
+ title,
26
+ description,
27
+ closeToast = () => {},
28
+ actions = [],
29
+ close = true,
30
+ color = 'primary',
31
+ duration = getAppContext().toaster.duration!,
32
+ icon,
33
+ progress = true,
34
+ orientation = 'vertical',
35
+ expanded = false,
36
+ ui = {},
37
+ }: ToastProps = $props();
38
+
39
+ let remaining = $state(boxWith(() => duration).current);
40
+
41
+ const { pause, resume } = useRafFn(({ delta }) => (remaining = remaining - delta));
42
+
43
+ const variants = $derived(
44
+ tv({
45
+ slots: {
46
+ base: 'overflow-hidden bg-surface-base shadow-lg rounded-lg border border-surface-accented space-y-4',
47
+ wrapper: 'm-4 flex gap-4',
48
+ title: 'text-sm font-medium text-labe-highlighted',
49
+ description: 'text-sm text-label-muted',
50
+ icon: 'shrink-0 size-5',
51
+ avatar: 'shrink-0',
52
+ avatarSize: '2xl',
53
+ actions: 'flex gap-1.5 shrink-0 m-4',
54
+ progress: '',
55
+ close: 'p-0',
56
+ },
57
+ variants: {
58
+ color: {
59
+ primary: {
60
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary',
61
+ icon: 'text-primary',
62
+ },
63
+ surface: {
64
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted',
65
+ icon: 'text-highlighted',
66
+ },
67
+ success: {
68
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-success',
69
+ icon: 'text-success',
70
+ },
71
+ info: {
72
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-info',
73
+ icon: 'text-info',
74
+ },
75
+ warning: {
76
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-warning',
77
+ icon: 'text-warning',
78
+ },
79
+ error: {
80
+ base: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-error',
81
+ icon: 'text-error',
82
+ },
83
+ },
84
+ orientation: {
85
+ horizontal: {
86
+ base: 'items-center',
87
+ actions: 'items-center',
88
+ },
89
+ vertical: {
90
+ base: 'items-start',
91
+ actions: 'items-start mt-2.5',
92
+ },
93
+ },
94
+ title: {
95
+ true: {
96
+ description: 'mt-1',
97
+ },
98
+ },
99
+ },
100
+ })({ color, title: !!title, orientation }),
101
+ );
102
+
103
+ $effect(() => {
104
+ if (expanded) pause();
105
+ else resume();
106
+ });
107
+
108
+ onMount(resume);
109
+ onDestroy(pause);
110
+ </script>
111
+
112
+ <div class={variants.base({ class: ui.base })}>
113
+ <div class={variants.wrapper({ class: ui.wrapper })}>
114
+ <Icon name={icon} class={variants.icon({ class: ui.icon })} />
115
+
116
+ <div>
117
+ <h1 class={variants.title({ class: ui.title })}>
118
+ {title}
119
+ </h1>
120
+
121
+ {#if description}
122
+ <p class={variants.description({ class: ui.description })}>
123
+ {description}
124
+ </p>
125
+ {/if}
126
+ </div>
127
+
128
+ {#if orientation === 'horizontal'}
129
+ {@render actions_snippet()}
130
+ {/if}
131
+
132
+ {#if close}
133
+ <Button
134
+ {...defu(typeof close === 'boolean' ? {} : close, <ButtonProps>{
135
+ icon: getAppContext().icons.close,
136
+ color: 'surface',
137
+ variant: 'link',
138
+ onclick: closeToast,
139
+ ui: { base: variants.icon({ class: ui.icon }) },
140
+ })}
141
+ />
142
+ {/if}
143
+ </div>
144
+
145
+ {#if orientation === 'vertical'}
146
+ {@render actions_snippet()}
147
+ {/if}
148
+
149
+ {#if progress}
150
+ <Progress
151
+ max={duration}
152
+ value={remaining}
153
+ {...defu(typeof progress === 'object' ? progress : {}, <ProgressProps>{
154
+ size: 'sm',
155
+ ui: { base: variants.progress({ class: ui.progress }) },
156
+ })}
157
+ />
158
+ {/if}
159
+ </div>
160
+
161
+ {#snippet actions_snippet()}
162
+ {#if actions.length > 0}
163
+ <div class={variants.actions({ class: ui.actions })}>
164
+ {#each actions as action, idx (idx)}
165
+ <Button
166
+ {...defu(action, <ButtonProps>{
167
+ size: 'xs',
168
+ })}
169
+ />
170
+ {/each}
171
+ </div>
172
+ {/if}
173
+ {/snippet}
@@ -0,0 +1,8 @@
1
+ import { type ToastOptions } from '../index.js';
2
+ export type ToastProps = ToastOptions & {
3
+ closeToast?: () => void;
4
+ expanded?: boolean;
5
+ };
6
+ declare const Toast: import("svelte").Component<any, {}, "">;
7
+ type Toast = ReturnType<typeof Toast>;
8
+ export default Toast;
@@ -0,0 +1,11 @@
1
+ import { toast as _toast } from 'svelte-sonner';
2
+ import Toast from './toast.svelte';
3
+ import {} from 'svelte';
4
+ import {} from '../index.js';
5
+ export function toast(opts) {
6
+ return _toast.custom(Toast, {
7
+ componentProps: opts,
8
+ id: opts.id,
9
+ });
10
+ }
11
+ export const useToasts = _toast.getActiveToasts;
@@ -1,6 +1,6 @@
1
- import { Context } from 'runed';
2
- export declare const app_icons: Context<Record<"arrowdown" | "arrowleft" | "arrowright" | "arrowup" | "caution" | "check" | "chevrondoubleleft" | "chevrondoubleright" | "chevrondown" | "chevronleft" | "chevronright" | "chevronup" | "close" | "copy" | "copycheck" | "dark" | "drag" | "ellipsis" | "error" | "external" | "eye" | "eyeoff" | "file" | "folder" | "folderopen" | "hash" | "info" | "light" | "loading" | "menu" | "minus" | "panelclose" | "panelopen" | "plus" | "reload" | "search" | "stop" | "success" | "system" | "tip" | "upload" | "warning", string>>;
3
- export type AppIcons = keyof typeof DEFAULT_ICONS;
1
+ import type { ToasterProps } from 'svelte-sonner';
2
+ import type { ModeWatcherProps } from './mode.js';
3
+ import type { TooltipProviderProps } from 'bits-ui';
4
4
  export declare const DEFAULT_ICONS: {
5
5
  arrowdown: string;
6
6
  arrowleft: string;
@@ -45,3 +45,10 @@ export declare const DEFAULT_ICONS: {
45
45
  upload: string;
46
46
  warning: string;
47
47
  };
48
+ export type AppContext = {
49
+ icons: Partial<typeof DEFAULT_ICONS>;
50
+ toaster: Partial<ToasterProps>;
51
+ modewatcher: ModeWatcherProps;
52
+ tooltip: TooltipProviderProps;
53
+ };
54
+ export declare const getAppContext: () => AppContext, setAppContext: (context: AppContext) => AppContext;
package/dist/contexts.js CHANGED
@@ -1,5 +1,4 @@
1
- import { Context } from 'runed';
2
- export const app_icons = new Context('app-config');
1
+ import { createContext } from 'svelte';
3
2
  export const DEFAULT_ICONS = {
4
3
  arrowdown: 'i-lucide:arrow-down',
5
4
  arrowleft: 'i-lucide:arrow-left',
@@ -44,3 +43,4 @@ export const DEFAULT_ICONS = {
44
43
  upload: 'i-lucide:upload',
45
44
  warning: 'i-lucide:warning',
46
45
  };
46
+ export const [getAppContext, setAppContext] = createContext();
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './components/index.js';
2
- export * from './utilities.svelte.js';
2
+ export * from './utilities/index.js';
3
3
  export * from './types.js';
4
4
  export { ModeWatcher } from 'mode-watcher';
5
- export { toast, type ToastOptions } from 'svelte-sonner';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './components/index.js';
2
- export * from './utilities.svelte.js';
2
+ export * from './utilities/index.js';
3
3
  export * from './types.js';
4
4
  export { ModeWatcher } from 'mode-watcher';
5
- export { toast } from 'svelte-sonner';
@@ -0,0 +1,5 @@
1
+ export * from './isComponent.js';
2
+ export * from './isSnippet.js';
3
+ export * from './useElementRects.svelte.js';
4
+ export * from './useRafFn.svelte.js';
5
+ export * from './useStyle.svelte.js';
@@ -0,0 +1,5 @@
1
+ export * from './isComponent.js';
2
+ export * from './isSnippet.js';
3
+ export * from './useElementRects.svelte.js';
4
+ export * from './useRafFn.svelte.js';
5
+ export * from './useStyle.svelte.js';
@@ -0,0 +1,7 @@
1
+ import type { Component } from 'svelte';
2
+ /**
3
+ * Checks if a value is a Svelte component
4
+ * @param v - The value to check
5
+ * @returns true if the value is a component, false otherwise
6
+ */
7
+ export declare function isComponent(v: unknown): v is Component;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Checks if a value is a Svelte component
3
+ * @param v - The value to check
4
+ * @returns true if the value is a component, false otherwise
5
+ */
6
+ export function isComponent(v) {
7
+ if (typeof document !== 'undefined')
8
+ return typeof v === 'function';
9
+ return typeof v === 'function' && 'render' in v;
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from 'svelte';
2
+ /**
3
+ * Checks if a value is a Svelte snippet
4
+ * @param v - The value to check
5
+ * @returns true if the value is a snippet, false otherwise
6
+ */
7
+ export declare function isSnippet<T>(v: unknown): v is Snippet<[T]>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Checks if a value is a Svelte snippet
3
+ * @param v - The value to check
4
+ * @returns true if the value is a snippet, false otherwise
5
+ */
6
+ export function isSnippet(v) {
7
+ return typeof v === 'function' && !('render' in v);
8
+ }
@@ -0,0 +1,11 @@
1
+ import { type ElementSizeOptions, type MaybeGetter } from 'runed';
2
+ /**
3
+ * Returns a reactive value holding the dom rect of `node`s.
4
+ *
5
+ * Accepts an `options` object with the following properties:
6
+ * - `initialSize`: The initial size of the element. Defaults to `{ width: 0, height: 0 }`.
7
+ * - `box`: The box model to use. Can be either `"content-box"` or `"border-box"`. Defaults to `"border-box"`.
8
+ *
9
+ * @returns an array of dom rects.
10
+ */
11
+ export declare function useElementRects(nodes: MaybeGetter<HTMLElement[]>, options?: ElementSizeOptions): DOMRect[];
@@ -1,22 +1,4 @@
1
1
  import { extract, useMutationObserver, useResizeObserver, } from 'runed';
2
- /**
3
- * Checks if a value is a Svelte component
4
- * @param v - The value to check
5
- * @returns true if the value is a component, false otherwise
6
- */
7
- export function isComponent(v) {
8
- if (typeof document !== 'undefined')
9
- return typeof v === 'function';
10
- return typeof v === 'function' && 'render' in v;
11
- }
12
- /**
13
- * Checks if a value is a Svelte snippet
14
- * @param v - The value to check
15
- * @returns true if the value is a snippet, false otherwise
16
- */
17
- export function isSnippet(v) {
18
- return typeof v === 'function' && !('render' in v);
19
- }
20
2
  /**
21
3
  * Returns a reactive value holding the dom rect of `node`s.
22
4
  *
@@ -47,23 +29,3 @@ export function useElementRects(nodes, options = {}) {
47
29
  });
48
30
  return rects;
49
31
  }
50
- let uisv_usestyle_id = 0;
51
- /**
52
- * Inject reactive style element in head.
53
- * @param css string
54
- */
55
- export function useStyle(css) {
56
- const id = `uisv_styletag_${++uisv_usestyle_id}`;
57
- let el = $state();
58
- $effect(() => {
59
- if (!el) {
60
- el = (document.getElementById(id) || document.createElement('style'));
61
- if (!el.isConnected) {
62
- el.id = id;
63
- document.head.appendChild(el);
64
- }
65
- }
66
- el.textContent = extract(css);
67
- });
68
- return { id };
69
- }
@@ -0,0 +1,43 @@
1
+ export interface UseRafFnCallbackArguments {
2
+ /**
3
+ * Time elapsed between this and the last frame.
4
+ */
5
+ delta: number;
6
+ /**
7
+ * Time elapsed since the creation of the web page. See {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#the_time_origin Time origin}.
8
+ */
9
+ timestamp: DOMHighResTimeStamp;
10
+ }
11
+ export interface UseRafFnOptions {
12
+ /**
13
+ * Start the requestAnimationFrame loop immediately on creation
14
+ *
15
+ * @default true
16
+ */
17
+ immediate?: boolean;
18
+ /**
19
+ * The maximum frame per second to execute the function.
20
+ * Set to `-1` to disable the limit.
21
+ *
22
+ * @default -1
23
+ */
24
+ fpslimit?: number;
25
+ /**
26
+ * After the requestAnimationFrame loop executed once, it will be automatically stopped.
27
+ *
28
+ * @default false
29
+ */
30
+ once?: boolean;
31
+ }
32
+ /**
33
+ * Call function on every `requestAnimationFrame`. With controls of pausing and resuming.
34
+ *
35
+ * @see https://vueuse.org/useRafFn
36
+ * @param fn
37
+ * @param options
38
+ */
39
+ export declare function useRafFn(fn: (args: UseRafFnCallbackArguments) => void, options?: UseRafFnOptions): {
40
+ is_active: boolean;
41
+ pause: () => void;
42
+ resume: () => void;
43
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Call function on every `requestAnimationFrame`. With controls of pausing and resuming.
3
+ *
4
+ * @see https://vueuse.org/useRafFn
5
+ * @param fn
6
+ * @param options
7
+ */
8
+ export function useRafFn(fn, options = {}) {
9
+ const { immediate = true, fpslimit = -1, once = false } = options;
10
+ let is_active = $state(false);
11
+ const interval_limit = $derived.by(() => {
12
+ return fpslimit ? 1000 / fpslimit : null;
13
+ });
14
+ let previousFrameTimestamp = 0;
15
+ let rafId = null;
16
+ function loop(timestamp) {
17
+ if (!is_active || !window)
18
+ return;
19
+ if (!previousFrameTimestamp)
20
+ previousFrameTimestamp = timestamp;
21
+ const delta = timestamp - previousFrameTimestamp;
22
+ if (interval_limit && delta < interval_limit) {
23
+ rafId = window.requestAnimationFrame(loop);
24
+ return;
25
+ }
26
+ previousFrameTimestamp = timestamp;
27
+ fn({ delta, timestamp });
28
+ if (once) {
29
+ is_active = false;
30
+ rafId = null;
31
+ return;
32
+ }
33
+ rafId = window.requestAnimationFrame(loop);
34
+ }
35
+ function resume() {
36
+ if (!is_active && window) {
37
+ is_active = true;
38
+ previousFrameTimestamp = 0;
39
+ rafId = window.requestAnimationFrame(loop);
40
+ }
41
+ }
42
+ function pause() {
43
+ is_active = false;
44
+ if (rafId != null && window) {
45
+ window.cancelAnimationFrame(rafId);
46
+ rafId = null;
47
+ }
48
+ }
49
+ if (immediate)
50
+ resume();
51
+ return {
52
+ is_active,
53
+ pause,
54
+ resume,
55
+ };
56
+ }
@@ -0,0 +1,8 @@
1
+ import { type MaybeGetter } from 'runed';
2
+ /**
3
+ * Inject reactive style element in head.
4
+ * @param css string
5
+ */
6
+ export declare function useStyle(css: MaybeGetter<string>): {
7
+ id: string;
8
+ };
@@ -0,0 +1,21 @@
1
+ import { extract } from 'runed';
2
+ let uisv_usestyle_id = 0;
3
+ /**
4
+ * Inject reactive style element in head.
5
+ * @param css string
6
+ */
7
+ export function useStyle(css) {
8
+ const id = `uisv_styletag_${++uisv_usestyle_id}`;
9
+ let el = $state();
10
+ $effect(() => {
11
+ if (!el) {
12
+ el = (document.getElementById(id) || document.createElement('style'));
13
+ if (!el.isConnected) {
14
+ el.id = id;
15
+ document.head.appendChild(el);
16
+ }
17
+ }
18
+ el.textContent = extract(css);
19
+ });
20
+ return { id };
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uisv",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "ui library for the rest of us",
5
5
  "license": "MIT",
6
6
  "repository": "ui-sv/uisv",
@@ -33,7 +33,6 @@
33
33
  ".": {
34
34
  "types": "./dist/index.d.ts",
35
35
  "svelte": "./dist/index.js",
36
- "style": "./dist/runtime/index.css",
37
36
  "default": "./dist/index.js"
38
37
  },
39
38
  "./vite": {
@@ -50,6 +49,11 @@
50
49
  "types": "./dist/mode.d.ts",
51
50
  "svelte": "./dist/mode.js",
52
51
  "default": "./dist/mode.js"
52
+ },
53
+ "./contexts": {
54
+ "types": "./dist/contexts.d.ts",
55
+ "svelte": "./dist/contexts.js",
56
+ "default": "./dist/contexts.js"
53
57
  }
54
58
  },
55
59
  "peerDependencies": {
@@ -84,7 +88,6 @@
84
88
  "dependencies": {
85
89
  "@internationalized/date": "^3.12.1",
86
90
  "@unocss/preset-web-fonts": "^66.6.8",
87
- "@unocss/svelte-scoped": "^66.6.8",
88
91
  "bits-ui": "^2.18.1",
89
92
  "colortranslator": "^6.1.1",
90
93
  "defu": "^6.1.7",
@@ -96,6 +99,7 @@
96
99
  "scule": "^1.3.0",
97
100
  "svelte-sonner": "^1.1.1",
98
101
  "svelte-toolbelt": "^0.10.6",
102
+ "tailwind-merge": "^3.6.0",
99
103
  "tailwind-variants": "^3.2.2",
100
104
  "theme-colors": "^0.1.0",
101
105
  "unocss": "^66.6.8"
@@ -103,5 +107,8 @@
103
107
  "keywords": [
104
108
  "svelte",
105
109
  "ui"
106
- ]
110
+ ],
111
+ "patchedDependencies": {
112
+ "svelte-sonner@1.1.1": "patches/svelte-sonner@1.1.1.patch"
113
+ }
107
114
  }
@@ -1,31 +0,0 @@
1
- import type { Component, Snippet } from 'svelte';
2
- import { type MaybeGetter, type ElementSizeOptions } from 'runed';
3
- /**
4
- * Checks if a value is a Svelte component
5
- * @param v - The value to check
6
- * @returns true if the value is a component, false otherwise
7
- */
8
- export declare function isComponent(v: unknown): v is Component;
9
- /**
10
- * Checks if a value is a Svelte snippet
11
- * @param v - The value to check
12
- * @returns true if the value is a snippet, false otherwise
13
- */
14
- export declare function isSnippet<T>(v: unknown): v is Snippet<[T]>;
15
- /**
16
- * Returns a reactive value holding the dom rect of `node`s.
17
- *
18
- * Accepts an `options` object with the following properties:
19
- * - `initialSize`: The initial size of the element. Defaults to `{ width: 0, height: 0 }`.
20
- * - `box`: The box model to use. Can be either `"content-box"` or `"border-box"`. Defaults to `"border-box"`.
21
- *
22
- * @returns an array of dom rects.
23
- */
24
- export declare function useElementRects(nodes: MaybeGetter<HTMLElement[]>, options?: ElementSizeOptions): DOMRect[];
25
- /**
26
- * Inject reactive style element in head.
27
- * @param css string
28
- */
29
- export declare function useStyle(css: MaybeGetter<string>): {
30
- id: string;
31
- };