@byyuurin/ui 0.0.9 → 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 (91) hide show
  1. package/README.md +0 -3
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1 -1
  4. package/dist/runtime/app/injections.d.ts +9299 -3
  5. package/dist/runtime/app/injections.js +35 -0
  6. package/dist/runtime/components/Accordion.vue +16 -20
  7. package/dist/runtime/components/Alert.vue +1 -1
  8. package/dist/runtime/components/Badge.vue +1 -1
  9. package/dist/runtime/components/Breadcrumb.vue +17 -21
  10. package/dist/runtime/components/Calendar.vue +15 -6
  11. package/dist/runtime/components/Carousel.vue +5 -3
  12. package/dist/runtime/components/Checkbox.vue +12 -7
  13. package/dist/runtime/components/Drawer.vue +12 -12
  14. package/dist/runtime/components/DropdownMenu.vue +143 -0
  15. package/dist/runtime/components/DropdownMenuContent.vue +188 -0
  16. package/dist/runtime/components/Form.vue +311 -0
  17. package/dist/runtime/components/FormItem.vue +129 -0
  18. package/dist/runtime/components/Input.vue +27 -13
  19. package/dist/runtime/components/InputNumber.vue +22 -14
  20. package/dist/runtime/components/Link.vue +17 -2
  21. package/dist/runtime/components/Modal.vue +11 -11
  22. package/dist/runtime/components/PinInput.vue +22 -13
  23. package/dist/runtime/components/Popover.vue +3 -3
  24. package/dist/runtime/components/RadioGroup.vue +50 -46
  25. package/dist/runtime/components/Select.vue +90 -80
  26. package/dist/runtime/components/Slider.vue +12 -7
  27. package/dist/runtime/components/Switch.vue +12 -6
  28. package/dist/runtime/components/Table.vue +21 -8
  29. package/dist/runtime/components/Tabs.vue +12 -11
  30. package/dist/runtime/components/Textarea.vue +19 -13
  31. package/dist/runtime/components/Toast.vue +6 -3
  32. package/dist/runtime/components/Tooltip.vue +3 -3
  33. package/dist/runtime/composables/useFormItem.d.ts +27 -0
  34. package/dist/runtime/composables/useFormItem.js +64 -0
  35. package/dist/runtime/composables/useTheme.js +2 -1
  36. package/dist/runtime/index.d.ts +3 -0
  37. package/dist/runtime/index.js +3 -0
  38. package/dist/runtime/theme/app.d.ts +1 -0
  39. package/dist/runtime/theme/app.js +2 -1
  40. package/dist/runtime/theme/badge.d.ts +21 -45
  41. package/dist/runtime/theme/breadcrumb.d.ts +3 -3
  42. package/dist/runtime/theme/button.d.ts +111 -57
  43. package/dist/runtime/theme/calendar.d.ts +2 -2
  44. package/dist/runtime/theme/chip.d.ts +11 -44
  45. package/dist/runtime/theme/drawer.d.ts +68 -33
  46. package/dist/runtime/theme/dropdown-menu.d.ts +71 -0
  47. package/dist/runtime/theme/dropdown-menu.js +83 -0
  48. package/dist/runtime/theme/form-item.d.ts +76 -0
  49. package/dist/runtime/theme/form-item.js +34 -0
  50. package/dist/runtime/theme/form.d.ts +8 -0
  51. package/dist/runtime/theme/form.js +7 -0
  52. package/dist/runtime/theme/index.d.ts +3 -0
  53. package/dist/runtime/theme/index.js +3 -0
  54. package/dist/runtime/theme/input-number.d.ts +41 -61
  55. package/dist/runtime/theme/input.d.ts +99 -71
  56. package/dist/runtime/theme/input.js +2 -2
  57. package/dist/runtime/theme/modal.d.ts +5 -33
  58. package/dist/runtime/theme/pinInput.d.ts +42 -42
  59. package/dist/runtime/theme/pinInput.js +1 -1
  60. package/dist/runtime/theme/progress.d.ts +117 -53
  61. package/dist/runtime/theme/select.d.ts +100 -84
  62. package/dist/runtime/theme/select.js +2 -1
  63. package/dist/runtime/theme/separator.d.ts +13 -28
  64. package/dist/runtime/theme/table.d.ts +3 -0
  65. package/dist/runtime/theme/table.js +2 -1
  66. package/dist/runtime/theme/tabs.d.ts +51 -68
  67. package/dist/runtime/theme/textarea.d.ts +37 -43
  68. package/dist/runtime/theme/textarea.js +1 -1
  69. package/dist/runtime/theme/toast-provider.d.ts +26 -41
  70. package/dist/runtime/types/components.d.ts +3 -0
  71. package/dist/runtime/types/form.d.ts +45 -0
  72. package/dist/runtime/types/form.js +0 -0
  73. package/dist/runtime/types/index.d.ts +5 -2
  74. package/dist/runtime/types/index.js +1 -0
  75. package/dist/runtime/types/utils.d.ts +32 -11
  76. package/dist/runtime/utils/extend-theme.js +15 -4
  77. package/dist/runtime/utils/form.d.ts +5 -0
  78. package/dist/runtime/utils/form.js +24 -0
  79. package/dist/runtime/utils/index.d.ts +2 -0
  80. package/dist/runtime/utils/index.js +4 -0
  81. package/dist/runtime/utils/link.d.ts +4 -26
  82. package/dist/runtime/utils/link.js +10 -3
  83. package/dist/shared/ui.3e7fad19.mjs +5 -0
  84. package/dist/shared/ui.3e7fad19.mjs.map +1 -0
  85. package/dist/unocss.mjs +2 -2
  86. package/dist/unocss.mjs.map +1 -1
  87. package/dist/unplugin.mjs +1 -1
  88. package/dist/vite.mjs +1 -1
  89. package/package.json +16 -14
  90. package/dist/shared/ui.1a1f119c.mjs +0 -5
  91. package/dist/shared/ui.1a1f119c.mjs.map +0 -1
