feat: add storybook
This commit is contained in:
0
apps/message/message.js
Normal file
0
apps/message/message.js
Normal file
270
apps/ui/deploy/message.js
Normal file
270
apps/ui/deploy/message.js
Normal file
@@ -0,0 +1,270 @@
|
||||
export class MessageContainer {
|
||||
container;
|
||||
id = 'for-message';
|
||||
root = document.body;
|
||||
constructor(opts) {
|
||||
const { id } = opts || {};
|
||||
if (id) {
|
||||
this.id = id;
|
||||
}
|
||||
this.initContainer();
|
||||
}
|
||||
initContainer() {
|
||||
const id = this.id;
|
||||
const root = this.root;
|
||||
let forModal = document.querySelector('#' + id);
|
||||
if (!forModal) {
|
||||
forModal = document.createElement('div');
|
||||
forModal.id = id;
|
||||
forModal.style = ``;
|
||||
// 点击穿透
|
||||
root.appendChild(forModal);
|
||||
}
|
||||
this.initStyle();
|
||||
this.container = forModal;
|
||||
}
|
||||
initStyle(force) {
|
||||
const id = this.id;
|
||||
const styleId = id + '-style';
|
||||
const _style = document.querySelector('#' + styleId);
|
||||
if (force && _style) {
|
||||
_style.remove();
|
||||
}
|
||||
if (!force && _style) {
|
||||
return;
|
||||
}
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
|
||||
style.innerHTML = `
|
||||
#${id} {
|
||||
position: fixed; top: 0; left: 0; z-index: 1000; width: 100vw;height: 100vh;pointer-events: none; display: flex; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.message-wrapper {
|
||||
display: flex;
|
||||
transition: transform 2s ease-in-out, opacity 1.2s ease-in-out; /* 缩小并淡出 */
|
||||
}
|
||||
.message-wrapper:first-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.message {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 6px 10px;
|
||||
margin: 0 auto;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||
0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
justify-content: center;
|
||||
align-item: center;
|
||||
animation: message-slide-down 0.5s ease-out forwards; /* 应用动画 */
|
||||
}
|
||||
/* 添加消失类时 */
|
||||
.message-wrapper.message-hide {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
pointer-events: none; /* 防止交互 */
|
||||
}
|
||||
.message-success {
|
||||
}
|
||||
@keyframes message-slide-down {
|
||||
0% {
|
||||
transform: translateY(-100px); /* 从上方开始 */
|
||||
opacity: 0; /* 从不可见状态开始 */
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5; /* 渐渐变为半透明 */
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0); /* 移动到初始位置 */
|
||||
opacity: 1; /* 最终完全可见 */
|
||||
}
|
||||
}
|
||||
.message-icon {
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.message-icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 3px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
}
|
||||
.icon-success {
|
||||
border: 2px solid green; /* 外圆圈 */
|
||||
border-radius: 50%; /* 使其为圆形 */
|
||||
}
|
||||
.icon-success::before {
|
||||
border-right: 2px solid green; /* 打勾的右边部分 */
|
||||
border-bottom: 2px solid green; /* 打勾的下边部分 */
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.icon-info {
|
||||
border: 2px solid blue; /* 外圆圈 */
|
||||
border-radius: 50%; /* 使其为圆形 */
|
||||
}
|
||||
.icon-info::before {
|
||||
content: "i";
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
color: blue;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
left: 8px;
|
||||
}
|
||||
.icon-error::before, .icon-error::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
background-color: red;
|
||||
transform-origin: center;
|
||||
}
|
||||
.icon-error {
|
||||
border: 2px solid red; /* 外圆圈 */
|
||||
border-radius: 50%; /* 使其为圆形 */
|
||||
}
|
||||
.icon-error::before {
|
||||
transform: translateX(-50%) rotate(45deg); /* 旋转形成叉号的一部分 */
|
||||
}
|
||||
|
||||
.icon-error::after {
|
||||
transform: translateX(-50%) rotate(-45deg); /* 旋转形成叉号的另一部分 */
|
||||
}
|
||||
.icon-warning {
|
||||
position: relative;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 12px solid transparent;
|
||||
border-right: 12px solid transparent;
|
||||
border-bottom: 24px solid orange; /* 三角形 */
|
||||
display: inline-block;
|
||||
transform: scale(0.8); /* 缩小三角形 */
|
||||
}
|
||||
|
||||
.icon-warning::before {
|
||||
content: "!";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
.icon-loading {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid #f3f3f3; /* 边框颜色,用于加载圈的背景 */
|
||||
border-top: 3px solid #3498db; /* 顶部边框的颜色,用于显示加载进度 */
|
||||
border-radius: 50%; /* 圆形 */
|
||||
animation: spin 1s linear infinite; /* 旋转动画 */
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
setRoot(root) {
|
||||
if (root instanceof HTMLElement) {
|
||||
this.root = root;
|
||||
root.appendChild(this.container);
|
||||
}
|
||||
}
|
||||
}
|
||||
const controller = new MessageContainer();
|
||||
export const createMessage = (content, opts) => {
|
||||
let { icon, key, style, className, type } = opts || {};
|
||||
const div = document.createElement('div');
|
||||
div.className = 'message-wrapper' + (className ? ' ' + className : '');
|
||||
if (style) div.style = style;
|
||||
if (key) div.setAttribute('data-key', key);
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.className = 'message';
|
||||
if (icon) {
|
||||
const i = document.createElement('i');
|
||||
i.className = icon;
|
||||
i.classList.add('message-icon');
|
||||
contentDiv.appendChild(i);
|
||||
} else if (type) {
|
||||
const i = document.createElement('div');
|
||||
i.className = 'icon-' + type;
|
||||
i.classList.add('message-icon');
|
||||
contentDiv.appendChild(i);
|
||||
}
|
||||
console.log(content, type, icon, 'content');
|
||||
if (content instanceof HTMLElement) {
|
||||
contentDiv.appendChild(content);
|
||||
} else {
|
||||
const text = document.createElement('span');
|
||||
text.innerText = content;
|
||||
contentDiv.appendChild(text);
|
||||
}
|
||||
div.appendChild(contentDiv);
|
||||
|
||||
return div;
|
||||
};
|
||||
const methods = ['success', 'info', 'warning', 'error', 'loading'];
|
||||
|
||||
export class Message {
|
||||
static controller = controller;
|
||||
static open = (message, timeout = 3000, onClose, opts) => {
|
||||
const div = createMessage(message, opts);
|
||||
const remove = () => {
|
||||
div.classList.add('message-hide');
|
||||
setTimeout(() => {
|
||||
if (div.isConnected) {
|
||||
div.remove();
|
||||
} else {
|
||||
console.log('not connected');
|
||||
controller.container.removeChild(div);
|
||||
}
|
||||
}, 1000);
|
||||
onClose && onClose();
|
||||
};
|
||||
controller.container.appendChild(div);
|
||||
controller.initStyle(true);
|
||||
if (timeout === 0) {
|
||||
return () => {
|
||||
remove();
|
||||
};
|
||||
}
|
||||
const time = setTimeout(() => {
|
||||
remove();
|
||||
}, timeout);
|
||||
return () => {
|
||||
clearTimeout(time);
|
||||
remove();
|
||||
};
|
||||
};
|
||||
static success = (message, timeout = 3000, onClose) => {
|
||||
return Message.open(message, timeout, onClose, { type: 'success' });
|
||||
};
|
||||
static info = (message, timeout = 3000, onClose) => {
|
||||
return Message.open(message, timeout, onClose, { type: 'info' });
|
||||
};
|
||||
static warning = (message, timeout = 3000, onClose) => {
|
||||
return Message.open(message, timeout, onClose, { type: 'warning' });
|
||||
};
|
||||
static error = (message, timeout = 3000, onClose) => {
|
||||
return Message.open(message, timeout, onClose, { type: 'error' });
|
||||
};
|
||||
static loading = (message, timeout = 0, onClose) => {
|
||||
return Message.open(message, timeout, onClose, { type: 'loading' });
|
||||
};
|
||||
}
|
||||
53
apps/ui/docs/message.stories.js
Normal file
53
apps/ui/docs/message.stories.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Message, createMessage } from '../deploy/message.js';
|
||||
export default {
|
||||
title: 'ui/message',
|
||||
tags: ['autodocs'],
|
||||
render: ({ label, fn, ...args }) => {
|
||||
const div = createMessage(label, { type: 'success' });
|
||||
div.addEventListener('click', () => {
|
||||
fn('Hello World', 2000);
|
||||
});
|
||||
div.style.border = '1px solid #000';
|
||||
const uiMessage = document.createElement('div');
|
||||
uiMessage.style = 'display: flex;gap:10px; flex-direction: column;background: #f0f0f0; padding: 20px';
|
||||
uiMessage.appendChild(div);
|
||||
|
||||
const info = createMessage('Info', { type: 'info' });
|
||||
info.style.border = '1px solid #000';
|
||||
info.addEventListener('click', () => {
|
||||
Message.info('Hello World', 1000);
|
||||
});
|
||||
uiMessage.appendChild(info);
|
||||
|
||||
const error = createMessage('Error', { type: 'error' });
|
||||
error.style.border = '1px solid #000';
|
||||
error.addEventListener('click', () => {
|
||||
Message.error('Hello World', 1500);
|
||||
});
|
||||
uiMessage.appendChild(error);
|
||||
|
||||
const warning = createMessage('Warning', { type: 'warning' });
|
||||
warning.style.border = '1px solid #000';
|
||||
warning.addEventListener('click', () => {
|
||||
Message.warning('Hello World', 3000);
|
||||
});
|
||||
uiMessage.appendChild(warning);
|
||||
|
||||
const loading = createMessage('Loading', { type: 'loading' });
|
||||
loading.style.border = '1px solid #000';
|
||||
loading.addEventListener('click', () => {
|
||||
Message.loading('Hello World');
|
||||
});
|
||||
uiMessage.appendChild(loading);
|
||||
|
||||
return uiMessage;
|
||||
},
|
||||
};
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
||||
export const Success = {
|
||||
args: {
|
||||
label: 'Success',
|
||||
fn: Message.success,
|
||||
},
|
||||
};
|
||||
15
apps/ui/index.html
Normal file
15
apps/ui/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>UI</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./deploy/me.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
13
apps/ui/package.json
Normal file
13
apps/ui/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "ui",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"pub": "envision switchOrg system && envision deploy ./deploy -v 0.0.1 -k ui -y y",
|
||||
"dev": "vite"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
Reference in New Issue
Block a user