@byyuurin/ui 0.0.0

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 (101) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +32 -0
  3. package/dist/components/Accordion.vue +104 -0
  4. package/dist/components/App.vue +57 -0
  5. package/dist/components/Button.vue +94 -0
  6. package/dist/components/Card.vue +76 -0
  7. package/dist/components/Checkbox.vue +104 -0
  8. package/dist/components/Drawer.vue +133 -0
  9. package/dist/components/Input.vue +169 -0
  10. package/dist/components/Link.vue +117 -0
  11. package/dist/components/Modal.vue +145 -0
  12. package/dist/components/ModalProvider.vue +10 -0
  13. package/dist/components/Popover.vue +97 -0
  14. package/dist/components/RadioGroup.vue +180 -0
  15. package/dist/components/Select.vue +258 -0
  16. package/dist/components/Switch.vue +99 -0
  17. package/dist/components/Tabs.vue +117 -0
  18. package/dist/components/Toast.vue +126 -0
  19. package/dist/components/Toaster.vue +143 -0
  20. package/dist/components/Tooltip.vue +71 -0
  21. package/dist/components/index.d.ts +18 -0
  22. package/dist/components/index.mjs +18 -0
  23. package/dist/composables/index.d.ts +4 -0
  24. package/dist/composables/index.mjs +4 -0
  25. package/dist/composables/useComponentIcons.d.ts +26 -0
  26. package/dist/composables/useComponentIcons.mjs +24 -0
  27. package/dist/composables/useModal.d.ts +15 -0
  28. package/dist/composables/useModal.mjs +51 -0
  29. package/dist/composables/useTheme.d.ts +8 -0
  30. package/dist/composables/useTheme.mjs +18 -0
  31. package/dist/composables/useToast.d.ts +24 -0
  32. package/dist/composables/useToast.mjs +48 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.mjs +3 -0
  35. package/dist/internal/constants.d.ts +3 -0
  36. package/dist/internal/constants.mjs +21 -0
  37. package/dist/internal/extend-theme.d.ts +9 -0
  38. package/dist/internal/extend-theme.mjs +16 -0
  39. package/dist/internal/extend-theme.test.d.ts +1 -0
  40. package/dist/internal/extend-theme.test.mjs +45 -0
  41. package/dist/internal/index.d.ts +4 -0
  42. package/dist/internal/index.mjs +4 -0
  43. package/dist/internal/link.d.ts +15 -0
  44. package/dist/internal/link.mjs +4 -0
  45. package/dist/internal/styler.d.ts +5 -0
  46. package/dist/internal/styler.mjs +236 -0
  47. package/dist/internal/styler.test.d.ts +1 -0
  48. package/dist/internal/styler.test.mjs +10 -0
  49. package/dist/nuxt.d.ts +10 -0
  50. package/dist/nuxt.mjs +28 -0
  51. package/dist/resolver.d.ts +10 -0
  52. package/dist/resolver.mjs +18 -0
  53. package/dist/theme/accordion.d.ts +39 -0
  54. package/dist/theme/accordion.mjs +25 -0
  55. package/dist/theme/app.d.ts +10 -0
  56. package/dist/theme/app.mjs +9 -0
  57. package/dist/theme/button.d.ts +184 -0
  58. package/dist/theme/button.mjs +140 -0
  59. package/dist/theme/card.d.ts +43 -0
  60. package/dist/theme/card.mjs +11 -0
  61. package/dist/theme/checkbox.d.ts +97 -0
  62. package/dist/theme/checkbox.mjs +53 -0
  63. package/dist/theme/drawer.d.ts +72 -0
  64. package/dist/theme/drawer.mjs +72 -0
  65. package/dist/theme/index.d.ts +17 -0
  66. package/dist/theme/index.mjs +17 -0
  67. package/dist/theme/input.d.ts +159 -0
  68. package/dist/theme/input.mjs +133 -0
  69. package/dist/theme/link.d.ts +31 -0
  70. package/dist/theme/link.mjs +23 -0
  71. package/dist/theme/modal.d.ts +50 -0
  72. package/dist/theme/modal.mjs +54 -0
  73. package/dist/theme/popover.d.ts +27 -0
  74. package/dist/theme/popover.mjs +10 -0
  75. package/dist/theme/radioGroup.d.ts +131 -0
  76. package/dist/theme/radioGroup.mjs +67 -0
  77. package/dist/theme/select.d.ts +177 -0
  78. package/dist/theme/select.mjs +154 -0
  79. package/dist/theme/switch.d.ts +131 -0
  80. package/dist/theme/switch.mjs +78 -0
  81. package/dist/theme/tabs.d.ts +101 -0
  82. package/dist/theme/tabs.mjs +117 -0
  83. package/dist/theme/toast.d.ts +51 -0
  84. package/dist/theme/toast.mjs +27 -0
  85. package/dist/theme/toaster.d.ts +73 -0
  86. package/dist/theme/toaster.mjs +89 -0
  87. package/dist/theme/tooltip.d.ts +31 -0
  88. package/dist/theme/tooltip.mjs +8 -0
  89. package/dist/types/components.d.ts +18 -0
  90. package/dist/types/components.mjs +0 -0
  91. package/dist/types/index.d.ts +7 -0
  92. package/dist/types/index.mjs +2 -0
  93. package/dist/types/utils.d.ts +29 -0
  94. package/dist/types/utils.mjs +0 -0
  95. package/dist/unocss-preset.d.ts +37 -0
  96. package/dist/unocss-preset.mjs +164 -0
  97. package/dist/utils/index.d.ts +18 -0
  98. package/dist/utils/index.mjs +70 -0
  99. package/dist/utils/unocss.d.ts +3 -0
  100. package/dist/utils/unocss.mjs +50 -0
  101. package/package.json +103 -0
