-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(components): make alert atom component ui
META DATA: #3
- Loading branch information
1 parent
ba850ca
commit a71a95f
Showing
6 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<template> | ||
<div> | ||
Alert: | ||
<mei-alert title="Heads up!" /> | ||
<mei-alert | ||
icon="i-heroicons-command-line" | ||
description="You can add components to your app using the cli." | ||
title="Heads up!" | ||
/> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
<template> | ||
<div | ||
:class="alertClass" | ||
v-bind="attrs" | ||
> | ||
<div | ||
class="flex" | ||
:class="[ui.gap, { 'items-start': (description || $slots.description), 'items-center': !description && !$slots.description }]" | ||
> | ||
<slot | ||
name="icon" | ||
:icon="icon" | ||
> | ||
<MeiIcon | ||
v-if="icon" | ||
:name="icon" | ||
:ui="ui.icon.base" | ||
/> | ||
</slot> | ||
<slot | ||
name="avatar" | ||
:avatar="avatar" | ||
> | ||
<MeiAvatar | ||
v-if="avatar" | ||
v-bind="{ size: ui.avatar.size, ...avatar }" | ||
:class="ui.avatar.base" | ||
/> | ||
</slot> | ||
|
||
<div :class="ui.inner"> | ||
<p | ||
v-if="(title || $slots.title)" | ||
:class="ui.title" | ||
> | ||
<slot | ||
name="title" | ||
:title="title" | ||
> | ||
{{ title }} | ||
</slot> | ||
</p> | ||
<p | ||
v-if="description || $slots.description" | ||
:class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')" | ||
> | ||
<slot | ||
name="description" | ||
:description="description" | ||
> | ||
{{ description }} | ||
</slot> | ||
</p> | ||
|
||
<div | ||
v-if="(description || $slots.description) && actions.length" | ||
:class="ui.actions" | ||
> | ||
<MeiButton | ||
v-for="(action, index) of actions" | ||
:key="index" | ||
v-bind="{ ...(ui.default.actionButton || {}), ...action }" | ||
@click.stop="onAction(action)" | ||
/> | ||
</div> | ||
</div> | ||
<div | ||
v-if="closeButton || (!description && !$slots.description && actions.length)" | ||
:class="twMerge(ui.actions, 'mt-0')" | ||
> | ||
<template v-if="!description && !$slots.description && actions.length"> | ||
<MeiButton | ||
v-for="(action, index) of actions" | ||
:key="index" | ||
v-bind="{ ...(ui.default.actionButton || {}), ...action }" | ||
@click.stop="onAction(action)" | ||
/> | ||
</template> | ||
|
||
<MeiButton | ||
v-if="closeButton" | ||
aria-label="Close" | ||
v-bind="{ ...(ui.default.closeButton || {}), ...closeButton }" | ||
@click.stop="$emit('close')" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { computed, toRef, defineComponent } from 'vue' | ||
import type { PropType } from 'vue' | ||
import { twMerge, twJoin } from 'tailwind-merge' | ||
import MeiIcon from "./icon.vue"; | ||
import MeiAvatar from "./avatar.vue"; | ||
import MeiButton from './button.vue' | ||
import { useMeiUI } from "../../composables/use-mei-ui"; | ||
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy } from '../../types' | ||
import { mergeConfig } from '../../utils' | ||
// @ts-expect-error | ||
import appConfig from '#build/app.config' | ||
import { alert } from '#mei-ui/ui-configs' | ||
const config = mergeConfig<typeof alert>(appConfig.meiUI.strategy, appConfig.meiUI.alert, alert) | ||
export default defineComponent({ | ||
components: { | ||
MeiIcon, | ||
MeiAvatar, | ||
MeiButton | ||
}, | ||
inheritAttrs: false, | ||
props: { | ||
title: { | ||
type: String, | ||
default: null | ||
}, | ||
description: { | ||
type: String, | ||
default: null | ||
}, | ||
icon: { | ||
type: String, | ||
default: () => config.default.icon | ||
}, | ||
avatar: { | ||
type: Object as PropType<Avatar>, | ||
default: null | ||
}, | ||
closeButton: { | ||
type: Object as PropType<Button>, | ||
default: () => config.default.closeButton as unknown as Button | ||
}, | ||
actions: { | ||
type: Array as PropType<AlertAction[]>, | ||
default: () => [] | ||
}, | ||
color: { | ||
type: String as PropType<AlertColor>, | ||
default: () => config.default.color, | ||
validator (value: string) { | ||
return [...appConfig.meiUI.colors, ...Object.keys(config.color)].includes(value) | ||
} | ||
}, | ||
variant: { | ||
type: String as PropType<AlertVariant>, | ||
default: () => config.default.variant, | ||
validator (value: string) { | ||
return [ | ||
...Object.keys(config.variant), | ||
...Object.values(config.color).flatMap(value => Object.keys(value)) | ||
].includes(value) | ||
} | ||
}, | ||
class: { | ||
type: [String, Object, Array] as PropType<any>, | ||
default: () => '' | ||
}, | ||
ui: { | ||
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>, | ||
default: () => ({}) | ||
} | ||
}, | ||
emits: ['close'], | ||
setup (props) { | ||
const { ui, attrs } = useMeiUI('alert', toRef(props, 'ui'), config) | ||
const alertClass = computed(() => { | ||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant] | ||
return twMerge(twJoin( | ||
ui.value.wrapper, | ||
ui.value.rounded, | ||
ui.value.shadow, | ||
ui.value.padding, | ||
variant?.replaceAll('{color}', props.color) | ||
), props.class) | ||
}) | ||
function onAction (action: AlertAction) { | ||
if (action.click) { | ||
action.click() | ||
} | ||
} | ||
return { | ||
// eslint-disable-next-line vue/no-dupe-keys | ||
ui, | ||
attrs, | ||
alertClass, | ||
onAction, | ||
twMerge | ||
} | ||
} | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { alert } from "../ui-configs"; | ||
import type { NestedKeyOf, ExtractDeepKey, ExtractDeepObject } from '.' | ||
import type { Button } from './button' | ||
import colors from '#mei-ui-colors' | ||
import type { AppConfig } from 'nuxt/schema' | ||
|
||
export type AlertColor = keyof typeof alert.color | ExtractDeepKey<AppConfig, ['ui', 'alert', 'color']> | typeof colors[number] | ||
export type AlertVariant = keyof typeof alert.variant | ExtractDeepKey<AppConfig, ['ui', 'alert', 'variant']> | NestedKeyOf<typeof alert.color> | NestedKeyOf<ExtractDeepObject<AppConfig, ['ui', 'alert', 'color']>> | ||
|
||
export interface AlertAction extends Button { | ||
click?: Function | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
export default { | ||
wrapper: 'w-full relative overflow-hidden', | ||
inner: 'w-0 flex-1', | ||
title: 'text-sm font-medium', | ||
description: 'mt-1 text-sm leading-4 opacity-90', | ||
actions: 'flex items-center gap-2 mt-3 flex-shrink-0', | ||
shadow: '', | ||
rounded: 'rounded-lg', | ||
padding: 'p-4', | ||
gap: 'gap-3', | ||
icon: { | ||
base: 'flex-shrink-0 w-5 h-5' | ||
}, | ||
avatar: { | ||
base: 'flex-shrink-0 self-center', | ||
size: 'md' as const | ||
}, | ||
color: { | ||
white: { | ||
solid: 'text-gray-900 dark:text-white bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-800' | ||
} | ||
}, | ||
variant: { | ||
solid: 'bg-{color}-500 dark:bg-{color}-400 text-white dark:text-gray-900', | ||
outline: 'text-{color}-500 dark:text-{color}-400 ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400', | ||
soft: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400', | ||
subtle: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400 ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400 ring-opacity-25 dark:ring-opacity-25' | ||
}, | ||
default: { | ||
color: 'white', | ||
variant: 'solid', | ||
icon: null, | ||
closeButton: null, | ||
actionButton: { | ||
size: 'xs' as const, | ||
color: 'primary' as const, | ||
variant: 'link' as const | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters