@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.
- package/LICENSE.md +21 -0
- package/README.md +32 -0
- package/dist/components/Accordion.vue +104 -0
- package/dist/components/App.vue +57 -0
- package/dist/components/Button.vue +94 -0
- package/dist/components/Card.vue +76 -0
- package/dist/components/Checkbox.vue +104 -0
- package/dist/components/Drawer.vue +133 -0
- package/dist/components/Input.vue +169 -0
- package/dist/components/Link.vue +117 -0
- package/dist/components/Modal.vue +145 -0
- package/dist/components/ModalProvider.vue +10 -0
- package/dist/components/Popover.vue +97 -0
- package/dist/components/RadioGroup.vue +180 -0
- package/dist/components/Select.vue +258 -0
- package/dist/components/Switch.vue +99 -0
- package/dist/components/Tabs.vue +117 -0
- package/dist/components/Toast.vue +126 -0
- package/dist/components/Toaster.vue +143 -0
- package/dist/components/Tooltip.vue +71 -0
- package/dist/components/index.d.ts +18 -0
- package/dist/components/index.mjs +18 -0
- package/dist/composables/index.d.ts +4 -0
- package/dist/composables/index.mjs +4 -0
- package/dist/composables/useComponentIcons.d.ts +26 -0
- package/dist/composables/useComponentIcons.mjs +24 -0
- package/dist/composables/useModal.d.ts +15 -0
- package/dist/composables/useModal.mjs +51 -0
- package/dist/composables/useTheme.d.ts +8 -0
- package/dist/composables/useTheme.mjs +18 -0
- package/dist/composables/useToast.d.ts +24 -0
- package/dist/composables/useToast.mjs +48 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/internal/constants.d.ts +3 -0
- package/dist/internal/constants.mjs +21 -0
- package/dist/internal/extend-theme.d.ts +9 -0
- package/dist/internal/extend-theme.mjs +16 -0
- package/dist/internal/extend-theme.test.d.ts +1 -0
- package/dist/internal/extend-theme.test.mjs +45 -0
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.mjs +4 -0
- package/dist/internal/link.d.ts +15 -0
- package/dist/internal/link.mjs +4 -0
- package/dist/internal/styler.d.ts +5 -0
- package/dist/internal/styler.mjs +236 -0
- package/dist/internal/styler.test.d.ts +1 -0
- package/dist/internal/styler.test.mjs +10 -0
- package/dist/nuxt.d.ts +10 -0
- package/dist/nuxt.mjs +28 -0
- package/dist/resolver.d.ts +10 -0
- package/dist/resolver.mjs +18 -0
- package/dist/theme/accordion.d.ts +39 -0
- package/dist/theme/accordion.mjs +25 -0
- package/dist/theme/app.d.ts +10 -0
- package/dist/theme/app.mjs +9 -0
- package/dist/theme/button.d.ts +184 -0
- package/dist/theme/button.mjs +140 -0
- package/dist/theme/card.d.ts +43 -0
- package/dist/theme/card.mjs +11 -0
- package/dist/theme/checkbox.d.ts +97 -0
- package/dist/theme/checkbox.mjs +53 -0
- package/dist/theme/drawer.d.ts +72 -0
- package/dist/theme/drawer.mjs +72 -0
- package/dist/theme/index.d.ts +17 -0
- package/dist/theme/index.mjs +17 -0
- package/dist/theme/input.d.ts +159 -0
- package/dist/theme/input.mjs +133 -0
- package/dist/theme/link.d.ts +31 -0
- package/dist/theme/link.mjs +23 -0
- package/dist/theme/modal.d.ts +50 -0
- package/dist/theme/modal.mjs +54 -0
- package/dist/theme/popover.d.ts +27 -0
- package/dist/theme/popover.mjs +10 -0
- package/dist/theme/radioGroup.d.ts +131 -0
- package/dist/theme/radioGroup.mjs +67 -0
- package/dist/theme/select.d.ts +177 -0
- package/dist/theme/select.mjs +154 -0
- package/dist/theme/switch.d.ts +131 -0
- package/dist/theme/switch.mjs +78 -0
- package/dist/theme/tabs.d.ts +101 -0
- package/dist/theme/tabs.mjs +117 -0
- package/dist/theme/toast.d.ts +51 -0
- package/dist/theme/toast.mjs +27 -0
- package/dist/theme/toaster.d.ts +73 -0
- package/dist/theme/toaster.mjs +89 -0
- package/dist/theme/tooltip.d.ts +31 -0
- package/dist/theme/tooltip.mjs +8 -0
- package/dist/types/components.d.ts +18 -0
- package/dist/types/components.mjs +0 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.mjs +2 -0
- package/dist/types/utils.d.ts +29 -0
- package/dist/types/utils.mjs +0 -0
- package/dist/unocss-preset.d.ts +37 -0
- package/dist/unocss-preset.mjs +164 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.mjs +70 -0
- package/dist/utils/unocss.d.ts +3 -0
- package/dist/utils/unocss.mjs +50 -0
- 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>
|