@byyuurin/ui 0.0.8 → 0.0.10

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 (151) hide show
  1. package/README.md +0 -3
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +4 -3
  4. package/dist/module.mjs.map +1 -1
  5. package/dist/runtime/app/injections.d.ts +9299 -8
  6. package/dist/runtime/app/injections.js +35 -5
  7. package/dist/runtime/components/Accordion.vue +17 -19
  8. package/dist/runtime/components/Alert.vue +6 -9
  9. package/dist/runtime/components/App.vue +14 -19
  10. package/dist/runtime/components/Avatar.vue +5 -8
  11. package/dist/runtime/components/AvatarGroup.vue +2 -5
  12. package/dist/runtime/components/Badge.vue +3 -6
  13. package/dist/runtime/components/Breadcrumb.vue +22 -23
  14. package/dist/runtime/components/Button.vue +2 -3
  15. package/dist/runtime/components/ButtonGroup.vue +2 -5
  16. package/dist/runtime/components/Calendar.vue +185 -0
  17. package/dist/runtime/components/Card.vue +2 -5
  18. package/dist/runtime/components/Carousel.vue +11 -16
  19. package/dist/runtime/components/Checkbox.vue +13 -11
  20. package/dist/runtime/components/Chip.vue +6 -9
  21. package/dist/runtime/components/Collapsible.vue +2 -5
  22. package/dist/runtime/components/Drawer.vue +88 -45
  23. package/dist/runtime/components/DropdownMenu.vue +143 -0
  24. package/dist/runtime/components/DropdownMenuContent.vue +188 -0
  25. package/dist/runtime/components/Form.vue +311 -0
  26. package/dist/runtime/components/FormItem.vue +129 -0
  27. package/dist/runtime/components/Input.vue +34 -23
  28. package/dist/runtime/components/InputNumber.vue +23 -18
  29. package/dist/runtime/components/Kbd.vue +2 -6
  30. package/dist/runtime/components/Link.vue +25 -7
  31. package/dist/runtime/components/Modal.vue +31 -22
  32. package/dist/runtime/components/OverlayProvider.vue +29 -0
  33. package/dist/runtime/components/Pagination.vue +34 -27
  34. package/dist/runtime/components/PinInput.vue +23 -17
  35. package/dist/runtime/components/Popover.vue +5 -8
  36. package/dist/runtime/components/Progress.vue +2 -5
  37. package/dist/runtime/components/RadioGroup.vue +52 -49
  38. package/dist/runtime/components/ScrollArea.vue +2 -6
  39. package/dist/runtime/components/Select.vue +96 -89
  40. package/dist/runtime/components/Separator.vue +2 -6
  41. package/dist/runtime/components/Skeleton.vue +2 -5
  42. package/dist/runtime/components/Slider.vue +13 -11
  43. package/dist/runtime/components/Switch.vue +18 -11
  44. package/dist/runtime/components/Table.vue +23 -13
  45. package/dist/runtime/components/Tabs.vue +14 -16
  46. package/dist/runtime/components/Textarea.vue +20 -17
  47. package/dist/runtime/components/Toast.vue +12 -13
  48. package/dist/runtime/components/{Toaster.vue → ToastProvider.vue} +18 -16
  49. package/dist/runtime/components/Tooltip.vue +5 -8
  50. package/dist/runtime/composables/useFormItem.d.ts +27 -0
  51. package/dist/runtime/composables/useFormItem.js +64 -0
  52. package/dist/runtime/composables/useKbd.d.ts +1 -1
  53. package/dist/runtime/composables/useOverlay.d.ts +29 -0
  54. package/dist/runtime/composables/useOverlay.js +69 -0
  55. package/dist/runtime/composables/useTheme.d.ts +6 -2
  56. package/dist/runtime/composables/useTheme.js +10 -3
  57. package/dist/runtime/composables/useToast.d.ts +4 -20
  58. package/dist/runtime/composables/useToast.js +6 -5
  59. package/dist/runtime/index.d.ts +6 -2
  60. package/dist/runtime/index.js +6 -2
  61. package/dist/runtime/locale/en.js +6 -0
  62. package/dist/runtime/locale/zh-tw.js +6 -0
  63. package/dist/runtime/theme/accordion.js +3 -3
  64. package/dist/runtime/theme/alert.js +3 -3
  65. package/dist/runtime/theme/app.d.ts +1 -0
  66. package/dist/runtime/theme/app.js +2 -1
  67. package/dist/runtime/theme/avatar.js +2 -2
  68. package/dist/runtime/theme/badge.d.ts +21 -45
  69. package/dist/runtime/theme/breadcrumb.d.ts +3 -3
  70. package/dist/runtime/theme/breadcrumb.js +5 -5
  71. package/dist/runtime/theme/button.d.ts +111 -57
  72. package/dist/runtime/theme/button.js +13 -13
  73. package/dist/runtime/theme/calendar.d.ts +56 -0
  74. package/dist/runtime/theme/calendar.js +69 -0
  75. package/dist/runtime/theme/card.js +6 -6
  76. package/dist/runtime/theme/carousel.js +1 -1
  77. package/dist/runtime/theme/checkbox.js +5 -5
  78. package/dist/runtime/theme/chip.d.ts +11 -44
  79. package/dist/runtime/theme/chip.js +3 -3
  80. package/dist/runtime/theme/drawer.d.ts +85 -47
  81. package/dist/runtime/theme/drawer.js +46 -19
  82. package/dist/runtime/theme/dropdown-menu.d.ts +71 -0
  83. package/dist/runtime/theme/dropdown-menu.js +83 -0
  84. package/dist/runtime/theme/form-item.d.ts +76 -0
  85. package/dist/runtime/theme/form-item.js +34 -0
  86. package/dist/runtime/theme/form.d.ts +8 -0
  87. package/dist/runtime/theme/form.js +7 -0
  88. package/dist/runtime/theme/index.d.ts +5 -1
  89. package/dist/runtime/theme/index.js +5 -1
  90. package/dist/runtime/theme/input-number.d.ts +41 -61
  91. package/dist/runtime/theme/input-number.js +1 -1
  92. package/dist/runtime/theme/input.d.ts +99 -71
  93. package/dist/runtime/theme/input.js +15 -15
  94. package/dist/runtime/theme/kbd.d.ts +2 -2
  95. package/dist/runtime/theme/kbd.js +1 -1
  96. package/dist/runtime/theme/link.d.ts +1 -1
  97. package/dist/runtime/theme/link.js +3 -3
  98. package/dist/runtime/theme/modal.d.ts +5 -33
  99. package/dist/runtime/theme/modal.js +4 -4
  100. package/dist/runtime/theme/pagination.d.ts +27 -3
  101. package/dist/runtime/theme/pagination.js +6 -2
  102. package/dist/runtime/theme/pinInput.d.ts +42 -42
  103. package/dist/runtime/theme/pinInput.js +13 -13
  104. package/dist/runtime/theme/progress.d.ts +117 -53
  105. package/dist/runtime/theme/progress.js +4 -4
  106. package/dist/runtime/theme/radio-group.d.ts +2 -2
  107. package/dist/runtime/theme/radio-group.js +7 -7
  108. package/dist/runtime/theme/select.d.ts +100 -84
  109. package/dist/runtime/theme/select.js +21 -20
  110. package/dist/runtime/theme/separator.d.ts +13 -28
  111. package/dist/runtime/theme/separator.js +1 -1
  112. package/dist/runtime/theme/skeleton.d.ts +1 -1
  113. package/dist/runtime/theme/skeleton.js +1 -1
  114. package/dist/runtime/theme/slider.js +1 -1
  115. package/dist/runtime/theme/switch.js +5 -5
  116. package/dist/runtime/theme/table.d.ts +3 -0
  117. package/dist/runtime/theme/table.js +8 -7
  118. package/dist/runtime/theme/tabs.d.ts +51 -68
  119. package/dist/runtime/theme/tabs.js +10 -10
  120. package/dist/runtime/theme/textarea.d.ts +37 -43
  121. package/dist/runtime/theme/textarea.js +13 -13
  122. package/dist/runtime/theme/{toaster.d.ts → toast-provider.d.ts} +26 -41
  123. package/dist/runtime/theme/toast.js +5 -5
  124. package/dist/runtime/theme/tooltip.js +1 -1
  125. package/dist/runtime/types/components.d.ts +6 -1
  126. package/dist/runtime/types/form.d.ts +45 -0
  127. package/dist/runtime/types/form.js +0 -0
  128. package/dist/runtime/types/index.d.ts +5 -2
  129. package/dist/runtime/types/index.js +1 -0
  130. package/dist/runtime/types/locale.d.ts +6 -0
  131. package/dist/runtime/types/utils.d.ts +35 -12
  132. package/dist/runtime/utils/extend-theme.js +15 -4
  133. package/dist/runtime/utils/form.d.ts +5 -0
  134. package/dist/runtime/utils/form.js +24 -0
  135. package/dist/runtime/utils/index.d.ts +2 -0
  136. package/dist/runtime/utils/index.js +4 -0
  137. package/dist/runtime/utils/link.d.ts +4 -26
  138. package/dist/runtime/utils/link.js +10 -3
  139. package/dist/shared/ui.3e7fad19.mjs +5 -0
  140. package/dist/shared/ui.3e7fad19.mjs.map +1 -0
  141. package/dist/unocss.mjs +21 -16
  142. package/dist/unocss.mjs.map +1 -1
  143. package/dist/unplugin.mjs +1 -1
  144. package/dist/vite.mjs +1 -1
  145. package/package.json +20 -18
  146. package/dist/runtime/components/ModalProvider.vue +0 -11
  147. package/dist/runtime/composables/useModal.d.ts +0 -10
  148. package/dist/runtime/composables/useModal.js +0 -47
  149. package/dist/shared/ui.ba24b380.mjs +0 -4
  150. package/dist/shared/ui.ba24b380.mjs.map +0 -1
  151. /package/dist/runtime/theme/{toaster.js → toast-provider.js} +0 -0
