uisv 0.0.21 → 0.0.22

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 (74) hide show
  1. package/dist/components/accordion.svelte +20 -21
  2. package/dist/components/accordion.svelte.d.ts +10 -10
  3. package/dist/components/alert.svelte +22 -28
  4. package/dist/components/alert.svelte.d.ts +5 -5
  5. package/dist/components/app.svelte +57 -0
  6. package/dist/components/app.svelte.d.ts +15 -0
  7. package/dist/components/badge.svelte +18 -23
  8. package/dist/components/badge.svelte.d.ts +3 -3
  9. package/dist/components/banner.svelte +18 -24
  10. package/dist/components/banner.svelte.d.ts +5 -5
  11. package/dist/components/breadcrumb.svelte +6 -6
  12. package/dist/components/breadcrumb.svelte.d.ts +4 -25
  13. package/dist/components/button.svelte +36 -44
  14. package/dist/components/button.svelte.d.ts +12 -11
  15. package/dist/components/calendar.svelte +6 -7
  16. package/dist/components/calendar.svelte.d.ts +3 -3
  17. package/dist/components/card.svelte +11 -12
  18. package/dist/components/card.svelte.d.ts +5 -5
  19. package/dist/components/checkbox-group.svelte +12 -13
  20. package/dist/components/checkbox-group.svelte.d.ts +4 -4
  21. package/dist/components/checkbox.svelte +39 -38
  22. package/dist/components/checkbox.svelte.d.ts +6 -6
  23. package/dist/components/chip.svelte +7 -8
  24. package/dist/components/chip.svelte.d.ts +3 -3
  25. package/dist/components/collapsible.svelte +4 -5
  26. package/dist/components/collapsible.svelte.d.ts +4 -4
  27. package/dist/components/color-picker.svelte +1 -1
  28. package/dist/components/h1.svelte +8 -7
  29. package/dist/components/h2.svelte +12 -11
  30. package/dist/components/h3.svelte +9 -8
  31. package/dist/components/h4.svelte +12 -11
  32. package/dist/components/h5.svelte +12 -11
  33. package/dist/components/h6.svelte +12 -11
  34. package/dist/components/index.d.ts +6 -0
  35. package/dist/components/index.js +6 -0
  36. package/dist/components/input-number.svelte +5 -7
  37. package/dist/components/input-number.svelte.d.ts +5 -5
  38. package/dist/components/input-time.svelte +11 -12
  39. package/dist/components/input-time.svelte.d.ts +6 -6
  40. package/dist/components/input.svelte +11 -12
  41. package/dist/components/input.svelte.d.ts +6 -6
  42. package/dist/components/kbd.svelte +6 -7
  43. package/dist/components/kbd.svelte.d.ts +2 -2
  44. package/dist/components/modal.svelte +189 -0
  45. package/dist/components/modal.svelte.d.ts +33 -0
  46. package/dist/components/p.svelte +3 -1
  47. package/dist/components/pin-input.svelte +10 -11
  48. package/dist/components/pin-input.svelte.d.ts +3 -3
  49. package/dist/components/placeholder.svelte +4 -4
  50. package/dist/components/popover.svelte +33 -61
  51. package/dist/components/popover.svelte.d.ts +8 -30
  52. package/dist/components/progress.svelte +22 -21
  53. package/dist/components/progress.svelte.d.ts +5 -5
  54. package/dist/components/select.svelte +48 -53
  55. package/dist/components/select.svelte.d.ts +22 -29
  56. package/dist/components/seperator.svelte +6 -7
  57. package/dist/components/seperator.svelte.d.ts +6 -6
  58. package/dist/components/slider.svelte +13 -14
  59. package/dist/components/slider.svelte.d.ts +4 -4
  60. package/dist/components/switch.svelte +17 -22
  61. package/dist/components/switch.svelte.d.ts +6 -6
  62. package/dist/components/tabs.svelte +19 -20
  63. package/dist/components/tabs.svelte.d.ts +7 -7
  64. package/dist/components/tooltip.svelte +94 -0
  65. package/dist/components/tooltip.svelte.d.ts +24 -0
  66. package/dist/contexts.d.ts +47 -0
  67. package/dist/contexts.js +46 -0
  68. package/dist/index.d.ts +1 -0
  69. package/dist/index.js +1 -0
  70. package/dist/mode.d.ts +89 -0
  71. package/dist/mode.js +1 -0
  72. package/dist/utilities.svelte.d.ts +1 -1
  73. package/dist/vite.js +30 -35
  74. package/package.json +31 -26
