@byyuurin/ui 0.0.8 → 0.0.9

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 (111) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +4 -3
  3. package/dist/module.mjs.map +1 -1
  4. package/dist/runtime/app/injections.d.ts +2 -7
  5. package/dist/runtime/app/injections.js +0 -5
  6. package/dist/runtime/components/Accordion.vue +10 -8
  7. package/dist/runtime/components/Alert.vue +5 -8
  8. package/dist/runtime/components/App.vue +14 -19
  9. package/dist/runtime/components/Avatar.vue +5 -8
  10. package/dist/runtime/components/AvatarGroup.vue +2 -5
  11. package/dist/runtime/components/Badge.vue +2 -5
  12. package/dist/runtime/components/Breadcrumb.vue +11 -8
  13. package/dist/runtime/components/Button.vue +2 -3
  14. package/dist/runtime/components/ButtonGroup.vue +2 -5
  15. package/dist/runtime/components/Calendar.vue +176 -0
  16. package/dist/runtime/components/Card.vue +2 -5
  17. package/dist/runtime/components/Carousel.vue +6 -13
  18. package/dist/runtime/components/Checkbox.vue +2 -5
  19. package/dist/runtime/components/Chip.vue +6 -9
  20. package/dist/runtime/components/Collapsible.vue +2 -5
  21. package/dist/runtime/components/Drawer.vue +80 -37
  22. package/dist/runtime/components/Input.vue +10 -13
  23. package/dist/runtime/components/InputNumber.vue +2 -5
  24. package/dist/runtime/components/Kbd.vue +2 -6
  25. package/dist/runtime/components/Link.vue +8 -5
  26. package/dist/runtime/components/Modal.vue +20 -11
  27. package/dist/runtime/components/OverlayProvider.vue +29 -0
  28. package/dist/runtime/components/Pagination.vue +34 -27
  29. package/dist/runtime/components/PinInput.vue +2 -5
  30. package/dist/runtime/components/Popover.vue +2 -5
  31. package/dist/runtime/components/Progress.vue +2 -5
  32. package/dist/runtime/components/RadioGroup.vue +6 -7
  33. package/dist/runtime/components/ScrollArea.vue +2 -6
  34. package/dist/runtime/components/Select.vue +10 -13
  35. package/dist/runtime/components/Separator.vue +2 -6
  36. package/dist/runtime/components/Skeleton.vue +2 -5
  37. package/dist/runtime/components/Slider.vue +2 -5
  38. package/dist/runtime/components/Switch.vue +6 -5
  39. package/dist/runtime/components/Table.vue +2 -5
  40. package/dist/runtime/components/Tabs.vue +2 -5
  41. package/dist/runtime/components/Textarea.vue +2 -5
  42. package/dist/runtime/components/Toast.vue +7 -11
  43. package/dist/runtime/components/{Toaster.vue → ToastProvider.vue} +18 -16
  44. package/dist/runtime/components/Tooltip.vue +2 -5
  45. package/dist/runtime/composables/useKbd.d.ts +1 -1
  46. package/dist/runtime/composables/useOverlay.d.ts +29 -0
  47. package/dist/runtime/composables/useOverlay.js +69 -0
  48. package/dist/runtime/composables/useTheme.d.ts +6 -2
  49. package/dist/runtime/composables/useTheme.js +9 -3
  50. package/dist/runtime/composables/useToast.d.ts +4 -20
  51. package/dist/runtime/composables/useToast.js +6 -5
  52. package/dist/runtime/index.d.ts +3 -2
  53. package/dist/runtime/index.js +3 -2
  54. package/dist/runtime/locale/en.js +6 -0
  55. package/dist/runtime/locale/zh-tw.js +6 -0
  56. package/dist/runtime/theme/accordion.js +3 -3
  57. package/dist/runtime/theme/alert.js +3 -3
  58. package/dist/runtime/theme/avatar.js +2 -2
  59. package/dist/runtime/theme/breadcrumb.js +5 -5
  60. package/dist/runtime/theme/button.js +13 -13
  61. package/dist/runtime/theme/calendar.d.ts +56 -0
  62. package/dist/runtime/theme/calendar.js +69 -0
  63. package/dist/runtime/theme/card.js +6 -6
  64. package/dist/runtime/theme/carousel.js +1 -1
  65. package/dist/runtime/theme/checkbox.js +5 -5
  66. package/dist/runtime/theme/chip.js +3 -3
  67. package/dist/runtime/theme/drawer.d.ts +24 -21
  68. package/dist/runtime/theme/drawer.js +46 -19
  69. package/dist/runtime/theme/index.d.ts +2 -1
  70. package/dist/runtime/theme/index.js +2 -1
  71. package/dist/runtime/theme/input-number.js +1 -1
  72. package/dist/runtime/theme/input.js +14 -14
  73. package/dist/runtime/theme/kbd.d.ts +2 -2
  74. package/dist/runtime/theme/kbd.js +1 -1
  75. package/dist/runtime/theme/link.d.ts +1 -1
  76. package/dist/runtime/theme/link.js +3 -3
  77. package/dist/runtime/theme/modal.js +4 -4
  78. package/dist/runtime/theme/pagination.d.ts +27 -3
  79. package/dist/runtime/theme/pagination.js +6 -2
  80. package/dist/runtime/theme/pinInput.js +13 -13
  81. package/dist/runtime/theme/progress.js +4 -4
  82. package/dist/runtime/theme/radio-group.d.ts +2 -2
  83. package/dist/runtime/theme/radio-group.js +7 -7
  84. package/dist/runtime/theme/select.js +20 -20
  85. package/dist/runtime/theme/separator.js +1 -1
  86. package/dist/runtime/theme/skeleton.d.ts +1 -1
  87. package/dist/runtime/theme/skeleton.js +1 -1
  88. package/dist/runtime/theme/slider.js +1 -1
  89. package/dist/runtime/theme/switch.js +5 -5
  90. package/dist/runtime/theme/table.js +7 -7
  91. package/dist/runtime/theme/tabs.js +10 -10
  92. package/dist/runtime/theme/textarea.js +13 -13
  93. package/dist/runtime/theme/toast.js +5 -5
  94. package/dist/runtime/theme/tooltip.js +1 -1
  95. package/dist/runtime/types/components.d.ts +3 -1
  96. package/dist/runtime/types/locale.d.ts +6 -0
  97. package/dist/runtime/types/utils.d.ts +4 -2
  98. package/dist/shared/ui.1a1f119c.mjs +5 -0
  99. package/dist/shared/ui.1a1f119c.mjs.map +1 -0
  100. package/dist/unocss.mjs +19 -14
  101. package/dist/unocss.mjs.map +1 -1
  102. package/dist/unplugin.mjs +1 -1
  103. package/dist/vite.mjs +1 -1
  104. package/package.json +9 -9
  105. package/dist/runtime/components/ModalProvider.vue +0 -11
  106. package/dist/runtime/composables/useModal.d.ts +0 -10
  107. package/dist/runtime/composables/useModal.js +0 -47
  108. package/dist/shared/ui.ba24b380.mjs +0 -4
  109. package/dist/shared/ui.ba24b380.mjs.map +0 -1
  110. /package/dist/runtime/theme/{toaster.d.ts → toast-provider.d.ts} +0 -0
  111. /package/dist/runtime/theme/{toaster.js → toast-provider.js} +0 -0