@@ -9,6 +9,41 @@ export const {
9
9
  inject: injectButtonGroup,
10
10
  provide: provideButtonGroup
11
11
  } = defineInjection("ui.button-group");
12
+ export const {
13
+ InjectionKey: InjectionKeyFormOptions,
14
+ inject: injectFormOptions,
15
+ provide: provideFormOptions
16
+ } = defineInjection("ui.form-options");
17
+ export const {
18
+ InjectionKey: InjectionKeyFormBus,
19
+ inject: injectFormBus,
20
+ provide: provideFormBus
21
+ } = defineInjection("ui.form-bus");
22
+ export const {
23
+ InjectionKey: InjectionKeyFormItem,
24
+ inject: injectFormItem,
25
+ provide: provideFormItem
26
+ } = defineInjection("ui.form-item");
27
+ export const {
28
+ InjectionKey: InjectionKeyFormInputId,
29
+ inject: injectFormInputId,
30
+ provide: provideFormInputId
31
+ } = defineInjection("ui.form-input-id");
32
+ export const {
33
+ InjectionKey: InjectionKeyFormInputs,
34
+ inject: injectFormInputs,
35
+ provide: provideFormInputs
36
+ } = defineInjection("ui.form-inputs");
37
+ export const {
38
+ InjectionKey: InjectionKeyFormLoading,
39
+ inject: injectFormLoading,
40
+ provide: provideFormLoading
41
+ } = defineInjection("ui.form-loading");
42
+ export const {
43
+ InjectionKey: InjectionKeyFormErrors,
44
+ inject: injectFormErrors,
45
+ provide: provideFormErrors
46
+ } = defineInjection("ui.form-errors", null);
12
47
  export const {
13
48
  InjectionKey: InjectionKeyLocaleContext,
14
49
  inject: injectLocaleContext,
@@ -1,25 +1,10 @@
1
1
  <script lang="ts">
2
2
  import type { AccordionRootEmits, AccordionRootProps } from 'reka-ui'
3
3
  import type { accordion } from '../theme'
4
- import type { ComponentAttrs } from '../types'
4
+ import type { ComponentAttrs, DynamicSlots } from '../types'
5
5
 
6
6
  export interface AccordionEmits extends AccordionRootEmits {}
7
7
 
8
- type SlotProps<T> = (props: { item: T, index: number, open: boolean }) => any
9
-
10
- type DynamicSlots<T extends { slot?: string }, SlotProps, Slot = T['slot']> =
11
- Slot extends string
12
- ? Record<Slot | `${Slot}-body`, SlotProps>
13
- : Record<string, never>
14
-
15
- export type AccordionSlots<T extends { slot?: string }> = {
16
- default?: SlotProps<T>
17
- leading?: SlotProps<T>
18
- trailing?: SlotProps<T>
19
- content?: SlotProps<T>
20
- body?: SlotProps<T>
21
- } & DynamicSlots<T, SlotProps<T>>
22
-
23
8
  export interface AccordionItem {
24
9
  label?: string
25
10
  icon?: string
@@ -29,9 +14,20 @@ export interface AccordionItem {
29
14
  /** A unique value for the accordion item. Defaults to the index. */
30
15
  value?: string
31
16
  disabled?: boolean
17
+ [key: string]: any
32
18
  }
33
19
 
34
- export interface AccordionProps<T> extends ComponentAttrs<typeof accordion>, Pick<AccordionRootProps, 'as' | 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
20
+ type SlotProps<T extends AccordionItem> = (props: { item: T, index: number, open: boolean }) => any
21
+
22
+ export type AccordionSlots<T extends AccordionItem = AccordionItem> = {
23
+ default?: SlotProps<T>
24
+ leading?: SlotProps<T>
25
+ trailing?: SlotProps<T>
26
+ content?: SlotProps<T>
27
+ body?: SlotProps<T>
28
+ } & DynamicSlots<T, 'body', SlotProps<T>>
29
+
30
+ export interface AccordionProps<T extends AccordionItem = AccordionItem> extends ComponentAttrs<typeof accordion>, Pick<AccordionRootProps, 'as' | 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
35
31
  items?: T[]
36
32
  /**
37
33
  * The icon displayed on the right side of the trigger.
@@ -91,12 +87,12 @@ const style = computed(() => generateStyle('accordion', props))
91
87
  </AccordionHeader>
92
88
 
93
89
  <AccordionContent
94
- v-if="item.content || slots.content || (item.slot && slots[item.slot]) || slots.body || (item.slot && slots[`${item.slot}-body`])"
90
+ v-if="item.content || slots.content || (item.slot && slots[item.slot as keyof AccordionSlots<T>]) || slots.body || (item.slot && slots[`${item.slot}-body` as keyof AccordionSlots<T>])"
95
91
  :class="style.content({ class: props.ui?.content })"
96
92
  >
97
- <slot :name="item.slot || 'content'" v-bind="{ item, index, open }">
93
+ <slot :name="((item.slot || 'content') as keyof AccordionSlots<T>)" v-bind="{ item, index, open }">
98
94
  <div :class="style.body({ class: props.ui?.body })">
99
- <slot :name="item.slot ? `${item.slot}-body` : 'body'" v-bind="{ item, index, open }">
95
+ <slot :name="((item.slot ? `${item.slot}-body` : 'body') as keyof AccordionSlots<T>)" v-bind="{ item, index, open }">
100
96
  {{ item.content }}
101
97
  </slot>
102
98
  </div>
@@ -5,7 +5,7 @@ import type { alert } from '../theme'
5
5
  import type { ButtonProps, ComponentAttrs } from '../types'
6
6
 
7
7
  export interface AlertEmits {
8
- (event: 'update:open', value: boolean): void
8
+ 'update:open': [value: boolean]
9
9
  }
10
10
 
11
11
  export interface AlertSlots {
@@ -5,7 +5,7 @@ import type { badge } from '../theme'
5
5
  import type { ComponentAttrs } from '../types'
6
6
 
7
7
  export interface BadgeEmits {
8
- (e: 'update:show', payload: boolean): void
8
+ 'update:show': [payload: boolean]
9
9
  }
10
10
 
11
11
  export interface BadgeSlots {
@@ -1,30 +1,26 @@
1
1
  <script lang="ts">
2
2
  import type { PrimitiveProps } from 'reka-ui'
3
3
  import type { breadcrumb } from '../theme'
4
- import type { ComponentAttrs, LinkProps } from '../types'
4
+ import type { ComponentAttrs, DynamicSlots, LinkProps } from '../types'
5
5
 
6
- type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
6
+ export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
7
+ label?: string
8
+ icon?: string
9
+ slot?: string
10
+ [key: string]: any
11
+ }
7
12
 
8
- type DynamicSlots<T extends { slot?: string }, SlotProps, Slot = T['slot']> =
9
- Slot extends string
10
- ? Record<Slot | `${Slot}-${'leading' | 'label' | 'trailing'}`, SlotProps>
11
- : Record<string, never>
13
+ type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => any
12
14
 
13
- export type BreadcrumbSlots<T extends { slot?: string }> = {
15
+ export type BreadcrumbSlots<T extends BreadcrumbItem = BreadcrumbItem> = {
14
16
  'item'?: SlotProps<T>
15
17
  'item-leading'?: SlotProps<T>
16
18
  'item-label'?: SlotProps<T>
17
19
  'item-trailing'?: SlotProps<T>
18
- 'separator'?: (props?: {}) => any
19
- } & DynamicSlots<T, SlotProps<T>>
20
-
21
- export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
22
- label?: string
23
- icon?: string
24
- slot?: string
25
- }
20
+ 'separator'?: any
21
+ } & DynamicSlots<T, 'leading' | 'label' | 'trailing', SlotProps<T>>
26
22
 
27
- export interface BreadcrumbProps<T> extends ComponentAttrs<typeof breadcrumb> {
23
+ export interface BreadcrumbProps<T extends BreadcrumbItem = BreadcrumbItem> extends ComponentAttrs<typeof breadcrumb> {
28
24
  /**
29
25
  * The element or component this component should render as.
30
26
  * @default "nav"
@@ -80,18 +76,18 @@ const style = computed(() => generateStyle('breadcrumb', props))
80
76
  :aria-current="active && (index === items!.length - 1) ? 'page' : undefined"
81
77
  :class="style.link({ class: props.ui?.link, active: index === items!.length - 1, disabled: item.disabled, to: !!item.to })"
82
78
  >
83
- <slot :name="item.slot || 'item'" :item="item" :index="index">
84
- <slot :name="`${item.slot || 'item'}-leading`" :item="item" :active="index === items!.length - 1" :index="index">
79
+ <slot :name="((item.slot || 'item') as keyof BreadcrumbSlots<T>)" :item="item" :index="index">
80
+ <slot :name="(`${item.slot || 'item'}-leading` as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
85
81
  <span v-if="item.icon" :class="style.linkLeadingIcon({ class: [item.icon, props.ui?.linkLeadingIcon] })"></span>
86
82
  </slot>
87
83
 
88
- <span v-if="get(item, props.labelKey) || slots[`${item.slot || 'item'}-label`]" :class="style.linkLabel({ class: props.ui?.linkLabel })">
89
- <slot :name="`${item.slot || 'item'}-label`" :item="item" :active="index === items!.length - 1" :index="index">
84
+ <span v-if="get(item, props.labelKey) || slots[(`${item.slot || 'item'}-label` as keyof BreadcrumbSlots<T>)]" :class="style.linkLabel({ class: props.ui?.linkLabel })">
85
+ <slot :name="(`${item.slot || 'item'}-label` as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
90
86
  {{ get(item, props.labelKey) }}
91
87
  </slot>
92
88
  </span>
93
89
 
94
- <slot :name="`${item.slot || 'item'}-trailing`" :item="item" :active="index === items!.length - 1" :index="index"></slot>
90
+ <slot :name="(`${item.slot || 'item'}-trailing` as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index"></slot>
95
91
  </slot>
96
92
  </LinkBase>
97
93
  </Link>
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from '@byyuurin/ui-kit'
3
3
  import type { DateValue } from '@internationalized/date'
4
- import type { CalendarCellTriggerProps, CalendarRootEmits, CalendarRootProps, DateRange, RangeCalendarRootEmits } from 'reka-ui'
4
+ import type { CalendarCellTriggerProps, CalendarRootEmits, CalendarRootProps, DateRange, RangeCalendarRootEmits, RangeCalendarRootProps } from 'reka-ui'
5
5
  import type { calendar } from '../theme'
6
6
  import type { ComponentAttrs } from '../types'
7
7
 
@@ -17,11 +17,20 @@ export interface CalendarSlots {
17
17
 
18
18
  type CalendarVariants = VariantProps<typeof calendar>
19
19
 
20
- type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
20
+ type CalendarDefaultValue<R extends boolean = false, M extends boolean = false> = R extends true
21
21
  ? DateRange
22
22
  : M extends true ? DateValue[] : DateValue
23
23
 
24
- export interface CalendarProps<R extends boolean, M extends boolean> extends ComponentAttrs<typeof calendar>, Omit<CalendarRootProps, 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'> {
24
+ type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
25
+ ? (DateRange | null)
26
+ : M extends true
27
+ ? (DateValue[] | undefined)
28
+ : (DateValue | undefined)
29
+
30
+ type _CalendarRootProps = Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
31
+ type _RangeCalendarRootProps = Omit<RangeCalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
32
+
33
+ export interface CalendarProps<R extends boolean = false, M extends boolean = false> extends ComponentAttrs<typeof calendar>, _CalendarRootProps, _RangeCalendarRootProps {
25
34
  /**
26
35
  * The icon to use for the next year control.
27
36
  * @default app.icons.chevronDoubleRight
@@ -54,12 +63,12 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Com
54
63
  monthControls?: boolean
55
64
  /** Show year controls */
56
65
  yearControls?: boolean
57
- defaultValue?: CalendarModelValue<R, M>
58
- modelValue?: CalendarModelValue<R, M>
66
+ defaultValue?: CalendarDefaultValue<R, M>
67
+ modelValue?: CalendarDefaultValue<R, M>
59
68
  }
60
69
  </script>
61
70
 
62
- <script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
71
+ <script setup lang="ts" generic="R extends boolean, M extends boolean">
63
72
  import { reactiveOmit } from '@vueuse/core'
64
73
  import { useForwardPropsEmits } from 'reka-ui'
65
74
  import { Calendar as BaseCalendar, RangeCalendar } from 'reka-ui/namespaced'
@@ -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"
@@ -88,7 +90,7 @@ export interface CarouselProps<T> extends ComponentAttrs<typeof carousel>, Omit<
88
90
  }
