mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-23 00:15:05 +01:00
feat(Alert/Toast)!: add orientation prop
This commit is contained in:
@@ -25,10 +25,11 @@ export interface AlertProps {
|
||||
avatar?: AvatarProps
|
||||
color?: AlertVariants['color']
|
||||
variant?: AlertVariants['variant']
|
||||
orientation?: AlertVariants['orientation']
|
||||
/**
|
||||
* Display a list of actions:
|
||||
* - under the title and description if multiline
|
||||
* - next to the close button if not multiline
|
||||
* - under the title and description when orientation is `vertical`
|
||||
* - next to the close button when orientation is `horizontal`
|
||||
* `{ size: 'xs' }`{lang="ts-type"}
|
||||
*/
|
||||
actions?: ButtonProps[]
|
||||
@@ -72,23 +73,25 @@ import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
const props = defineProps<AlertProps>()
|
||||
const props = withDefaults(defineProps<AlertProps>(), {
|
||||
orientation: 'vertical'
|
||||
})
|
||||
const emits = defineEmits<AlertEmits>()
|
||||
const slots = defineSlots<AlertSlots>()
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const multiline = computed(() => !!props.title && !!props.description)
|
||||
|
||||
const ui = computed(() => alert({
|
||||
color: props.color,
|
||||
variant: props.variant
|
||||
variant: props.variant,
|
||||
orientation: props.orientation,
|
||||
title: !!props.title || !!slots.title
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root], multiline })">
|
||||
<Primitive :as="as" :data-orientation="orientation" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<slot name="leading">
|
||||
<UAvatar v-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
|
||||
<UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
@@ -106,15 +109,15 @@ const ui = computed(() => alert({
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div v-if="multiline && actions?.length" :class="ui.actions({ class: props.ui?.actions, multiline: true })">
|
||||
<div v-if="orientation === 'vertical' && actions?.length" :class="ui.actions({ class: props.ui?.actions })">
|
||||
<slot name="actions">
|
||||
<UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="(!multiline && actions?.length) || close" :class="ui.actions({ class: props.ui?.actions, multiline: false })">
|
||||
<template v-if="!multiline">
|
||||
<div v-if="(orientation === 'horizontal' && actions?.length) || close" :class="ui.actions({ class: props.ui?.actions, orientation: 'horizontal' })">
|
||||
<template v-if="orientation === 'horizontal' && actions?.length">
|
||||
<slot name="actions">
|
||||
<UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
|
||||
</slot>
|
||||
|
||||
@@ -25,10 +25,11 @@ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open'
|
||||
icon?: string
|
||||
avatar?: AvatarProps
|
||||
color?: ToastVariants['color']
|
||||
orientation?: ToastVariants['orientation']
|
||||
/**
|
||||
* Display a list of actions:
|
||||
* - under the title and description if multiline
|
||||
* - next to the close button if not multiline
|
||||
* - under the title and description when orientation is `vertical`
|
||||
* - next to the close button when orientation is `horizontal`
|
||||
* `{ size: 'xs' }`{lang="ts-type"}
|
||||
*/
|
||||
actions?: ButtonProps[]
|
||||
@@ -71,7 +72,8 @@ import UAvatar from './Avatar.vue'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
const props = withDefaults(defineProps<ToastProps>(), {
|
||||
close: true
|
||||
close: true,
|
||||
orientation: 'vertical'
|
||||
})
|
||||
const emits = defineEmits<ToastEmits>()
|
||||
const slots = defineSlots<ToastSlots>()
|
||||
@@ -81,10 +83,10 @@ const appConfig = useAppConfig()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'duration', 'type'), emits)
|
||||
|
||||
const multiline = computed(() => !!props.title && !!props.description)
|
||||
|
||||
const ui = computed(() => toast({
|
||||
color: props.color
|
||||
color: props.color,
|
||||
orientation: props.orientation,
|
||||
title: !!props.title || !!slots.title
|
||||
}))
|
||||
|
||||
const el = ref()
|
||||
@@ -110,7 +112,8 @@ defineExpose({
|
||||
ref="el"
|
||||
v-slot="{ remaining, duration }"
|
||||
v-bind="rootProps"
|
||||
:class="ui.root({ class: [props.class, props.ui?.root], multiline })"
|
||||
:data-orientation="orientation"
|
||||
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||
:style="{ '--height': height }"
|
||||
>
|
||||
<slot name="leading">
|
||||
@@ -130,7 +133,7 @@ defineExpose({
|
||||
</slot>
|
||||
</ToastDescription>
|
||||
|
||||
<div v-if="multiline && actions?.length" :class="ui.actions({ class: props.ui?.actions, multiline: true })">
|
||||
<div v-if="orientation === 'vertical' && actions?.length" :class="ui.actions({ class: props.ui?.actions })">
|
||||
<slot name="actions">
|
||||
<ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
|
||||
<UButton size="xs" :color="color" v-bind="action" />
|
||||
@@ -139,8 +142,8 @@ defineExpose({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="(!multiline && actions?.length) || close !== null" :class="ui.actions({ class: props.ui?.actions, multiline: false })">
|
||||
<template v-if="!multiline">
|
||||
<div v-if="(orientation === 'horizontal' && actions?.length) || close" :class="ui.actions({ class: props.ui?.actions, orientation: 'horizontal' })">
|
||||
<template v-if="orientation === 'horizontal' && actions?.length">
|
||||
<slot name="actions">
|
||||
<ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
|
||||
<UButton size="xs" :color="color" v-bind="action" />
|
||||
|
||||
@@ -3,14 +3,14 @@ import type { ModuleOptions } from '../module'
|
||||
export default (options: Required<ModuleOptions>) => ({
|
||||
slots: {
|
||||
root: 'relative overflow-hidden w-full rounded-[calc(var(--ui-radius)*2)] p-4 flex gap-2.5',
|
||||
wrapper: 'min-w-0 flex-1 flex flex-col gap-1',
|
||||
wrapper: 'min-w-0 flex-1 flex flex-col',
|
||||
title: 'text-sm font-medium',
|
||||
description: 'text-sm opacity-90',
|
||||
icon: 'shrink-0 size-5',
|
||||
avatar: 'shrink-0',
|
||||
avatarSize: '2xl',
|
||||
actions: 'flex flex-wrap gap-1.5 shrink-0',
|
||||
close: 'p-0.5'
|
||||
close: 'p-0'
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
@@ -23,14 +23,19 @@ export default (options: Required<ModuleOptions>) => ({
|
||||
soft: '',
|
||||
subtle: ''
|
||||
},
|
||||
multiline: {
|
||||
true: {
|
||||
root: 'items-start',
|
||||
actions: 'items-start mt-1'
|
||||
},
|
||||
false: {
|
||||
orientation: {
|
||||
horizontal: {
|
||||
root: 'items-center',
|
||||
actions: 'items-center'
|
||||
},
|
||||
vertical: {
|
||||
root: 'items-start',
|
||||
actions: 'items-start mt-2.5'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
true: {
|
||||
description: 'mt-1'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ModuleOptions } from '../module'
|
||||
export default (options: Required<ModuleOptions>) => ({
|
||||
slots: {
|
||||
root: 'relative group overflow-hidden bg-[var(--ui-bg)] shadow-lg rounded-[calc(var(--ui-radius)*2)] ring ring-[var(--ui-border)] p-4 flex gap-2.5 focus:outline-none',
|
||||
wrapper: 'w-0 flex-1 flex flex-col gap-1',
|
||||
wrapper: 'w-0 flex-1 flex flex-col',
|
||||
title: 'text-sm font-medium text-[var(--ui-text-highlighted)]',
|
||||
description: 'text-sm text-[var(--ui-text-muted)]',
|
||||
icon: 'shrink-0 size-5',
|
||||
@@ -11,7 +11,7 @@ export default (options: Required<ModuleOptions>) => ({
|
||||
avatarSize: '2xl',
|
||||
actions: 'flex gap-1.5 shrink-0',
|
||||
progress: 'absolute inset-x-0 bottom-0 h-1 z-[-1]',
|
||||
close: 'p-0.5'
|
||||
close: 'p-0'
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
@@ -26,14 +26,19 @@ export default (options: Required<ModuleOptions>) => ({
|
||||
progress: 'bg-[var(--ui-bg-inverted)]'
|
||||
}
|
||||
},
|
||||
multiline: {
|
||||
true: {
|
||||
root: 'items-start',
|
||||
actions: 'items-start mt-1'
|
||||
},
|
||||
false: {
|
||||
orientation: {
|
||||
horizontal: {
|
||||
root: 'items-center',
|
||||
actions: 'items-center'
|
||||
},
|
||||
vertical: {
|
||||
root: 'items-start',
|
||||
actions: 'items-start mt-2.5'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
true: {
|
||||
description: 'mt-1'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user