@@ -2,10 +2,9 @@
2
2
  import { type PropColor, type PropVariant, isComponent, isSnippet } from '../index.js';
3
3
  import type { Component, Snippet } from 'svelte';
4
4
  import type { SvelteHTMLElements } from 'svelte/elements';
5
- import type { ClassNameValue } from 'tailwind-merge';
6
5
  import { maska } from 'maska/svelte';
7
6
  import { type MaskInputOptions } from 'maska';
8
- import { tv } from 'tailwind-variants';
7
+ import { tv, type ClassValue } from 'tailwind-variants';
9
8
 
10
9
  export type InputProps = Omit<SvelteHTMLElements['input'], 'size'> & {
11
10
  name?: string;
@@ -47,11 +46,11 @@
47
46
  loadingicon?: string | Component;
48
47
  mask?: string | MaskInputOptions;
49
48
  ui?: {
50
- root?: ClassNameValue;
51
- base?: ClassNameValue;
52
- leading?: ClassNameValue;
53
- icon?: ClassNameValue;
54
- trailing?: ClassNameValue;
49
+ root?: ClassValue;
50
+ base?: ClassValue;
51
+ leading?: ClassValue;
52
+ icon?: ClassValue;
53
+ trailing?: ClassValue;
55
54
  };
56
55
  };
57
56
  </script>