@@ -1,23 +1,27 @@
1
1
  <script lang="ts">
2
- import type { DialogContentProps } from 'reka-ui'
3
- import type { DrawerRootEmits, DrawerRootProps } from 'vaul-vue'
2
+ import type { VariantProps } from '@byyuurin/ui-kit'
3
+ import type { DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-ui'
4
4
  import type { drawer } from '../theme'
5
- import type { ComponentAttrs } from '../types'
5
+ import type { ButtonProps, ComponentAttrs } from '../types'
6
6
 
7
- export interface DrawerEmits extends DrawerRootEmits {}
7
+ export interface DrawerEmits extends DialogRootEmits {
8
+ 'after-leave': []
9
+ }
8
10
 
9
11
  export interface DrawerSlots {
10
12
  default?: (props?: {}) => any
11
- handle?: (props?: {}) => any
12
13
  content?: (props?: {}) => any
13
14
  header?: (props?: {}) => any
14
15
  title?: (props?: {}) => any
15
16
  description?: (props?: {}) => any
17
+ close?: (props?: {}) => any
16
18
  body?: (props?: {}) => any
17
19
  footer?: (props?: {}) => any
18
20
  }
19
21
 
20
- export interface DrawerProps extends ComponentAttrs<typeof drawer>, Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | 'fadeFromIndex' | 'fixed' | 'modal' | 'nested' | 'direction' | 'open' | 'scrollLockTimeout' | 'shouldScaleBackground' | 'snapPoints'> {
22
+ type DrawerVariants = VariantProps<typeof drawer>
23
+
24
+ export interface DrawerProps extends ComponentAttrs<typeof drawer>, DialogRootProps {
21
25
  title?: string
22
26
  description?: string
23
27
  /** The content of the drawer. */
@@ -27,20 +31,29 @@ export interface DrawerProps extends ComponentAttrs<typeof drawer>, Pick<DrawerR
27
31
  * @default true
28
32
  */
29
33
  overlay?: boolean
34
+ /** @default true */
35
+ transition?: boolean
36
+ /**
37
+ * The direction of the drawer.
38
+ * @default "bottom"
39
+ */
40
+ direction?: DrawerVariants['direction']
30
41
  /**
31
42
  * Whether to inset the drawer from the edges.
32
43
  */
33
44
  inset?: boolean
34
45
  /**
35
- * Render a handle on the drawer.
46
+ * Render the drawer in a portal.
36
47
  * @default true
37
48
  */
38
- handle?: boolean
49
+ portal?: boolean
39
50
  /**
40
- * Render the drawer in a portal.
51
+ * Display a close button to dismiss the drawer.
41
52
  * @default true
42
53
  */
43
- portal?: boolean
54
+ close?: ButtonProps | boolean
55
+ /** @default app.icons.close */
56
+ closeIcon?: string
44
57
  /**
45
58
  * When `false`, the drawer will not close when clicking outside or pressing escape.
46
59
  * @default true
@@ -51,71 +64,101 @@ export interface DrawerProps extends ComponentAttrs<typeof drawer>, Pick<DrawerR
51
64
 
52
65
  <script setup lang="ts">
53
66
  import { reactivePick } from '@vueuse/core'
54
- import { useForwardPropsEmits } from 'reka-ui'
55
- import { DrawerContent, DrawerDescription, DrawerOverlay, DrawerPortal, DrawerRoot, DrawerTitle, DrawerTrigger } from 'vaul-vue'
67
+ import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, useForwardPropsEmits, VisuallyHidden } from 'reka-ui'
56
68
  import { computed, toRef } from 'vue'
69
+ import { useLocale } from '../composables/useLocale'
57
70
  import { useTheme } from '../composables/useTheme'
71
+ import Button from './Button.vue'
58
72
 
59
73
  const props = withDefaults(defineProps<DrawerProps>(), {
60
74
  direction: 'bottom',
75
+ modal: true,
61
76
  portal: true,
62
77
  overlay: true,
63
- handle: true,
78
+ transition: true,
79
+ dismissible: true,
80
+ close: true,
64
81
  })
65
82
  const emit = defineEmits<DrawerEmits>()
66
83
  const slots = defineSlots<DrawerSlots>()
67
84
 
68
- const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'defaultOpen', 'dismissible', 'fadeFromIndex', 'fixed', 'modal', 'nested', 'direction', 'open', 'scrollLockTimeout', 'shouldScaleBackground', 'snapPoints'), emit)
69
- const contentProps = toRef(() => props.content)
85
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emit)
86
+ const contentProps = toRef(() => ({
87
+ ...props.content,
88
+ ...(slots.content || slots.header || (!props.description && !slots.description)) ? { 'aria-describedby': undefined } : {},
89
+ }))
90
+ const contentEvents = computed(() => {
91
+ if (props.dismissible)
92
+ return {}
70
93
 
71
- const { theme, createStyler } = useTheme()
72
- const style = computed(() => {
73
- const styler = createStyler(theme.value.drawer)
74
- return styler(props)
94
+ return {
95
+ pointerDownOutside: (e: Event) => e.preventDefault(),
96
+ interactOutside: (e: Event) => e.preventDefault(),
97
+ escapeKeyDown: (e: Event) => e.preventDefault(),
98
+ }
75
99
  })
100
+
101
+ const { t } = useLocale()
102
+ const { theme, generateStyle } = useTheme()
103
+ const style = computed(() => generateStyle('drawer', props))
76
104
  </script>
77
105
 
78
106
  <template>
79
- <DrawerRoot v-bind="rootProps">
80
- <DrawerTrigger v-if="slots.default" as-child :class="props.class">
81
- <slot></slot>
82
- </DrawerTrigger>
107
+ <DialogRoot v-slot="{ open }" v-bind="rootProps">
108
+ <DialogTrigger v-if="slots.default" as-child :class="props.class">
109
+ <slot :open="open"></slot>
110
+ </DialogTrigger>
83
111
 
84
- <DrawerPortal :disabled="!props.portal">
85
- <DrawerOverlay v-if="props.overlay" :class="style.overlay({ class: props.ui?.overlay })" />
112
+ <DialogPortal :disabled="!props.portal">
113
+ <DialogOverlay v-if="props.overlay" :class="style.overlay({ class: props.ui?.overlay })" />
86
114
 
87
- <DrawerContent
115
+ <DialogContent
88
116
  :class="style.content({ class: [!slots.default && props.class, props.ui?.content] })"
117
+ :data-direction="props.direction"
89
118
  v-bind="contentProps"
119
+ v-on="contentEvents"
120
+ @after-leave="emit('after-leave')"
90
121
  >
91
- <slot name="handle">
92
- <div v-if="props.handle" :class="style.handle({ class: props.ui?.handle })"></div>
93
- </slot>
122
+ <VisuallyHidden v-if="slots.content || slots.header || (!props.title && !slots.title)">
123
+ <DialogTitle />
124
+ </VisuallyHidden>
94
125
 
95
126
  <slot name="content">
96
127
  <div :class="style.container({ class: props.ui?.container })">
97
128
  <div
98
- v-if="slots.header || props.title || slots.title || props.description || slots.description"
129
+ v-if="slots.header || props.title || slots.title || props.description || slots.description || props.close || slots.close"
99
130
  :class="style.header({ class: props.ui?.header })"
100
131
  >
101
132
  <slot name="header">
102
- <DrawerTitle
133
+ <DialogTitle
103
134
  v-if="props.title || slots.title"
104
135
  :class="style.title({ class: props.ui?.title })"
105
136
  >
106
137
  <slot name="title">
107
138
  {{ props.title }}
108
139
  </slot>
109
- </DrawerTitle>
140
+ </DialogTitle>
141
+
142
+ <DialogClose v-if="props.close || slots.close" as-child>
143
+ <slot name="close">
144
+ <Button
145
+ variant="ghost"
146
+ :icon="props.closeIcon || theme.app.icons.close"
147
+ v-bind="typeof props.close === 'boolean' ? {} : props.close"
148
+ :class="style.close({ class: props.ui?.close })"
149
+ :aria-label="t('modal.close')"
150
+ />
151
+ </slot>
152
+ </DialogClose>
110
153
 
111
- <DrawerDescription
154
+ <DialogDescription
112
155
  v-if="props.description || slots.description"
113
156
  :class="style.description({ class: props.ui?.description })"
114
157
  >
115
158
  <slot name="description">
116
159
  {{ props.description }}
117
160
  </slot>
118
- </DrawerDescription>
161
+ </DialogDescription>
119
162
  </slot>
120
163
  </div>
121
164
 
@@ -128,7 +171,7 @@ const style = computed(() => {
128
171
  </div>
129
172
  </div>
130
173
  </slot>
131
- </DrawerContent>
132
- </DrawerPortal>
133
- </DrawerRoot>
174
+ </DialogContent>
175
+ </DialogPortal>
176
+ </DialogRoot>
134
177
  </template>
@@ -71,19 +71,16 @@ const inputRef = ref<HTMLInputElement | null>(null)
71
71
  const { size, orientation } = useButtonGroup(props)
72
72
  const { isLeading, leadingIconName, isTrailing, trailingIconName } = useComponentIcons(props)
73
73
 
74
- const { theme, createStyler } = useTheme()
75
- const style = computed(() => {
76
- const styler = createStyler(theme.value.input)
77
- return styler({
78
- ...props,
79
- // @ts-expect-error ignore type
80
- type: props.type,
81
- size: size.value,
82
- groupOrientation: orientation.value,
83
- leading: isLeading.value || !!slots.leading,
84
- trailing: isTrailing.value || !!slots.trailing,
85
- })
86
- })
74
+ const { generateStyle } = useTheme()
75
+ const style = computed(() => generateStyle('input', {
76
+ ...props,
77
+ // @ts-expect-error ignore type
78
+ type: props.type,
79
+ size: size.value,
80
+ groupOrientation: orientation.value,
81
+ leading: isLeading.value || !!slots.leading,
82
+ trailing: isTrailing.value || !!slots.trailing,
83
+ }))
87
84
 
88
85
  function autoFocus() {
89
86
  if (props.autofocus)
@@ -83,14 +83,11 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '
83
83
  const inputRef = ref<InstanceType<typeof NumberFieldInput> | null>(null)
84
84
 
85
85
  const { t } = useLocale()
86
- const { theme, createStyler } = useTheme()
86
+ const { theme, generateStyle } = useTheme()
87
87
  const incrementIcon = computed(() => props.incrementIcon || (props.orientation === 'horizontal' ? theme.value.app.icons.plus : theme.value.app.icons.chevronUp))
88
88
  const decrementIcon = computed(() => props.decrementIcon || (props.orientation === 'horizontal' ? theme.value.app.icons.minus : theme.value.app.icons.chevronDown))
89
89
 
90
- const style = computed(() => {
91
- const styler = createStyler(theme.value.inputNumber)
92
- return styler(props)
93
- })
90
+ const style = computed(() => generateStyle('inputNumber', props))
94
91
 
95
92
  onMounted(() => {
96
93
  setTimeout(() => {
@@ -36,12 +36,8 @@ const props = withDefaults(defineProps<KbdProps>(), {
36
36
  defineSlots<KbdSlots>()
37
37
 
38
38
  const { getKbdKey } = useKbd()
39
-
40
- const { theme, createStyler } = useTheme()
41
- const style = computed(() => {
42
- const styler = createStyler(theme.value.kbd)
43
- return styler(props)
44
- })
39
+ const { generateStyle } = useTheme()
40
+ const style = computed(() => generateStyle('kbd', props))
45
41
  </script>
46
42
 
47
43
  <template>
@@ -163,10 +163,12 @@ function isPartiallyEqual(item1: any, item2: any) {
163
163
  }
164
164
 
165
165
  const isExternalLink = computed(() => {
166
- if (!props.to)
166
+ const to = props.to || props.href
167
+
168
+ if (!to)
167
169
  return false
168
170
 
169
- return typeof props.to === 'string' && hasProtocol(props.to, { acceptRelative: true })
171
+ return typeof to === 'string' && hasProtocol(to, { acceptRelative: true })
170
172
  })
171
173
 
172
174
  function isLinkActive({ route: linkRoute, isActive, isExactActive }: any) {
@@ -301,6 +303,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any = {}) {
301
303
  href: to ? href : undefined,
302
304
  navigate,
303
305
  }"
306
+ :is-external="isExternalLink"
304
307
  :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive })"
305
308
  >
306
309
  <slot :active="isLinkActive({ route: linkRoute, isActive, isExactActive })">
@@ -318,7 +321,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any = {}) {
318
321
  type,
319
322
  disabled,
320
323
  href: to || href,
321
- target: isExternalLink ? '_blank' : target || undefined,
324
+ target: target || (isExternalLink ? '_blank' : undefined),
322
325
  active: false,
323
326
  }"
324
327
  >
@@ -332,8 +335,8 @@ function resolveLinkClass({ route, isActive, isExactActive }: any = {}) {
332
335
  as,
333
336
  type,
334
337
  disabled,
335
- href: ((to || href) as string),
336
- target: isExternalLink ? '_blank' : target || undefined,
338
+ href: ((typeof to === 'string' ? to : href) as string),
339
+ target: target || (isExternalLink ? '_blank' : undefined),
337
340
  }"
338
341
  :is-external="isExternalLink"
339
342
  :class="resolveLinkClass()"
@@ -4,7 +4,9 @@ import type { DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-
4
4
  import type { modal } from '../theme'
5
5
  import type { ButtonProps, ComponentAttrs } from '../types'
6
6
 
7
- export interface ModalEmits extends DialogRootEmits {}
7
+ export interface ModalEmits extends DialogRootEmits {
8
+ 'after-leave': []
9
+ }
8
10
 
9
11
  export interface ModalSlots {
10
12
  default?: (props: { open: boolean }) => any
@@ -35,6 +37,10 @@ export interface ModalProps extends ComponentAttrs<typeof modal>, DialogRootProp
35
37
  * @default true
36
38
  */
37
39
  dismissible?: boolean
40
+ /**
41
+ * Display a close button to dismiss the modal.
42
+ * @default true
43
+ */
38
44
  close?: ButtonProps | boolean
39
45
  /** @default app.icons.close */
40
46
  closeIcon?: string
@@ -43,7 +49,7 @@ export interface ModalProps extends ComponentAttrs<typeof modal>, DialogRootProp
43
49
 
44
50
  <script setup lang="ts">
45
51
  import { reactivePick } from '@vueuse/core'
46
- import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, useForwardPropsEmits } from 'reka-ui'
52
+ import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, useForwardPropsEmits, VisuallyHidden } from 'reka-ui'
47
53
  import { computed, toRef } from 'vue'
48
54
  import { useLocale } from '../composables/useLocale'
49
55
  import { useTheme } from '../composables/useTheme'
@@ -60,7 +66,10 @@ const props = withDefaults(defineProps<ModalProps>(), {
60
66
  const emit = defineEmits<ModalEmits>()
61
67
  const slots = defineSlots<ModalSlots>()
62
68
  const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emit)
63
- const contentProps = toRef(() => props.content)
69
+ const contentProps = toRef(() => ({
70
+ ...props.content,
71
+ ...(slots.content || slots.header || (!props.description && !slots.description)) ? { 'aria-describedby': undefined } : {},
72
+ }))
64
73
  const contentEvents = computed(() => {
65
74
  if (props.dismissible)
66
75
  return {}
@@ -73,11 +82,8 @@ const contentEvents = computed(() => {
73
82
  })
74
83
 
75
84
  const { t } = useLocale()
76
- const { theme, createStyler } = useTheme()
77
- const style = computed(() => {
78
- const styler = createStyler(theme.value.modal)
79
- return styler(props)
80
- })
85
+ const { theme, generateStyle } = useTheme()
86
+ const style = computed(() => generateStyle('modal', props))
81
87
  </script>
82
88
 
83
89
  <template>
@@ -92,7 +98,11 @@ const style = computed(() => {
92
98
  <DialogPortal :disabled="!props.portal">
93
99
  <DialogOverlay v-if="props.overlay" :class="style.overlay({ class: props.ui?.overlay })" />
94
100
 
95
- <DialogContent :class="style.content({ class: props.ui?.content })" v-bind="contentProps" v-on="contentEvents">
101
+ <DialogContent :class="style.content({ class: props.ui?.content })" v-bind="contentProps" v-on="contentEvents" @after-leave="emit('after-leave')">
102
+ <VisuallyHidden v-if="slots.content || slots.header || (!props.title && !slots.title)">
103
+ <DialogTitle />
104
+ </VisuallyHidden>
105
+
96
106
  <slot name="content">
97
107
  <div
98
108
  v-if="slots.header || props.title || slots.title || props.description || slots.description || props.close || slots.close"
@@ -108,10 +118,9 @@ const style = computed(() => {
108
118
  </slot>
109
119
  </DialogTitle>
110
120
 
111
- <DialogClose as-child>
121
+ <DialogClose v-if="props.close || slots.close" as-child>
112
122
  <slot name="close">
113
123
  <Button
114
- v-if="props.close"
115
124
  variant="ghost"
116
125
  :icon="props.closeIcon || theme.app.icons.close"
117
126
  v-bind="typeof props.close === 'boolean' ? {} : props.close"
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { OverlayComponentState } from '../composables/useOverlay'
4
+ import { useOverlay } from '../composables/useOverlay'
5
+
6
+ const { overlays, unmount, close } = useOverlay()
7
+ const mountedOverlays = computed(() => overlays.filter((overlay) => overlay.isMounted))
8
+
9
+ function onOverlayUnmount(id: OverlayComponentState['id']) {
10
+ close(id)
11
+ unmount(id)
12
+ }
13
+
14
+ function onClose(id: OverlayComponentState['id'], value: any) {
15
+ close(id, value)
16
+ }
17
+ </script>
18
+
19
+ <template>
20
+ <component
21
+ :is="overlay.component"
22
+ v-for="overlay in mountedOverlays"
23
+ :key="overlay.id"
24
+ v-bind="overlay.props"
25
+ v-model:open="overlay.modelValue"
26
+ @close="onClose(overlay.id, $event)"
27
+ @after-leave="onOverlayUnmount(overlay.id)"
28
+ />
29
+ </template>
@@ -70,6 +70,10 @@ export interface PaginationProps extends ComponentAttrs<typeof pagination>, Pick
70
70
  * @default true
71
71
  */
72
72
  showControls?: boolean
73
+ /**
74
+ * A function to render page controls as links.
75
+ */
76
+ to?: (page: number) => ButtonProps['to']
73
77
  }
74
78
  </script>
75
79
 
@@ -77,6 +81,7 @@ export interface PaginationProps extends ComponentAttrs<typeof pagination>, Pick
77
81
  import { reactivePick } from '@vueuse/core'
78
82
  import { PaginationEllipsis, PaginationFirst, PaginationLast, PaginationList, PaginationListItem, PaginationNext, PaginationPrev, PaginationRoot, useForwardPropsEmits } from 'reka-ui'
79
83
  import { computed } from 'vue'
84
+ import { useLocale } from '../composables/useLocale'
80
85
  import { useTheme } from '../composables/useTheme'
81
86
  import Button from './Button.vue'
82
87
 
@@ -95,71 +100,73 @@ const slots = defineSlots<PaginationSlots>()
95
100
 
96
101
  const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultPage', 'disabled', 'itemsPerPage', 'page', 'showEdges', 'siblingCount', 'total'), emit)
97
102
 
98
- const { theme, createStyler } = useTheme()
103
+ const { dir } = useLocale()
104
+ const { theme, generateStyle } = useTheme()
105
+ const style = computed(() => generateStyle('pagination', props))
99
106
 
100
- const firstIcon = computed(() => props.firstIcon || theme.value.app.icons.chevronDoubleLeft)
101
- const prevIcon = computed(() => props.prevIcon || theme.value.app.icons.chevronLeft)
102
- const nextIcon = computed(() => props.nextIcon || theme.value.app.icons.chevronRight)
103
- const lastIcon = computed(() => props.lastIcon || theme.value.app.icons.chevronDoubleRight)
107
+ const firstIcon = computed(() => props.firstIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronDoubleRight : theme.value.app.icons.chevronDoubleLeft))
108
+ const prevIcon = computed(() => props.prevIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronRight : theme.value.app.icons.chevronLeft))
109
+ const nextIcon = computed(() => props.nextIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronLeft : theme.value.app.icons.chevronRight))
110
+ const lastIcon = computed(() => props.lastIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronDoubleLeft : theme.value.app.icons.chevronDoubleRight))
104
111
  const ellipsisIcon = computed(() => props.ellipsisIcon || theme.value.app.icons.ellipsis)
105
-
106
- const style = computed(() => {
107
- const styler = createStyler(theme.value.pagination)
108
- return styler(props)
109
- })
110
112
  </script>
111
113
 
112
114
  <template>
113
115
  <PaginationRoot v-slot="{ page, pageCount }" v-bind="rootProps" :class="style.root({ class: [props.class, props.ui?.root] })">
114
116
  <PaginationList v-slot="{ items }" :class="style.list({ class: props.ui?.list })">
115
- <PaginationFirst v-if="props.showControls || !!slots.first" as-child>
117
+ <PaginationFirst v-if="props.showControls || !!slots.first" :class="style.first({ class: props.ui?.first })" as-child>
116
118
  <slot name="first">
117
- <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="firstIcon" />
119
+ <Button :variant="props.variant" :size="props.size" :icon="firstIcon" :to="props.to?.(1)" />
118
120
  </slot>
119
121
  </PaginationFirst>
120
- <PaginationPrev v-if="props.showControls || !!slots.prev" as-child>
122
+ <PaginationPrev v-if="props.showControls || !!slots.prev" :class="style.prev({ class: props.ui?.prev })" as-child>
121
123
  <slot name="prev">
122
- <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="prevIcon" />
124
+ <Button :variant="props.variant" :size="props.size" :icon="prevIcon" :to="page > 1 ? props.to?.(page - 1) : undefined" />
123
125
  </slot>
124
126
  </PaginationPrev>
125
127
 
126
128
  <template v-for="(item, index) in items">
127
- <PaginationListItem v-if="item.type === 'page'" :key="index" as-child :value="item.value">
129
+ <PaginationListItem v-if="item.type === 'page'" :key="index" :class="style.item({ class: props.ui?.item })" :value="item.value" as-child>
128
130
  <slot name="item" v-bind="{ item, index, page, pageCount }">
129
131
  <Button
130
- :class="style.item({ class: props.ui?.item })"
131
132
  :variant="props.page === item.value ? props.activeVariant : props.variant"
132
133
  :size="props.size"
133
134
  :label="String(item.value)"
134
- :ui="{ label: style.label() }"
135
+ :to="props.to?.(item.value)"
136
+ :ui="{ label: style.label({ class: props.ui?.label }) }"
135
137
  />
136
138
  </slot>
137
139
  </PaginationListItem>
138
140
 
139
- <PaginationEllipsis v-else :key="item.type" :index="index" as-child>
141
+ <PaginationEllipsis
142
+ v-else
143
+ :key="item.type"
144
+ :class="[
145
+ style.item({ class: props.ui?.item }),
146
+ style.ellipsis({ class: props.ui?.ellipsis }),
147
+ ]"
148
+ :index="index"
149
+ :disabled="props.disabled"
150
+ as-child
151
+ >
140
152
  <slot name="ellipsis">
141
153
  <Button
142
154
  :variant="props.variant"
143
155
  :size="props.size"
144
156
  :icon="ellipsisIcon"
145
- :disabled="props.disabled /* TODO: remove after reka-ui update */"
146
- :class="[
147
- style.item({ class: props.ui?.item }),
148
- style.ellipsis({ class: props.ui?.ellipsis }),
149
- ]"
150
157
  />
151
158
  </slot>
152
159
  </PaginationEllipsis>
153
160
  </template>
154
161
 
155
- <PaginationNext v-if="props.showControls || !!slots.next" as-child>
162
+ <PaginationNext v-if="props.showControls || !!slots.next" :class="style.next({ class: props.ui?.next })" as-child>
156
163
  <slot name="next">
157
- <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="nextIcon" />
164
+ <Button :variant="props.variant" :size="props.size" :icon="nextIcon" :to="page < pageCount ? props.to?.(page + 1) : undefined" />
158
165
  </slot>
159
166
  </PaginationNext>
160
- <PaginationLast v-if="props.showControls || !!slots.last" as-child>
167
+ <PaginationLast v-if="props.showControls || !!slots.last" :class="style.last({ class: props.ui?.last })" as-child>
161
168
  <slot name="last">
162
- <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="lastIcon" />
169
+ <Button :variant="props.variant" :size="props.size" :icon="lastIcon" :to="props.to?.(pageCount)" />
163
170
  </slot>
164
171
  </PaginationLast>
165
172
  </PaginationList>
@@ -41,11 +41,8 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultValue', 'disa
41
41
 
42
42
  const completed = ref(false)
43
43
 
44
- const { theme, createStyler } = useTheme()
45
- const style = computed(() => {
46
- const styler = createStyler(theme.value.pinInput)
47
- return styler(props)
48
- })
44
+ const { generateStyle } = useTheme()
45
+ const style = computed(() => generateStyle('pinInput', props))
49
46
 
50
47
  function onComplete(value: string[]) {
51
48
  // @ts-expect-error - 'target' does not exist in type 'EventInit'
@@ -69,11 +69,8 @@ const arrowProps = toRef(() => props.arrow as PopoverArrowProps)
69
69
 
70
70
  const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
71
71
 
72
- const { theme, createStyler } = useTheme()
73
- const style = computed(() => {
74
- const styler = createStyler(theme.value.popover)
75
- return styler(props)
76
- })
72
+ const { generateStyle } = useTheme()
73
+ const style = computed(() => generateStyle('popover', props))
77
74
  </script>
78
75
 
79
76
  <template>
@@ -83,11 +83,8 @@ const percent = computed(() => {
83
83
  })
84
84
 
85
85
  const { dir } = useLocale()
86
- const { theme, createStyler } = useTheme()
87
- const style = computed(() => {
88
- const styler = createStyler(theme.value.progress)
89
- return styler(props)
90
- })
86
+ const { generateStyle } = useTheme()
87
+ const style = computed(() => generateStyle('progress', props))
91
88
 
92
89
  const indicatorStyle = computed(() => {
93
90
  if (percent.value === undefined)
@@ -25,7 +25,7 @@ type NormalizeItem<T> = { id: string } & (
25
25
  label: string
26
26
  value: any
27
27
  description: string
28
- disabled: false
28
+ disabled: boolean
29
29
  }
30
30
  )
31
31
 
@@ -90,11 +90,8 @@ const slots = defineSlots<RadioGroupSlots<T>>()
90
90
  const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emit)
91
91
  const id = useId()
92
92
 
93
- const { theme, createStyler } = useTheme()
94
- const style = computed(() => {
95
- const styler = createStyler(theme.value.radioGroup)
96
- return styler(props)
97
- })
93
+ const { generateStyle } = useTheme()
94
+ const style = computed(() => generateStyle('radioGroup', props))
98
95
 
99
96
  function normalizeItem(item: any): NormalizeItem<T> {
100
97
  if (['string', 'number', 'boolean'].includes(typeof item)) {
@@ -103,6 +100,7 @@ function normalizeItem(item: any): NormalizeItem<T> {
103
100
  value: item,
104
101
  label: item,
105
102
  description: '',
103
+ disabled: props.disabled,
106
104
  } as any
107
105
  }
108
106
 
@@ -116,6 +114,7 @@ function normalizeItem(item: any): NormalizeItem<T> {
116
114
  label,
117
115
  description,
118
116
  id: `${id}:${value}`,
117
+ disabled: props.disabled || item.disabled,
119
118
  }
120
119
  }
121
120
 
@@ -149,7 +148,7 @@ function onUpdate(value: any) {
149
148
  {{ props.legend }}
150
149
  </slot>
151
150
  </legend>
152
- <div v-for="item in normalizedItems" :key="item.value" :class="style.item({ class: props.ui?.item })">
151
+ <div v-for="item in normalizedItems" :key="item.value" :class="style.item({ class: props.ui?.item, disabled: item.disabled })">
153
152
  <div :class="style.container({ class: props.ui?.container })">
154
153
  <RadioGroupItem
155
154
  :id="item.id"
@@ -17,12 +17,8 @@ const props = withDefaults(defineProps<ScrollAreaProps>(), {})
17
17
  const rootRef = ref<InstanceType<typeof ScrollAreaRoot>>()
18
18
  const rootProps = reactivePick(props, 'type', 'dir', 'scrollHideDelay')
19
19
 
20
- const { theme, createStyler } = useTheme()
21
-
22
- const style = computed(() => {
23
- const styler = createStyler(theme.value.scrollArea)
24
- return styler(props)
25
- })
20
+ const { generateStyle } = useTheme()
21
+ const style = computed(() => generateStyle('scrollArea', props))
26
22
 
27
23
  defineExpose({
28
24
  scrollTop,