This commit is contained in:
Nadar
2026-03-17 13:24:22 +03:00
commit 82e5ac9d81
554 changed files with 29637 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<template>
<div
:class="[
cn.b(),
cn.m(type),
cn.m(size),
cn.has('title', hasTitle),
cn.has('action', hasAction),
]"
>
<div :class="[cn.e('content')]">
<div
v-if="$slots.title || title"
:class="[cn.e('title')]"
>
<slot name="title">
{{ title }}
</slot>
</div>
<div :class="[cn.e('text')]">
<slot>{{ text }}</slot>
</div>
<slot name="action" />
</div>
<div :class="cn.e('icon')">
<slot name="icon">
<Component :is="resolveComponent(`ui-icon-${TYPE_ICON_MAPPING[type]}`)" />
</slot>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import useClassname from '../../composables/use-classname'
import type { AlertProps, AlertType } from './types'
import type { UiIcon } from '#build/types/ui/icons'
defineOptions({
name: 'UiAlert',
})
const props = withDefaults(defineProps<AlertProps>(), {
type: 'neutral',
size: 'normal',
})
const slots = defineSlots<{
default(props: NonNullable<unknown>): never
title(props: NonNullable<unknown>): never
action(props: NonNullable<unknown>): never
}>()
const TYPE_ICON_MAPPING: Record<AlertType, UiIcon> = {
neutral: 'exclamation-filled',
positive: 'confirmed-filled',
warning: 'danger-filled',
negative: 'cancel-filled',
marketing: 'ask-for-discount-filled',
}
const cn = useClassname('ui-alert')
const hasTitle = computed(() => !!props.title || slots.title)
const hasAction = computed(() => slots.action)
</script>
<style lang="scss">
@use 'styles';
</style>

View File

@@ -0,0 +1,83 @@
@use '../../styles/mixins' as *;
@use '../../styles/variables' as *;
.ui-alert {
$self: &;
--icon-color: var(--alert-icon-color);
display: flex;
align-items: center;
padding: 8px 16px;
border-radius: 12px;
background-color: var(--alert-background);
text-align: left;
&__title {
@include txt-i-b('alert-title');
grid-area: title;
margin-bottom: var(--alert-title-margin, 4px);
margin-top: 2px;
}
&__text {
@include txt-i-m('alert-text');
grid-area: text;
color: $clr-black;
#{$self}.has-title & {
color: var(--alert-text-color, $clr-grey-500);
}
#{$self}.has-action & {
margin-bottom: 4px;
}
}
&__icon {
grid-area: icon;
align-self: center;
color: var(--icon-color);
padding: 8px;
#{$self}.has-title &,
#{$self}.has-action & {
align-self: flex-start
}
}
&__content{
flex: 1;
}
@include element-variant('alert', 'large', (
'padding': 16px,
));
@include element-variant('alert', 'neutral', (
'icon-color': $clr-grey-500,
'background': $clr-grey-200,
));
@include element-variant('alert', 'positive', (
'icon-color': $clr-green-500,
'background': $clr-green-100,
));
@include element-variant('alert', 'warning', (
'icon-color': $clr-warn-500,
'background': $clr-warn-100,
));
@include element-variant('alert', 'negative', (
'icon-color': $clr-red-500,
'background': $clr-red-100,
));
@include element-variant('alert', 'marketing', (
'icon-color': $clr-market-500,
'background': $clr-market-100,
));
}

View File

@@ -0,0 +1,16 @@
export const ALERT_TYPES = [
'neutral',
'positive',
'warning',
'negative',
'marketing',
] as const
export type AlertType = (typeof ALERT_TYPES)[number]
export interface AlertProps {
type?: AlertType
title?: string
text?: string
size?: string
}