@@ -11,13 +11,15 @@ import type { AcceptableValue, PrimitiveProps } from 'reka-ui'
11
11
  import type { carousel } from '../theme'
12
12
  import type { ButtonProps, ComponentAttrs } from '../types'
13
13
 
14
- export interface CarouselSlots<T> {
14
+ export type CarouselItem = AcceptableValue
15
+
16
+ export interface CarouselSlots<T extends CarouselItem = CarouselItem> {
15
17
  default?: (props: { item: T, index: number }) => any
16
18
  }
17
19
 
18
20
  type CarouselVariants = VariantProps<typeof carousel>
19
21
 
20
- export interface CarouselProps<T> extends ComponentAttrs<typeof carousel>, Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
22
+ export interface CarouselProps<T extends CarouselItem = CarouselItem> extends ComponentAttrs<typeof carousel>, Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
21
23
  /**
22
24
  * The element or component this component should render as.
23
25
  * @default "div"
@@ -55,7 +57,6 @@ export interface CarouselProps<T> extends ComponentAttrs<typeof carousel>, Omit<
55
57
  dots?: boolean
56
58
  orientation?: CarouselVariants['orientation']
57
59
  items?: T[]
58
- dir?: 'rtl' | 'ltr'
59
60
  /**
60
61
  * Enable Autoplay plugin
61
62
  * @link https://www.embla-carousel.com/plugins/autoplay/
@@ -89,7 +90,7 @@ export interface CarouselProps<T> extends ComponentAttrs<typeof carousel>, Omit<
89
90
  }
90
91
  </script>
91
92
 
92
- <script setup lang="ts" generic="T extends AcceptableValue">
93
+ <script setup lang="ts" generic="T extends CarouselItem">
93
94
  import { computedAsync, reactivePick } from '@vueuse/core'
94
95
  import useEmblaCarousel from 'embla-carousel-vue'
95
96
  import { Primitive, useForwardProps } from 'reka-ui'
@@ -103,8 +104,6 @@ const props = withDefaults(defineProps<CarouselProps<T>>(), {
103
104
  arrows: false,
104
105
  dots: false,
105
106
 
106
- dir: 'ltr',
107
-
108
107
  // Embla Options
109
108
  active: true,
110
109
  align: 'center',
@@ -135,21 +134,18 @@ defineSlots<CarouselSlots<T>>()
135
134
 
136
135
  const rootProps = useForwardProps(reactivePick(props, 'active', 'align', 'breakpoints', 'containScroll', 'dragFree', 'dragThreshold', 'duration', 'inViewThreshold', 'loop', 'skipSnaps', 'slidesToScroll', 'startIndex', 'watchDrag', 'watchResize', 'watchSlides', 'watchFocus'))
137
136
 
138
- const { t } = useLocale()
139
- const { theme, createStyler } = useTheme()
140
- const style = computed(() => {
141
- const styler = createStyler(theme.value.carousel)
142
- return styler(props)
143
- })
137
+ const { t, dir } = useLocale()
138
+ const { theme, generateStyle } = useTheme()
139
+ const style = computed(() => generateStyle('carousel', props))
144
140
 
145
- const prevIcon = computed(() => props.prevIcon || (props.dir === 'rtl' ? theme.value.app.icons.chevronRight : theme.value.app.icons.chevronLeft))
146
- const nextIcon = computed(() => props.nextIcon || (props.dir === 'rtl' ? theme.value.app.icons.chevronLeft : theme.value.app.icons.chevronRight))
141
+ const prevIcon = computed(() => props.prevIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronRight : theme.value.app.icons.chevronLeft))
142
+ const nextIcon = computed(() => props.nextIcon || (dir.value === 'rtl' ? theme.value.app.icons.chevronLeft : theme.value.app.icons.chevronRight))
147
143
 
148
144
  const options = computed<EmblaOptionsType>(() => ({
149
145
  ...(props.fade ? { align: 'center', containScroll: false } : {}),
150
146
  ...rootProps.value,
151
147
  axis: props.orientation === 'horizontal' ? 'x' : 'y',
152
- direction: props.dir === 'rtl' ? 'rtl' : 'ltr',
148
+ direction: dir.value === 'rtl' ? 'rtl' : 'ltr',
153
149
  }))
154
150
 
155
151
  const plugins = computedAsync<EmblaPluginType[]>(async () => {
@@ -261,7 +257,6 @@ defineExpose({
261
257
  role="region"
262
258
  aria-roledescription="carousel"
263
259
  tabindex="0"
264
- :dir="props.dir"
265
260
  :class="style.root({ class: [props.class, props.ui?.root] })"
266
261
  @keydown="onKeyDown"
267
262
  >
@@ -5,7 +5,7 @@ import type { checkbox } from '../theme'
5
5
  import type { ComponentAttrs } from '../types'
6
6
 
7
7
  export interface CheckboxEmits {
8
- (event: 'change', payload: Event): void
8
+ change: [payload: Event]
9
9
  }
10
10
 
11
11
  export interface CheckboxSlots {
@@ -41,6 +41,7 @@ export interface CheckboxProps extends ComponentAttrs<typeof checkbox>, Pick<Che
41
41
  import { reactivePick } from '@vueuse/core'
42
42
  import { CheckboxIndicator, CheckboxRoot, Label, Primitive, useForwardProps } from 'reka-ui'
43
43
  import { computed, useId } from 'vue'
44
+ import { useFormItem } from '../composables/useFormItem'
44
45
  import { useTheme } from '../composables/useTheme'
45
46
 
46
47
  const props = withDefaults(defineProps<CheckboxProps>(), {
@@ -53,18 +54,22 @@ const slots = defineSlots<CheckboxSlots>()
53
54
  const innerValue = defineModel<boolean | 'indeterminate'>({ default: undefined })
54
55
  const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
55
56
 
56
- const id = props.id ?? useId()
57
+ const { id: _id, size, name, disabled, ariaAttrs, emitFormChange, emitFormInput } = useFormItem<CheckboxProps>(props)
58
+ const id = _id.value ?? useId()
57
59
 
58
- const { theme, createStyler } = useTheme()
59
- const style = computed(() => {
60
- const styler = createStyler(theme.value.checkbox)
61
- return styler(props)
62
- })
60
+ const { theme, generateStyle } = useTheme()
61
+ const style = computed(() => generateStyle('checkbox', {
62
+ ...props,
63
+ size: size.value,
64
+ disabled: disabled.value,
65
+ }))
63
66
 
64
67
  function onUpdate(value: any) {
65
68
  // @ts-expect-error - 'target' does not exist in type 'EventInit'
66
69
  const event = new Event('change', { target: value })
67
70
  emit('change', event)
71
+ emitFormChange()
72
+ emitFormInput()
68
73
  }
69
74
  </script>
70
75
 
@@ -72,12 +77,9 @@ function onUpdate(value: any) {
72
77
  <Primitive :as="props.as" :class="style.root({ class: [props.class, props.ui?.root] })">
73
78
  <div :class="style.container({ class: props.ui?.container })">
74
79
  <CheckboxRoot
75
- :id="id"
76
- v-bind="rootProps"
77
80
  v-slot="{ modelValue }"
81
+ v-bind="{ ...rootProps, ...ariaAttrs, id, name, disabled }"
78
82
  v-model="innerValue"
79
- :name="props.name"
80
- :disabled="props.disabled"
81
83
  :class="style.base({ class: props.ui?.base })"
82
84
  @update:model-value="onUpdate"
83
85
  >
@@ -41,15 +41,12 @@ const slots = defineSlots<ChipSlots>()
41
41
  const { size, orientation } = useButtonGroup(props)
42
42
  const { isLeading, leadingIconName, isTrailing, trailingIconName } = useComponentIcons(props)
43
43
 
44
- const { theme, createStyler } = useTheme()
45
- const style = computed(() => {
46
- const styler = createStyler(theme.value.chip)
47
- return styler({
48
- ...props,
49
- size: size.value,
50
- groupOrientation: orientation.value,
51
- })
52
- })
44
+ const { generateStyle } = useTheme()
45
+ const style = computed(() => generateStyle('chip', {
46
+ ...props,
47
+ size: size.value,
48
+ groupOrientation: orientation.value,
49
+ }))
53
50
  </script>
54
51
 
55
52
  <template>
@@ -28,11 +28,8 @@ const slots = defineSlots<CollapsibleSlots>()
28
28
 
29
29
  const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled', 'unmountOnHide'), emit)
30
30
 
31
- const { theme, createStyler } = useTheme()
32
- const style = computed(() => {
33
- const styler = createStyler(theme.value.collapsible)
34
- return styler(props)
35
- })
31
+ const { generateStyle } = useTheme()
32
+ const style = computed(() => generateStyle('collapsible', props))
36
33
  </script>
37
34
 
38
35
  <template>
@@ -1,46 +1,59 @@
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 { DialogContentEmits, DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-ui'
4
4
  import type { drawer } from '../theme'
5
- import type { ComponentAttrs } from '../types'
5
+ import type { ButtonProps, ComponentAttrs, EmitsToProps } 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
- default?: (props?: {}) => any
11
- handle?: (props?: {}) => any
12
- content?: (props?: {}) => any
13
- header?: (props?: {}) => any
14
- title?: (props?: {}) => any
15
- description?: (props?: {}) => any
16
- body?: (props?: {}) => any
17
- footer?: (props?: {}) => any
12
+ default?: any
13
+ content?: any
14
+ header?: any
15
+ title?: any
16
+ description?: any
17
+ close?: (props: { ui: ComponentAttrs<typeof drawer>['ui'] }) => any
18
+ body?: any
19
+ footer?: 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. */
24
- content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
28
+ content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
25
29
  /**
26
30
  * Render an overlay behind the drawer.
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" :ui="props.ui">
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>
@@ -0,0 +1,143 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from '@byyuurin/ui-kit'
3
+ import type { DropdownMenuArrowProps, DropdownMenuContentEmits, DropdownMenuContentProps, DropdownMenuRootEmits, DropdownMenuRootProps } from 'reka-ui'
4
+ import type { dropdownMenu } from '../theme'
5
+ import type { ArrayOrNested, AvatarProps, ComponentAttrs, DynamicSlots, EmitsToProps, KbdProps, LinkProps, MergeTypes, NestedItem } from '../types'
6
+
7
+ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom' | 'underline'> {
8
+ icon?: string
9
+ avatar?: AvatarProps
10
+ content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>
11
+ kbds?: Array<string | KbdProps['value']>
12
+ /**
13
+ * The item type.
14
+ * @default 'link'
15
+ */
16
+ type?: 'label' | 'separator' | 'link' | 'checkbox'
17
+ slot?: string
18
+ loading?: boolean
19
+ disabled?: boolean
20
+ checked?: boolean
21
+ open?: boolean
22
+ defaultOpen?: boolean
23
+ children?: ArrayOrNested<DropdownMenuItem>
24
+ onSelect?: (e: Event) => void
25
+ onUpdateChecked?: (checked: boolean) => void
26
+ [key: string]: any
27
+ }
28
+
29
+ type SlotProps<T extends DropdownMenuItem> = (props: { item: T, active?: boolean, index: number }) => any
30
+
31
+ export type DropdownMenuSlots<
32
+ T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
33
+ I extends NestedItem<T> = NestedItem<T>,
34
+ > = {
35
+ 'default'?: (props: { open: boolean }) => any
36
+ 'item'?: SlotProps<I>
37
+ 'item-leading'?: SlotProps<I>
38
+ 'item-label'?: SlotProps<I>
39
+ 'item-trailing'?: SlotProps<I>
40
+ } & DynamicSlots<MergeTypes<I>, 'leading' | 'label' | 'trailing', SlotProps<I>>
41
+
42
+ export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
43
+
44
+ type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
45
+
46
+ export interface DropdownMenuProps<
47
+ T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
48
+ > extends ComponentAttrs<typeof dropdownMenu>, Omit<DropdownMenuRootProps, 'dir'> {
49
+ /** @default "md" */
50
+ size?: DropdownMenuVariants['size']
51
+ items?: T
52
+ /**
53
+ * The icon displayed when an item is checked.
54
+ * @default app.icons.check
55
+ */
56
+ checkedIcon?: string
57
+ /**
58
+ * The icon displayed when an item is loading.
59
+ * @default app.icons.loading
60
+ */
61
+ loadingIcon?: string
62
+ /**
63
+ * The icon displayed when the item is an external link.
64
+ * Set to `false` to hide the external icon.
65
+ * @default app.icons.external
66
+ */
67
+ externalIcon?: boolean | string
68
+ /**
69
+ * The content of the menu.
70
+ * @default { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
71
+ */
72
+ content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>
73
+ /**
74
+ * Display an arrow alongside the menu.
75
+ * @default false
76
+ */
77
+ arrow?: boolean | Omit<DropdownMenuArrowProps, 'as' | 'asChild'>
78
+ /**
79
+ * Render the menu in a portal.
80
+ * @default true
81
+ */
82
+ portal?: boolean
83
+ /**
84
+ * The key used to get the label from the item.
85
+ * @default "label"
86
+ */
87
+ labelKey?: keyof NestedItem<T>
88
+ disabled?: boolean
89
+ }
90
+ </script>
91
+
92
+ <script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
93
+ import { reactivePick } from '@vueuse/core'
94
+ import { DropdownMenuArrow, DropdownMenuRoot, DropdownMenuTrigger, useForwardPropsEmits } from 'reka-ui'
95
+ import { computed, toRef } from 'vue'
96
+ import { useTheme } from '../composables/useTheme'
97
+ import { omit } from '../utils'
98
+ import DropdownMenuContent from './DropdownMenuContent.vue'
99
+
100
+ const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
101
+ portal: true,
102
+ modal: true,
103
+ externalIcon: true,
104
+ labelKey: 'label',
105
+ })
106
+ const emit = defineEmits<DropdownMenuEmits>()
107
+ const slots = defineSlots<DropdownMenuSlots<T>>()
108
+
109
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emit)
110
+ const contentProps = toRef(() => ({ side: 'bottom', sideOffset: 8, collisionPadding: 8, ...props.content }) as DropdownMenuContentProps)
111
+ const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
112
+ const proxySlots = omit(slots, ['default'])
113
+
114
+ const { generateStyle } = useTheme()
115
+ const style = computed(() => generateStyle('dropdownMenu', props))
116
+ </script>
117
+
118
+ <template>
119
+ <DropdownMenuRoot v-slot="{ open }" v-bind="rootProps">
120
+ <DropdownMenuTrigger v-if="slots.default" as-child :class="props.class" :disabled="props.disabled">
121
+ <slot :open="open"></slot>
122
+ </DropdownMenuTrigger>
123
+
124
+ <DropdownMenuContent
125
+ :class="style.content({ class: [!slots.default && props.class, props.ui?.content] })"
126
+ :ui="props.ui"
127
+ v-bind="contentProps"
128
+ :size="props.size"
129
+ :items="props.items"
130
+ :portal="props.portal"
131
+ :label-key="(props.labelKey as keyof NestedItem<T>)"
132
+ :checked-icon="props.checkedIcon"
133
+ :loading-icon="props.loadingIcon"
134
+ :external-icon="props.externalIcon"
135
+ >
136
+ <template v-for="(_, name) in proxySlots" #[name]="slotProps">
137
+ <slot :name="(name as keyof DropdownMenuSlots<T>)" v-bind="slotProps"></slot>
138
+ </template>
139
+
140
+ <DropdownMenuArrow v-if="props.arrow" v-bind="arrowProps" :class="style.arrow({ class: props.ui?.arrow })" />
141
+ </DropdownMenuContent>
142
+ </DropdownMenuRoot>
143
+ </template>