89
91
  </script>
90
92
 
91
- <script setup lang="ts" generic="T extends AcceptableValue">
93
+ <script setup lang="ts" generic="T extends CarouselItem">
92
94
  import { computedAsync, reactivePick } from '@vueuse/core'
93
95
  import useEmblaCarousel from 'embla-carousel-vue'
94
96
  import { Primitive, useForwardProps } from 'reka-ui'
@@ -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,15 +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
60
  const { theme, generateStyle } = useTheme()
59
- const style = computed(() => generateStyle('checkbox', props))
61
+ const style = computed(() => generateStyle('checkbox', {
62
+ ...props,
63
+ size: size.value,
64
+ disabled: disabled.value,
65
+ }))
60
66
 
61
67
  function onUpdate(value: any) {
62
68
  // @ts-expect-error - 'target' does not exist in type 'EventInit'
63
69
  const event = new Event('change', { target: value })
64
70
  emit('change', event)
71
+ emitFormChange()
72
+ emitFormInput()
65
73
  }
66
74
  </script>
67
75
 
@@ -69,12 +77,9 @@ function onUpdate(value: any) {
69
77
  <Primitive :as="props.as" :class="style.root({ class: [props.class, props.ui?.root] })">
70
78
  <div :class="style.container({ class: props.ui?.container })">
71
79
  <CheckboxRoot
72
- :id="id"
73
- v-bind="rootProps"
74
80
  v-slot="{ modelValue }"
81
+ v-bind="{ ...rootProps, ...ariaAttrs, id, name, disabled }"
75
82
  v-model="innerValue"
76
- :name="props.name"
77
- :disabled="props.disabled"
78
83
  :class="style.base({ class: props.ui?.base })"
79
84
  @update:model-value="onUpdate"
80
85
  >
@@ -1,22 +1,22 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from '@byyuurin/ui-kit'
3
- import type { DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-ui'
3
+ import type { DialogContentEmits, DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-ui'
4
4
  import type { drawer } from '../theme'
5
- import type { ButtonProps, ComponentAttrs } from '../types'
5
+ import type { ButtonProps, ComponentAttrs, EmitsToProps } from '../types'
6
6
 
7
7
  export interface DrawerEmits extends DialogRootEmits {
8
8
  'after-leave': []
9
9
  }
10
10
 
11
11
  export interface DrawerSlots {
12
- default?: (props?: {}) => any
13
- content?: (props?: {}) => any
14
- header?: (props?: {}) => any
15
- title?: (props?: {}) => any
16
- description?: (props?: {}) => any
17
- close?: (props?: {}) => any
18
- body?: (props?: {}) => any
19
- 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
20
20
  }
21
21
 
22
22
  type DrawerVariants = VariantProps<typeof drawer>
@@ -25,7 +25,7 @@ export interface DrawerProps extends ComponentAttrs<typeof drawer>, DialogRootPr
25
25
  title?: string
26
26
  description?: string
27
27
  /** The content of the drawer. */
28
- content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
28
+ content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
29
29
  /**
30
30
  * Render an overlay behind the drawer.
31
31
  * @default true
@@ -140,7 +140,7 @@ const style = computed(() => generateStyle('drawer', props))
140
140
  </DialogTitle>
141
141
 
142
142
  <DialogClose v-if="props.close || slots.close" as-child>
143
- <slot name="close">
143
+ <slot name="close" :ui="props.ui">
144
144
  <Button
145
145
  variant="ghost"
146
146
  :icon="props.closeIcon || theme.app.icons.close"
@@ -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>