@@ -0,0 +1,169 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from '@byyuurin/ui-kit'
3
+ import type { PrimitiveProps } from 'reka-ui'
4
+ import type { InputHTMLAttributes } from 'vue'
5
+ import type { UseComponentIconsProps } from '../composables/useComponentIcons'
6
+ import type { input } from '../theme'
7
+ import type { ComponentAttrs } from '../types'
8
+
9
+ type InputVariants = VariantProps<typeof input>
10
+
11
+ export interface InputProps extends ComponentAttrs<typeof input>, UseComponentIconsProps {
12
+ /**
13
+ * The element or component this component should render as.
14
+ * @defaultValue 'div'
15
+ */
16
+ as?: PrimitiveProps['as']
17
+ id?: string
18
+ name?: string
19
+ type?: InputHTMLAttributes['type']
20
+ placeholder?: string
21
+ size?: InputVariants['size']
22
+ variant?: InputVariants['variant']
23
+ loading?: boolean
24
+ highlight?: boolean
25
+ underline?: boolean
26
+ required?: boolean
27
+ autocomplete?: InputHTMLAttributes['autocomplete']
28
+ autofocus?: boolean
29
+ autofocusDelay?: number
30
+ disabled?: boolean
31
+ }
32
+
33
+ export interface InputEmits {
34
+ (e: 'update:modelValue', payload: string | number): void
35
+ (e: 'blur', event: FocusEvent): void
36
+ (e: 'change', event: Event): void
37
+ }
38
+
39
+ export interface InputSlots {
40
+ prefix?: (props?: {}) => any
41
+ default?: (props?: {}) => any
42
+ suffix?: (props?: {}) => any
43
+ }
44
+ </script>
45
+
46
+ <script setup lang="ts">
47
+ import { Primitive } from 'reka-ui'
48
+ import { computed, onMounted, ref } from 'vue'
49
+ import { useComponentIcons, useTheme } from '../composables'
50
+ import { looseToNumber } from '../utils'
51
+
52
+ defineOptions({
53
+ inheritAttrs: false,
54
+ })
55
+
56
+ const props = withDefaults(defineProps<InputProps>(), {
57
+ type: 'text',
58
+ size: 'md',
59
+ variant: 'outline',
60
+ autocomplete: 'off',
61
+ autofocusDelay: 0,
62
+ })
63
+
64
+ const emit = defineEmits<InputEmits>()
65
+ const slots = defineSlots<InputSlots>()
66
+ const [modelValue, modelModifiers] = defineModel<string | number>()
67
+
68
+ const inputRef = ref<HTMLInputElement | null>(null)
69
+
70
+ const { isPrefix, prefixIconName, isSuffix, suffixIconName } = useComponentIcons(props)
71
+
72
+ const { theme, createStyler } = useTheme()
73
+ const style = computed(() => {
74
+ const styler = createStyler(theme.value.input)
75
+ // @ts-expect-error ignore type
76
+ return styler({
77
+ ...props,
78
+ prefix: isPrefix.value || !!slots.prefix,
79
+ suffix: isSuffix.value || !!slots.suffix,
80
+ })
81
+ })
82
+
83
+ function autoFocus() {
84
+ if (props.autofocus)
85
+ inputRef.value?.focus()
86
+ }
87
+
88
+ function updateInput(value: string) {
89
+ if (modelModifiers.trim)
90
+ value = value.trim()
91
+
92
+ if (modelModifiers.number || props.type === 'number')
93
+ value = looseToNumber(value)
94
+
95
+ modelValue.value = value
96
+ }
97
+
98
+ function onInput(event: Event) {
99
+ if (!modelModifiers.lazy)
100
+ updateInput((event.target as HTMLInputElement).value)
101
+ }
102
+
103
+ function onChange(event: Event) {
104
+ const value = (event.target as HTMLInputElement).value
105
+
106
+ if (modelModifiers.lazy)
107
+ updateInput(value)
108
+
109
+ if (modelModifiers.trim)
110
+ (event.target as HTMLInputElement).value = value.trim()
111
+
112
+ emit('change', event)
113
+ }
114
+
115
+ function onBlur(event: FocusEvent) {
116
+ emit('blur', event)
117
+ }
118
+
119
+ defineExpose({
120
+ inputRef,
121
+ })
122
+
123
+ onMounted(() => {
124
+ setTimeout(() => {
125
+ autoFocus()
126
+ }, props.autofocusDelay)
127
+ })
128
+ </script>
129
+
130
+ <template>
131
+ <Primitive :as="as" :class="style.root({ class: [props.class, props.ui?.root] })" :aria-disabled="props.disabled ? true : undefined">
132
+ <span v-if="isPrefix || slots.prefix" :class="style.prefix({ class: props.ui?.prefix })">
133
+ <slot name="prefix">
134
+ <i
135
+ v-if="isPrefix && prefixIconName"
136
+ :class="style.prefixIcon({ class: [prefixIconName, props.ui?.prefixIcon] })"
137
+ ></i>
138
+ </slot>
139
+ </span>
140
+
141
+ <input
142
+ :id="id"
143
+ ref="inputRef"
144
+ :type="props.type"
145
+ :value="modelValue"
146
+ :name="props.name"
147
+ :placeholder="props.placeholder"
148
+ :class="style.base({ class: props.ui?.base })"
149
+ :disabled="props.disabled"
150
+ :required="props.required"
151
+ :autocomplete="props.autocomplete"
152
+ v-bind="$attrs"
153
+ @input="onInput"
154
+ @blur="onBlur"
155
+ @change="onChange"
156
+ />
157
+
158
+ <slot></slot>
159
+
160
+ <span v-if="isSuffix || slots.suffix" :class="style.suffix({ class: props.ui?.suffix })">
161
+ <slot name="suffix">
162
+ <i
163
+ v-if="isSuffix && suffixIconName"
164
+ :class="style.suffixIcon({ class: [suffixIconName, props.ui?.suffixIcon] })"
165
+ ></i>
166
+ </slot>
167
+ </span>
168
+ </Primitive>
169
+ </template>
@@ -0,0 +1,117 @@
1
+ <script lang="ts">
2
+ import type { PrimitiveProps } from 'reka-ui'
3
+ import type { link } from '../theme'
4
+ import type { ComponentAttrs, HintString, MaybeArray } from '../types'
5
+
6
+ export interface LinkProps extends Omit<ComponentAttrs<typeof link>, 'ui'> {
7
+ as?: PrimitiveProps['as']
8
+ type?: string
9
+ onClick?: MaybeArray<(e: MouseEvent) => void | Promise<void>>
10
+ label?: string
11
+ href?: string
12
+ navigate?: (e: MouseEvent) => void
13
+ /** A rel attribute value to apply on the link. */
14
+ rel?: HintString<'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc'> | null
15
+ noRel?: boolean
16
+ /** Where to display the linked URL, as the name for a browsing context. */
17
+ target?: HintString<'_blank' | '_parent' | '_self' | '_top'> | null
18
+ isExternal?: boolean
19
+ underline?: boolean
20
+ active?: boolean
21
+ disabled?: boolean
22
+ /** When `true`, only styles from `class`, `ui.active`, and `ui.inactive` will be applied. */
23
+ raw?: boolean
24
+ ui?: {
25
+ active?: string
26
+ inactive?: string
27
+ disabled?: string
28
+ }
29
+ }
30
+ </script>
31
+
32
+ <script setup lang="ts">
33
+ import { Primitive } from 'reka-ui'
34
+ import { computed } from 'vue'
35
+ import { useTheme } from '../composables'
36
+
37
+ const props = withDefaults(defineProps<LinkProps>(), {
38
+ as: 'button',
39
+ type: 'button',
40
+ })
41
+
42
+ const linkProps = computed(() => {
43
+ const { as, type, disabled, href, rel, target } = props
44
+ const base = { as, rel, target }
45
+
46
+ if (href) {
47
+ return {
48
+ ...base,
49
+ 'as': 'a',
50
+ 'href': disabled ? undefined : href,
51
+ 'aria-disabled': disabled ? 'true' : undefined,
52
+ 'role': disabled ? 'link' : undefined,
53
+ 'tabindex': disabled ? -1 : undefined,
54
+ }
55
+ }
56
+
57
+ if (as === 'button')
58
+ return { ...base, type, disabled }
59
+
60
+ return base
61
+ })
62
+
63
+ const { theme, createStyler } = useTheme()
64
+
65
+ const style = computed(() => {
66
+ if (props.raw)
67
+ return props.class
68
+
69
+ const { link } = theme.value
70
+
71
+ const styler = createStyler({
72
+ ...link,
73
+ variants: {
74
+ ...link.variants,
75
+ active: {
76
+ true: [link.variants.active.true, props.ui?.active],
77
+ false: [link.variants.active.false, props.ui?.inactive],
78
+ },
79
+ disabled: {
80
+ true: [link.variants.disabled.true, props.ui?.disabled],
81
+ },
82
+ },
83
+ })
84
+ return styler(props)
85
+ })
86
+
87
+ function handleClick(e: MouseEvent) {
88
+ if (props.disabled) {
89
+ e.stopPropagation()
90
+ e.preventDefault()
91
+ return
92
+ }
93
+
94
+ if (props.onClick) {
95
+ const handlers = Array.isArray(props.onClick) ? props.onClick : [props.onClick]
96
+ for (const handler of handlers)
97
+ handler(e)
98
+ }
99
+
100
+ if (props.href && props.navigate && !props.isExternal)
101
+ props.navigate(e)
102
+ }
103
+ </script>
104
+
105
+ <template>
106
+ <Primitive
107
+ v-bind="linkProps"
108
+ :rel="props.rel"
109
+ :target="props.target"
110
+ :class="style"
111
+ @click="handleClick"
112
+ >
113
+ <slot :active="props.active">
114
+ {{ props.label }}
115
+ </slot>
116
+ </Primitive>
117
+ </template>
@@ -0,0 +1,145 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from '@byyuurin/ui-kit'
3
+ import type { DialogContentProps, DialogRootEmits, DialogRootProps } from 'reka-ui'
4
+ import type { modal } from '../theme'
5
+ import type { ButtonProps, ComponentAttrs } from '../types'
6
+
7
+ export interface ModalEmits extends DialogRootEmits {}
8
+
9
+ export interface ModalSlots {
10
+ default?: (props: { open: boolean }) => any
11
+ content?: (props?: any) => any
12
+ header?: (props?: any) => any
13
+ title?: (props?: any) => any
14
+ description?: (props?: any) => any
15
+ close?: (props?: any) => any
16
+ body?: (props?: any) => any
17
+ footer?: (props?: any) => any
18
+ }
19
+
20
+ type ModalVariants = VariantProps<typeof modal>
21
+
22
+ export interface ModalProps extends ComponentAttrs<typeof modal>, DialogRootProps {
23
+ title?: string
24
+ description?: string
25
+ size?: ModalVariants['size']
26
+ content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
27
+ /** @default true */
28
+ portal?: boolean
29
+ /** @default true */
30
+ overlay?: boolean
31
+ blur?: boolean
32
+ /** @default true */
33
+ transition?: boolean
34
+ /**
35
+ * When `false`, the modal will not close when clicking outside or pressing escape.
36
+ * @default true
37
+ */
38
+ dismissible?: boolean
39
+ close?: ButtonProps | boolean
40
+ /** @default `app.icons.close` */
41
+ closeIcon?: string
42
+ }
43
+ </script>
44
+
45
+ <script setup lang="ts">
46
+ import { reactivePick } from '@vueuse/core'
47
+ import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, useForwardPropsEmits } from 'reka-ui'
48
+ import { computed, toRef } from 'vue'
49
+ import { useTheme } from '../composables'
50
+ import UButton from './Button.vue'
51
+
52
+ const props = withDefaults(defineProps<ModalProps>(), {
53
+ modal: true,
54
+ size: 'md',
55
+ portal: true,
56
+ overlay: true,
57
+ transition: true,
58
+ dismissible: true,
59
+ close: true,
60
+ })
61
+ const emit = defineEmits<ModalEmits>()
62
+ const slots = defineSlots<ModalSlots>()
63
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emit)
64
+ const contentProps = toRef(() => props.content)
65
+ const contentEvents = computed(() => {
66
+ if (props.dismissible)
67
+ return {}
68
+
69
+ return {
70
+ pointerDownOutside: (e: Event) => e.preventDefault(),
71
+ interactOutside: (e: Event) => e.preventDefault(),
72
+ escapeKeyDown: (e: Event) => e.preventDefault(),
73
+ }
74
+ })
75
+
76
+ const { theme, createStyler } = useTheme()
77
+ const style = computed(() => {
78
+ const styler = createStyler(theme.value.modal)
79
+ return styler(props)
80
+ })
81
+ </script>
82
+
83
+ <template>
84
+ <DialogRoot v-slot="{ open }" v-bind="rootProps">
85
+ <DialogTrigger
86
+ v-if="slots.default"
87
+ :class="props.class"
88
+ as-child
89
+ >
90
+ <slot :open="open"></slot>
91
+ </DialogTrigger>
92
+ <DialogPortal :disabled="!props.portal">
93
+ <DialogOverlay v-if="props.overlay" :class="style.overlay({ class: props.ui?.overlay })" />
94
+
95
+ <DialogContent :class="style.content({ class: props.ui?.content })" v-bind="contentProps" v-on="contentEvents">
96
+ <slot name="content">
97
+ <div
98
+ v-if="slots.header || (props.title || slots.title) || (props.description || slots.description) || (props.close || slots.close)"
99
+ :class="style.header({ class: props.ui?.header })"
100
+ >
101
+ <slot name="header">
102
+ <DialogTitle
103
+ v-if="props.title || slots.title"
104
+ :class="style.title({ class: props.ui?.title })"
105
+ >
106
+ <slot name="title">
107
+ {{ props.title }}
108
+ </slot>
109
+ </DialogTitle>
110
+
111
+ <DialogClose as-child>
112
+ <slot name="close">
113
+ <UButton
114
+ v-if="props.close"
115
+ variant="ghost"
116
+ :icon="props.closeIcon || theme.app.icons.close"
117
+ v-bind="typeof props.close === 'boolean' ? {} : props.close"
118
+ :class="style.close({ class: props.ui?.close })"
119
+ />
120
+ </slot>
121
+ </DialogClose>
122
+
123
+ <DialogDescription
124
+ v-if="props.description || slots.description"
125
+ :class="style.description({ class: props.ui?.description })"
126
+ >
127
+ <slot name="description">
128
+ {{ props.description }}
129
+ </slot>
130
+ </DialogDescription>
131
+ </slot>
132
+ </div>
133
+
134
+ <div v-if="slots.body" :class="style.body({ class: props.ui?.body })">
135
+ <slot name="body"></slot>
136
+ </div>
137
+
138
+ <div v-if="slots.footer" :class="style.footer({ class: props.ui?.footer })">
139
+ <slot name="footer"></slot>
140
+ </div>
141
+ </slot>
142
+ </DialogContent>
143
+ </DialogPortal>
144
+ </DialogRoot>
145
+ </template>
@@ -0,0 +1,10 @@
1
+ <script setup lang="ts">
2
+ import { injectModalState, useModal } from '../composables/useModal'
3
+
4
+ const modalState = injectModalState()
5
+ const { isOpen } = useModal()
6
+ </script>
7
+
8
+ <template>
9
+ <component :is="modalState.component" v-if="modalState?.props.open" v-bind="modalState.props" v-model:open="isOpen" />
10
+ </template>
@@ -0,0 +1,97 @@
1
+ <script lang="ts">
2
+ import type { HoverCardRootProps, PopoverArrowProps, PopoverContentProps, PopoverRootEmits, PopoverRootProps } from 'reka-ui'
3
+ import type { popover } from '../theme'
4
+ import type { ComponentAttrs } from '../types'
5
+
6
+ export interface PopoverProps extends ComponentAttrs<typeof popover>, PopoverRootProps, Pick<HoverCardRootProps, 'openDelay' | 'closeDelay'> {
7
+ /**
8
+ * The display mode of the popover.
9
+ * @default "click"
10
+ */
11
+ mode?: 'click' | 'hover'
12
+ /** @default { side: 'bottom', sideOffset: 8, collisionPadding: 8 } */
13
+ content?: Omit<PopoverContentProps, 'as' | 'asChild' | 'forceMount'>
14
+ arrow?: boolean | Omit<PopoverArrowProps, 'as' | 'asChild'>
15
+ /** @default true */
16
+ portal?: boolean
17
+ /**
18
+ * When `false`, the popover will not close when clicking outside or pressing escape.
19
+ * @default true
20
+ */
21
+ dismissible?: boolean
22
+ }
23
+
24
+ export interface PopoverEmits extends PopoverRootEmits {}
25
+
26
+ export interface PopoverSlots {
27
+ default?: (props: { open: boolean }) => any
28
+ content?: (props?: {}) => any
29
+ }
30
+ </script>
31
+
32
+ <script setup lang="ts">
33
+ import { reactivePick } from '@vueuse/core'
34
+ import { defu } from 'defu'
35
+ import { useForwardPropsEmits } from 'reka-ui'
36
+ import { HoverCard, Popover } from 'reka-ui/namespaced'
37
+ import { computed, toRef } from 'vue'
38
+ import { useTheme } from '../composables'
39
+
40
+ const props = withDefaults(defineProps<PopoverProps>(), {
41
+ mode: 'click',
42
+ portal: true,
43
+ openDelay: 0,
44
+ closeDelay: 0,
45
+ dismissible: true,
46
+ })
47
+ const emit = defineEmits<PopoverEmits>()
48
+ const slots = defineSlots<PopoverSlots>()
49
+
50
+ const pick = props.mode === 'hover'
51
+ ? reactivePick(props, 'defaultOpen', 'open', 'openDelay', 'closeDelay')
52
+ : reactivePick(props, 'defaultOpen', 'open', 'modal')
53
+
54
+ const rootProps = useForwardPropsEmits(pick, emit)
55
+ const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as PopoverContentProps)
56
+
57
+ const contentEvents = computed(() => {
58
+ if (props.dismissible)
59
+ return {}
60
+
61
+ return {
62
+ pointerDownOutside: (e: Event) => e.preventDefault(),
63
+ interactOutside: (e: Event) => e.preventDefault(),
64
+ escapeKeyDown: (e: Event) => e.preventDefault(),
65
+ }
66
+ })
67
+
68
+ const arrowProps = toRef(() => props.arrow as PopoverArrowProps)
69
+
70
+ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
71
+
72
+ const { theme, createStyler } = useTheme()
73
+ const style = computed(() => {
74
+ const styler = createStyler(theme.value.popover)
75
+ return styler(props)
76
+ })
77
+ </script>
78
+
79
+ <template>
80
+ <Component.Root v-slot="{ open }" v-bind="rootProps">
81
+ <Component.Trigger v-if="!!slots.default" as-child :class="props.class">
82
+ <slot :open="open"></slot>
83
+ </Component.Trigger>
84
+
85
+ <Component.Portal :disabled="!portal">
86
+ <Component.Content
87
+ v-bind="contentProps"
88
+ :class="style.content({ class: [!slots.default && props.class, props.ui?.content] })"
89
+ v-on="contentEvents"
90
+ >
91
+ <slot name="content"></slot>
92
+
93
+ <Component.Arrow v-if="!!arrow" v-bind="arrowProps" :class="style.arrow({ class: props.ui?.arrow })" />
94
+ </Component.Content>
95
+ </Component.Portal>
96
+ </Component.Root>
97
+ </template>