@@ -84,9 +83,9 @@
84
83
  tv({
85
84
  slots: {
86
85
  root: 'inline-flex items-center rounded transition-all ring ring-inset ring-transparent',
87
- base: 'appearance-none outline-none placeholder:text-muted',
88
- leading: 'text-muted flex items-center',
89
- trailing: 'text-muted flex items-center',
86
+ base: 'appearance-none outline-none placeholder:text-label-muted',
87
+ leading: 'text-label-muted flex items-center',
88
+ trailing: 'text-label-muted flex items-center',
90
89
  icon: '',
91
90
  },
92
91
  variants: {
@@ -162,7 +161,7 @@
162
161
  true: '',
163
162
  },
164
163
  type: {
165
- file: 'file:me-1.5 file:font-medium file:text-muted file:outline-none',
164
+ file: 'file:me-1.5 file:font-medium file:text-label-muted file:outline-none',
166
165
  },
167
166
  },
168
167
  compoundVariants: [
@@ -177,7 +176,7 @@
177
176
  color: 'surface',
178
177
  variant: ['outline', 'subtle'],
179
178
  class: {
180
- root: 'focus-within:ring-surface-800 focus-within:ring-2',
179
+ root: 'focus-within:ring-surface-inverted focus-within:ring-2',
181
180
  },
182
181
  },
183
182
  {
@@ -1,8 +1,8 @@
1
1
  import { type PropColor, type PropVariant } from '../index.js';
2
2
  import type { Component, Snippet } from 'svelte';
3
3
  import type { SvelteHTMLElements } from 'svelte/elements';
4
- import type { ClassNameValue } from 'tailwind-merge';
5
4
  import { type MaskInputOptions } from 'maska';
5
+ import { type ClassValue } from 'tailwind-variants';
6
6
  export type InputProps = Omit<SvelteHTMLElements['input'], 'size'> & {
7
7
  name?: string;
8
8
  /**
@@ -43,11 +43,11 @@ export type InputProps = Omit<SvelteHTMLElements['input'], 'size'> & {
43
43
  loadingicon?: string | Component;
44
44
  mask?: string | MaskInputOptions;
45
45
  ui?: {
46
- root?: ClassNameValue;
47
- base?: ClassNameValue;
48
- leading?: ClassNameValue;
49
- icon?: ClassNameValue;
50
- trailing?: ClassNameValue;
46
+ root?: ClassValue;
47
+ base?: ClassValue;
48
+ leading?: ClassValue;
49
+ icon?: ClassValue;
50
+ trailing?: ClassValue;
51
51
  };
52
52
  };
53
53
  declare const Input: Component<InputProps, {}, "value">;
@@ -1,8 +1,7 @@
1
1
  <script module lang="ts">
2
2
  import type { PropColor, PropVariant } from '../index.js';
3
3
  import type { Snippet } from 'svelte';
4
- import type { ClassNameValue } from 'tailwind-merge';
5
- import { tv } from 'tailwind-variants';
4
+ import { tv, type ClassValue } from 'tailwind-variants';
6
5
 
7
6
  export type KbdProps = {
8
7
  children?: Snippet;
@@ -10,7 +9,7 @@
10
9
  color?: PropColor;
11
10
  variant?: Exclude<PropVariant, 'none' | 'ghost'>;
12
11
  size?: 'sm' | 'md' | 'lg';
13
- class?: ClassNameValue;
12
+ class?: ClassValue;
14
13
  };
15
14
 
16
15
  export const KBD_KEYS = {
@@ -110,7 +109,7 @@
110
109
  {
111
110
  color: 'surface',
112
111
  variant: 'outline',
113
- class: 'border-surface-600',
112
+ class: 'border-label-toned',
114
113
  },
115
114
  {
116
115
  color: 'info',
@@ -142,7 +141,7 @@
142
141
  {
143
142
  color: 'surface',
144
143
  variant: 'solid',
145
- class: 'bg-surface-600 border-surface-700',
144
+ class: 'bg-label-toned border-label-highlighted',
146
145
  },
147
146
  {
148
147
  color: 'info',
@@ -174,7 +173,7 @@
174
173
  {
175
174
  color: 'surface',
176
175
  variant: 'soft',
177
- class: 'bg-surface-100 text-surface-700',
176
+ class: 'bg-surface-elevated text-label-toned',
178
177
  },
179
178
  {
180
179
  color: 'info',
@@ -206,7 +205,7 @@
206
205
  {
207
206
  color: 'surface',
208
207
  variant: 'subtle',
209
- class: 'bg-surface-100 border-surface-200 text-surface-700',
208
+ class: 'bg-surface-muted border-surface-accented text-label-toned',
210
209
  },
211
210
  {
212
211
  color: 'info',
@@ -1,13 +1,13 @@
1
1
  import type { PropColor, PropVariant } from '../index.js';
2
2
  import type { Snippet } from 'svelte';
3
- import type { ClassNameValue } from 'tailwind-merge';
3
+ import { type ClassValue } from 'tailwind-variants';
4
4
  export type KbdProps = {
5
5
  children?: Snippet;
6
6
  value?: string;
7
7
  color?: PropColor;
8
8
  variant?: Exclude<PropVariant, 'none' | 'ghost'>;
9
9
  size?: 'sm' | 'md' | 'lg';
10
- class?: ClassNameValue;
10
+ class?: ClassValue;
11
11
  };
12
12
  export declare const KBD_KEYS: {
13
13
  meta: string;
@@ -0,0 +1,189 @@
1
+ <script module lang="ts">
2
+ import { Dialog, type DialogContentProps, type PortalProps } from 'bits-ui';
3
+ import { tv, type ClassValue } from 'tailwind-variants';
4
+ import { Button, isComponent, isSnippet, type ButtonProps } from '../index.js';
5
+ import type { Component, Snippet } from 'svelte';
6
+ import defu from 'defu';
7
+ import { fade, scale } from 'svelte/transition';
8
+ import { cubicIn } from 'svelte/easing';
9
+ import { app_icons } from '../contexts.js';
10
+
11
+ export type ModalProps = {
12
+ open?: boolean;
13
+ trigger?: ButtonProps;
14
+ ui?: {
15
+ overlay?: ClassValue;
16
+ content?: ClassValue;
17
+ header?: ClassValue;
18
+ body?: ClassValue;
19
+ footer?: ClassValue;
20
+ title?: ClassValue;
21
+ description?: ClassValue;
22
+ };
23
+ children?: Snippet;
24
+ title?: string | Snippet<[Record<string, unknown>]> | Component;
25
+ description?: string | Snippet<[Record<string, unknown>]> | Component;
26
+ footer?: Snippet<[{ close: () => void }]>;
27
+ close?: boolean | ButtonProps;
28
+ transition?: boolean;
29
+ overlay?: boolean;
30
+ fullscreen?: boolean;
31
+ dismissable?: boolean;
32
+ portal?: PortalProps;
33
+ content?: DialogContentProps;
34
+ };
35
+ </script>
36
+
37
+ <script lang="ts">
38
+ let {
39
+ open = $bindable(false),
40
+ trigger,
41
+ ui = {},
42
+ children,
43
+ title,
44
+ description,
45
+ footer,
46
+ close = true,
47
+ transition = true,
48
+ overlay = true,
49
+ fullscreen,
50
+ dismissable = true,
51
+ portal,
52
+ content,
53
+ }: ModalProps = $props();
54
+
55
+ let content_element = $state<HTMLDivElement | null>(null);
56
+
57
+ const variants = $derived(
58
+ tv({
59
+ slots: {
60
+ content:
61
+ 'z-[calc(var(--bits-dialog-depth)*10+11)] fixed bg-surface-base border border-surface-accented rounded-md divide-y divide-surface-accented flex flex-col overflow-hidden pointer-events-auto',
62
+ overlay: 'fixed inset-0 bg-surface-elevated/75 z-[calc(var(--bits-dialog-depth)*10+10)] ',
63
+ header: 'flex items-center gap-1.5 p-4 sm:px-6',
64
+ body: 'flex-1 p-4 sm:p-6 overflow-y-auto',
65
+ footer: 'flex items-center gap-1.5 p-4 sm:px-6',
66
+ title: 'text-label-highlighted font-semibold select-all',
67
+ description: 'mt-1 text-label-muted text-sm',
68
+ },
69
+ variants: {
70
+ fullscreen: {
71
+ true: {
72
+ content: 'inset-0',
73
+ },
74
+ false: {
75
+ content:
76
+ '-translate-1/2 top-1/2 left-1/2 max-h-[calc(100dvh-2rem)] sm:max-h-[calc(100dvh-4rem)] max-w-lg w-full',
77
+ },
78
+ },
79
+ },
80
+ })({ fullscreen }),
81
+ );
82
+ </script>
83
+
84
+ <Dialog.Root bind:open>
85
+ {#if trigger}
86
+ <Dialog.Trigger>
87
+ {#snippet child({ props })}
88
+ <Button {...props} {...trigger} />
89
+ {/snippet}
90
+ </Dialog.Trigger>
91
+ {/if}
92
+
93
+ <Dialog.Portal {...portal}>
94
+ <Dialog.Overlay forceMount>
95
+ {#snippet child({ props })}
96
+ {#if overlay && open}
97
+ <div
98
+ {...props}
99
+ class={variants.overlay({
100
+ class: [ui.overlay],
101
+ })}
102
+ transition:fade={{ duration: transition ? 200 : 0 }}
103
+ ></div>
104
+ {/if}
105
+ {/snippet}
106
+ </Dialog.Overlay>
107
+
108
+ <Dialog.Content
109
+ {...content}
110
+ forceMount
111
+ escapeKeydownBehavior={dismissable ? 'close' : 'ignore'}
112
+ interactOutsideBehavior={dismissable ? 'close' : 'ignore'}
113
+ onInteractOutside={(e) => {
114
+ if (content_element?.contains(e.target as Node)) e.preventDefault();
115
+ if (content?.onInteractOutside) content.onInteractOutside(e);
116
+ }}
117
+ >
118
+ {#snippet child({ props })}
119
+ {#if open}
120
+ <div
121
+ bind:this={content_element}
122
+ {...props}
123
+ class={variants.content({ class: ui.content })}
124
+ transition:scale={{
125
+ duration: transition ? 200 : 0,
126
+ easing: cubicIn,
127
+ start: 0.95,
128
+ opacity: 0,
129
+ }}
130
+ >
131
+ {#if title || description}
132
+ <div class={variants.header({ class: ui.header })}>
133
+ <div class="flex-1">
134
+ {#if typeof title === 'string'}
135
+ <h1 class={variants.title({ class: ui.title })}>{title}</h1>
136
+ {:else if isSnippet(title)}
137
+ {@render title({ class: variants.title({ class: ui.title }) })}
138
+ {:else if isComponent(title)}
139
+ {@const Comp = title}
140
+
141
+ <Comp class={variants.title({ class: ui.title })} />
142
+ {/if}
143
+
144
+ {#if typeof description === 'string'}
145
+ <h1 class={variants.description({ class: ui.description })}>{description}</h1>
146
+ {:else if isSnippet(description)}
147
+ {@render description({
148
+ class: variants.description({ class: ui.description }),
149
+ })}
150
+ {:else if isComponent(title)}
151
+ {@const Comp = description}
152
+
153
+ <Comp class={variants.description({ class: ui.description })} />
154
+ {/if}
155
+ </div>
156
+
157
+ {#if close}
158
+ <Button
159
+ {...props}
160
+ {...defu(typeof close === 'boolean' ? {} : close, <ButtonProps>{
161
+ variant: 'ghost',
162
+ color: 'surface',
163
+ icon: app_icons.get().close,
164
+ onclick() {
165
+ open = false;
166
+ },
167
+ })}
168
+ />
169
+ {/if}
170
+ </div>
171
+ {/if}
172
+
173
+ <div class={variants.body({ class: ui.body })}>
174
+ {@render children?.()}
175
+ </div>
176
+
177
+ {#if footer}
178
+ <div class={variants.footer({ class: ui.footer })}>
179
+ {@render footer({
180
+ close: () => (open = false),
181
+ })}
182
+ </div>
183
+ {/if}
184
+ </div>
185
+ {/if}
186
+ {/snippet}
187
+ </Dialog.Content>
188
+ </Dialog.Portal>
189
+ </Dialog.Root>
@@ -0,0 +1,33 @@
1
+ import { type DialogContentProps, type PortalProps } from 'bits-ui';
2
+ import { type ClassValue } from 'tailwind-variants';
3
+ import { type ButtonProps } from '../index.js';
4
+ import type { Component, Snippet } from 'svelte';
5
+ export type ModalProps = {
6
+ open?: boolean;
7
+ trigger?: ButtonProps;
8
+ ui?: {
9
+ overlay?: ClassValue;
10
+ content?: ClassValue;
11
+ header?: ClassValue;
12
+ body?: ClassValue;
13
+ footer?: ClassValue;
14
+ title?: ClassValue;
15
+ description?: ClassValue;
16
+ };
17
+ children?: Snippet;
18
+ title?: string | Snippet<[Record<string, unknown>]> | Component;
19
+ description?: string | Snippet<[Record<string, unknown>]> | Component;
20
+ footer?: Snippet<[{
21
+ close: () => void;
22
+ }]>;
23
+ close?: boolean | ButtonProps;
24
+ transition?: boolean;
25
+ overlay?: boolean;
26
+ fullscreen?: boolean;
27
+ dismissable?: boolean;
28
+ portal?: PortalProps;
29
+ content?: DialogContentProps;
30
+ };
31
+ declare const Modal: Component<ModalProps, {}, "open">;
32
+ type Modal = ReturnType<typeof Modal>;
33
+ export default Modal;
@@ -2,8 +2,10 @@
2
2
  import type { SvelteHTMLElements } from 'svelte/elements';
3
3
  import { cn } from 'tailwind-variants';
4
4
  const { children, class: classes }: SvelteHTMLElements['p'] = $props();
5
+
6
+ const classname = $derived(cn('my-5 leading-7 text-pretty', classes));
5
7
  </script>
6
8
 
7
- <p class={cn('my-5 leading-7 text-pretty', classes)}>
9
+ <p class={classname}>
8
10
  {@render children?.()}
9
11
  </p>
@@ -1,8 +1,7 @@
1
1
  <script module lang="ts">
2
2
  import type { PropColor, PropVariant } from '../index.js';
3
3
  import { onMount } from 'svelte';
4
- import type { ClassNameValue } from 'tailwind-merge';
5
- import { tv } from 'tailwind-variants';
4
+ import { tv, type ClassValue } from 'tailwind-variants';
6
5
 
7
6
  export type PinInputProps = {
8
7
  value?: number[] | string[];
@@ -19,7 +18,7 @@
19
18
  placeholder?: string;
20
19
  required?: boolean;
21
20
  type?: 'text' | 'number';
22
- ui?: { root?: ClassNameValue; cell?: ClassNameValue };
21
+ ui?: { root?: ClassValue; cell?: ClassValue };
23
22
  };
24
23
  </script>
25
24
 
@@ -59,7 +58,7 @@
59
58
  const internal_id = $props.id();
60
59
  let input_els = $state<HTMLInputElement[]>([]);
61
60
 
62
- const classes = $derived(
61
+ const variants = $derived(
63
62
  tv({
64
63
  slots: { root: 'flex gap-2', cell: 'rounded text-center outline-none transition relative' },
65
64
  variants: {
@@ -80,13 +79,13 @@
80
79
  },
81
80
  variant: {
82
81
  outline: {
83
- cell: 'border border-surface-300 focus:border-2',
82
+ cell: 'border border-surface-accented focus:border-2',
84
83
  },
85
84
  soft: {
86
- cell: 'bg-surface-50 hover:bg-surface-100 focus:bg-surface-100',
85
+ cell: 'bg-surface-muted hover:bg-surface-elevated focus:bg-surface-elevated',
87
86
  },
88
- subtle: { cell: 'border border-surface-300 bg-surface-100 focus:border-2' },
89
- ghost: { cell: 'hover:bg-surface-100 focus:bg-surface-100' },
87
+ subtle: { cell: 'border border-surface-accented bg-surface-elevated focus:border-2' },
88
+ ghost: { cell: 'hover:bg-surface-elevated focus:bg-surface-elevated' },
90
89
  none: { cell: '' },
91
90
  },
92
91
  },
@@ -99,7 +98,7 @@
99
98
  {
100
99
  variant: ['outline', 'subtle'],
101
100
  color: 'surface',
102
- class: { cell: 'focus:border-surface-900' },
101
+ class: { cell: 'focus:border-surface-inverted' },
103
102
  },
104
103
  {
105
104
  variant: ['outline', 'subtle'],
@@ -132,7 +131,7 @@
132
131
  });
133
132
  </script>
134
133
 
135
- <div id={id || internal_id} class={classes.root({ class: ui.root })}>
134
+ <div id={id || internal_id} class={variants.root({ class: ui.root })}>
136
135
  {#each { length }, i (i)}
137
136
  <input
138
137
  bind:this={input_els[i]}
@@ -148,7 +147,7 @@
148
147
  }
149
148
  }
150
149
  }
151
- class={classes.cell({ class: ui.cell })}
150
+ class={variants.cell({ class: ui.cell })}
152
151
  onkeydown={(e) => {
153
152
  if (KEYS_TO_IGNORE.includes(e.key)) e.preventDefault();
154
153
  if (type === 'number' && isNaN(parseInt(e.key))) e.preventDefault();
@@ -1,5 +1,5 @@
1
1
  import type { PropColor, PropVariant } from '../index.js';
2
- import type { ClassNameValue } from 'tailwind-merge';
2
+ import { type ClassValue } from 'tailwind-variants';
3
3
  export type PinInputProps = {
4
4
  value?: number[] | string[];
5
5
  color?: PropColor;
@@ -16,8 +16,8 @@ export type PinInputProps = {
16
16
  required?: boolean;
17
17
  type?: 'text' | 'number';
18
18
  ui?: {
19
- root?: ClassNameValue;
20
- cell?: ClassNameValue;
19
+ root?: ClassValue;
20
+ cell?: ClassValue;
21
21
  };
22
22
  };
23
23
  declare const PinInput: import("svelte").Component<PinInputProps, {}, "value">;
@@ -1,15 +1,15 @@
1
1
  <script lang="ts">
2
2
  import type { SvelteHTMLElements } from 'svelte/elements';
3
- import { cx } from 'tailwind-variants';
3
+ import { cn } from 'tailwind-variants';
4
4
 
5
- let { class: klass, ...rest }: SvelteHTMLElements['svg'] = $props();
5
+ let { class: classes, ...rest }: SvelteHTMLElements['svg'] = $props();
6
6
  </script>
7
7
 
8
8
  <svg
9
9
  {...rest}
10
- class={cx(
10
+ class={cn(
11
11
  'inset-0 w-full stroke-surface-elevated border border-dashed border-surface-accented rounded-md',
12
- klass,
12
+ classes,
13
13
  )}
14
14
  fill="none"
15
15
  >
@@ -1,10 +1,8 @@
1
1
  <script module lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { ClassNameValue } from 'tailwind-merge';
4
- import { Popover, type PopoverContentProps } from 'bits-ui';
5
- import { cn, tv } from 'tailwind-variants';
6
- import { type ButtonProps,Button } from '../index.js';
7
- import type { PropColor } from '../index.js';
3
+ import { Popover, type PopoverArrowProps, type PopoverContentProps } from 'bits-ui';
4
+ import { tv, type ClassValue } from 'tailwind-variants';
5
+ import { type ButtonBaseProps, Button } from '../index.js';
8
6
 
9
7
  export type PopoverContentSnippet = {
10
8
  props: Record<string, unknown>;
@@ -12,7 +10,7 @@
12
10
  wrapperProps: Record<string, unknown>;
13
11
  };
14
12
 
15
- export type PopoverProps = {
13
+ export type PopoverProps = ButtonBaseProps & {
16
14
  /**
17
15
  * The display mode of the popover.
18
16
  */
@@ -21,20 +19,15 @@
21
19
  * The props for the content of popover.
22
20
  */
23
21
  contentprops?: PopoverContentProps;
24
- content?: Snippet<[PopoverContentSnippet]>;
25
22
  children?: Snippet<[]>;
26
- /**
27
- * Snippet if you want to implement your own trigger button
28
- */
29
- trigger?: Snippet<[Record<string, unknown>]>;
30
23
  /**
31
24
  * Display an arrow alongside the popover.
32
25
  */
33
- arrow?: boolean;
26
+ arrow?: boolean | PopoverArrowProps;
34
27
  /**
35
28
  * Render the popover in a portal.
36
29
  */
37
- portal?: string | false | true | HTMLElement;
30
+ // portal?: string | false | true | HTMLElement;
38
31
  /**
39
32
  * The reference (or anchor) element that is being referred to for positioning. If not provided will use the current component as anchor.
40
33
  */
@@ -49,10 +42,6 @@
49
42
  * When `false`, the popover will not close when clicking outside or pressing escape.
50
43
  */
51
44
  dismissible?: boolean;
52
- /**
53
- * The open state of the popover when it is initially rendered. Use when you do not need to control its open state.
54
- */
55
- defaultopen?: boolean;
56
45
  /**
57
46
  * The controlled open state of the popover.
58
47
  */
@@ -73,21 +62,10 @@
73
62
  *
74
63
  */
75
64
  ui?: {
76
- content?: ClassNameValue;
77
- arrow?: ClassNameValue;
65
+ content?: ClassValue;
66
+ arrow?: ClassValue;
67
+ trigger?: ClassValue;
78
68
  };
79
- /**
80
- * @default `outline`
81
- */
82
- variant?: ButtonProps['variant'];
83
- /**
84
- * @default primary
85
- */
86
- color?: PropColor;
87
- /**
88
- *
89
- */
90
- class?: ClassNameValue;
91
69
  };
92
70
  </script>
93
71
 
@@ -95,57 +73,51 @@
95
73
  let {
96
74
  mode = 'click',
97
75
  contentprops = { side: 'bottom', sideOffset: 8, collisionPadding: 8 },
98
- content,
99
76
  children,
100
- trigger,
101
- arrow = true,
102
- portal = true,
103
- reference,
77
+ arrow = false,
104
78
  dismissible = true,
105
- defaultopen,
106
79
  open = $bindable(false),
107
- modal = false,
108
80
  opendelay = 0,
109
81
  closedelay = 0,
110
82
  ui = {},
111
- variant = 'outline',
112
- color = 'primary',
113
- class: klass,
83
+ ...rest
114
84
  }: PopoverProps = $props();
115
85
 
116
- const classes = $derived(
86
+ const variants = $derived(
117
87
  tv({
118
88
  slots: {
119
- content:
120
- 'bg-default shadow-lg rounded-md ring ring-default data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-popover-content-transform-origin) focus:outline-none pointer-events-auto',
121
- arrow: 'fill-default',
89
+ content: [
90
+ 'bg-surface-base z-30 w-full shadow-lg rounded-md border border-surface-accented p-4',
91
+ 'origin-(--bits-popover-content-transform-origin)',
92
+ '',
93
+ ],
94
+ arrow: 'text-surface-accented',
122
95
  },
123
96
  })({}),
124
97
  );
125
98
  </script>
126
99
 
127
100
  <Popover.Root bind:open>
128
- <Popover.Trigger>
101
+ <Popover.Trigger openOnHover={mode === 'hover'} openDelay={opendelay} closeDelay={closedelay}>
129
102
  {#snippet child({ props })}
130
- {#if trigger}
131
- {@render trigger(props)}
132
- {:else}
133
- <Button {...props} {variant} {color} ui={{ base: klass }} />
134
- {/if}
103
+ <Button {...rest} {...props} ui={{ base: ui.trigger }} />
135
104
  {/snippet}
136
105
  </Popover.Trigger>
106
+
137
107
  <Popover.Portal>
138
- <Popover.Overlay />
139
- <Popover.Content {...contentprops} class={classes.content({ class: ui.content })}>
140
- {#snippet child(props)}
141
- {#if content}
142
- {@render content(props)}
143
- {:else}
144
- {@render children?.()}
145
- {/if}
146
- {/snippet}
108
+ <Popover.Content
109
+ {...contentprops}
110
+ class={variants.content({ class: ui.content })}
111
+ interactOutsideBehavior={dismissible ? 'close' : 'ignore'}
112
+ >
113
+ {@render children?.()}
147
114
 
148
- <Popover.Arrow />
115
+ {#if arrow}
116
+ <Popover.Arrow
117
+ {...typeof arrow === 'object' ? arrow : {}}
118
+ class={variants.arrow({ class: ui.arrow })}
119
+ />
120
+ {/if}
149
121
  </Popover.Content>
150
122
  </Popover.Portal>
151
123
  </Popover.Root>