Browse Source

feat: add alert component

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/581/head
Ryan Wang 3 years ago
parent
commit
db32465a04
  1. 91
      src/components/base/alert/Alert.story.vue
  2. 200
      src/components/base/alert/Alert.vue
  3. 8
      src/components/base/alert/__tests__/Alert.spec.ts
  4. 1
      src/components/base/alert/index.ts
  5. 1
      src/components/base/alert/interface.ts
  6. 12
      src/core/icons.ts

91
src/components/base/alert/Alert.story.vue

@ -0,0 +1,91 @@
<script lang="ts" setup>
import { VAlert } from "./index";
import { VButton } from "@/components/base/button";
import { IconMessage } from "@/core/icons";
function initState() {
return {
type: "default",
title: "提示",
description: "这是一个提示",
closable: true,
};
}
</script>
<template>
<Story title="Alert">
<Variant title="Show Cases">
<template #default>
<div class="p-3">
<VAlert type="default" title="default" />
</div>
<div class="p-3">
<VAlert type="success" title="default" />
</div>
<div class="p-3">
<VAlert type="info" title="default" />
</div>
<div class="p-3">
<VAlert type="warning" title="default" />
</div>
<div class="p-3">
<VAlert type="error" title="default" />
</div>
<div class="p-3">
<VAlert type="default" title="提示">
<template #description>
目前仅支持远程 Git 仓库和 ZIP 下载链接更多主题可以访问
<a href="https://halo.run/themes">https://halo.run/themes</a>
</template>
<template #actions>
<div class="flex justify-end">
<VButton type="default">访问</VButton>
</div>
</template>
</VAlert>
</div>
</template>
</Variant>
<Variant title="Playground" :init-state="initState">
<template #default="{ state }">
<div class="p-3">
<VAlert
:type="state.type"
:title="state.title"
:description="state.description"
:closable="state.closable"
/>
</div>
</template>
<template #controls="{ state }">
<HstText v-model="state.type" title="Type" />
<HstText v-model="state.title" title="Title" />
<HstText v-model="state.description" title="Description" />
<HstCheckbox v-model="state.closable" title="Closable" />
</template>
</Variant>
<Variant title="Slots">
<template #default>
<div class="p-3">
<VAlert type="default" title="你有新的消息">
<template #icon>
<IconMessage />
</template>
<template #description>
@Ryan Wang 在你的站点创建了新评论
</template>
<template #actions>
<div class="flex justify-end">
<VButton type="default">查看</VButton>
</div>
</template>
</VAlert>
</div>
</template>
</Variant>
</Story>
</template>

200
src/components/base/alert/Alert.vue

@ -0,0 +1,200 @@
<script lang="ts" setup>
import type { Component, PropType } from "vue";
import { computed } from "vue";
import type { Type } from "@/components/base/alert/interface";
import {
IconCheckboxCircle,
IconClose,
IconCloseCircle,
IconErrorWarning,
IconInformation,
} from "@/core/icons";
const TypeIcons: Record<Type, Component> = {
success: IconCheckboxCircle,
info: IconInformation,
default: IconInformation,
warning: IconErrorWarning,
error: IconCloseCircle,
};
const props = defineProps({
type: {
type: String as PropType<Type>,
default: "default",
},
title: {
type: String,
},
description: {
type: String,
},
closable: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(["close"]);
const classes = computed(() => {
return [`alert-${props.type}`];
});
const handleClose = () => {
emit("close");
};
</script>
<template>
<div :class="classes" class="alert-wrapper">
<div class="alert-header">
<div class="alert-icon">
<slot name="icon">
<component :is="TypeIcons[type]" />
</slot>
</div>
<div class="alert-title">
{{ title }}
</div>
<div v-if="closable" class="alert-close" @click="handleClose">
<IconClose />
</div>
</div>
<div v-if="description || $slots.description" class="alert-description">
<slot name="description">
{{ description }}
</slot>
</div>
<div v-if="$slots.actions" class="alert-actions">
<slot name="actions" />
</div>
</div>
</template>
<style lang="scss" scoped>
.alert-wrapper {
@apply flex;
@apply flex-col;
@apply box-border;
@apply border;
padding: 12px 16px;
border-radius: 4px;
.alert-header {
@apply flex;
.alert-icon {
@apply self-center;
@apply mr-3;
@apply text-lg;
}
.alert-title {
@apply self-center;
@apply mr-3;
@apply flex-1;
@apply font-medium;
@apply text-base;
}
.alert-close {
@apply self-center;
@apply cursor-pointer;
@apply rounded-full;
@apply p-0.5;
&:hover {
@apply transition-all;
@apply bg-gray-300;
@apply text-white;
}
}
}
.alert-description {
@apply text-sm;
@apply mt-2;
}
.alert-actions {
@apply border-t;
@apply pt-2 mt-3;
}
&.alert-default {
@apply bg-gray-50;
@apply border-gray-300;
.alert-icon,
.alert-description {
@apply text-gray-600;
}
.alert-close,
.alert-title {
@apply text-gray-700;
}
}
&.alert-success {
@apply bg-green-50;
@apply border-green-300;
.alert-icon,
.alert-description {
@apply text-green-600;
}
.alert-close,
.alert-title {
@apply text-green-700;
}
}
&.alert-info {
@apply bg-sky-50;
@apply border-sky-300;
.alert-icon,
.alert-description {
@apply text-sky-600;
}
.alert-close,
.alert-title {
@apply text-sky-700;
}
}
&.alert-warning {
@apply bg-orange-50;
@apply border-orange-300;
.alert-icon,
.alert-description {
@apply text-orange-600;
}
.alert-close,
.alert-title {
@apply text-orange-700;
}
}
&.alert-error {
@apply bg-red-50;
@apply border-red-300;
.alert-icon,
.alert-description {
@apply text-red-600;
}
.alert-close,
.alert-title {
@apply text-red-700;
}
}
}
</style>

8
src/components/base/alert/__tests__/Alert.spec.ts

@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest";
import { VAlert } from "../index";
describe("Alert", () => {
it("should render", () => {
expect(VAlert).toBeDefined();
});
});

1
src/components/base/alert/index.ts

@ -0,0 +1 @@
export { default as VAlert } from "./Alert.vue";

1
src/components/base/alert/interface.ts

@ -0,0 +1 @@
export type Type = "default" | "success" | "info" | "warning" | "error";

12
src/core/icons.ts

@ -28,6 +28,14 @@ import IconFolder from "~icons/ri/folder-2-line";
import IconMore from "~icons/ri/more-line";
// @ts-ignore
import IconClose from "~icons/ri/close-line";
// @ts-ignore
import IconErrorWarning from "~icons/ri/error-warning-line";
// @ts-ignore
import IconCheckboxCircle from "~icons/ri/checkbox-circle-line";
// @ts-ignore
import IconInformation from "~icons/ri/information-line";
// @ts-ignore
import IconCloseCircle from "~icons/ri/close-circle-line";
export {
IconDashboard,
@ -45,4 +53,8 @@ export {
IconFolder,
IconMore,
IconClose,
IconErrorWarning,
IconCheckboxCircle,
IconInformation,
IconCloseCircle,
};

Loading…
Cancel
Save