Files
MetonaToast/src/styles.js
T

1845 lines
42 KiB
JavaScript

/**
* MetonaToast Styles - 样式管理
* @module styles
* @version 2.0.0
* @description 样式注入、更新和主题管理
*/
import { THEMES } from './constants.js';
// 样式缓存
let injectedStyles = null;
let styleElement = null;
/**
* 生成CSS样式
* @param {Object} theme - 主题配置
* @returns {string} CSS字符串
*/
const generateCSS = (theme) => {
return `
/* MetonaToast 基础样式 */
.met-container {
pointer-events: none;
display: flex;
flex-direction: column;
gap: 12px;
box-sizing: border-box;
padding: 24px;
max-width: 100vw;
position: fixed;
z-index: 9999;
}
/* 位置样式 — 顶部位置用 column-reverse,新 toast 出现在最上方 */
.met-container.top-left {
top: 0;
left: 0;
align-items: flex-start;
flex-direction: column-reverse;
}
.met-container.top-center {
top: 0;
left: 0;
right: 0;
align-items: center;
flex-direction: column-reverse;
}
.met-container.top-right {
top: 0;
right: 0;
align-items: flex-end;
flex-direction: column-reverse;
}
.met-container.bottom-left {
bottom: 0;
left: 0;
align-items: flex-start;
flex-direction: column-reverse;
}
.met-container.bottom-center {
bottom: 0;
left: 0;
right: 0;
align-items: center;
flex-direction: column-reverse;
}
.met-container.bottom-right {
bottom: 0;
right: 0;
align-items: flex-end;
flex-direction: column-reverse;
}
/* Toast基础样式 */
.met-toast {
position: relative;
display: flex;
align-items: flex-start;
gap: 12px;
padding: 14px 16px;
border-radius: 12px;
box-sizing: border-box;
backdrop-filter: blur(14px) saturate(180%);
-webkit-backdrop-filter: blur(14px) saturate(180%);
font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
user-select: none;
-webkit-user-select: none;
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.25s ease,
opacity 0.25s ease;
will-change: transform, opacity;
flex-shrink: 0;
pointer-events: auto;
min-width: 240px;
max-width: calc(100vw - 48px);
width: 360px;
border: 1px solid var(--met-border, rgba(0,0,0,0.06));
background: var(--met-bg, rgba(255,255,255,0.96));
color: var(--met-text, #1f2937);
box-shadow: var(--met-shadow, 0 10px 36px -10px rgba(0,0,0,0.18), 0 4px 14px -4px rgba(0,0,0,0.08));
}
/* 悬停效果 */
.met-toast:hover {
box-shadow: var(--met-hover-shadow, 0 14px 48px -10px rgba(0,0,0,0.22), 0 6px 18px -4px rgba(0,0,0,0.10)) !important;
}
/* 可点击状态 */
.met-toast.met-clickable {
cursor: pointer;
}
/* 图标样式 */
.met-icon {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
margin-top: 1px;
}
/* 内容样式 */
.met-content {
flex: 1;
min-width: 0;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* 标题样式 */
.met-title {
font-weight: 600;
font-size: 14px;
letter-spacing: 0.1px;
}
/* 消息样式 */
.met-message {
font-size: 13px;
opacity: 0.85;
margin-top: 2px;
}
/* 关闭按钮样式 */
.met-close {
flex-shrink: 0;
background: transparent;
border: 0;
padding: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: background 0.2s, color 0.2s;
line-height: 0;
color: inherit;
opacity: 0.5;
}
.met-close:hover {
opacity: 1;
background: var(--met-close-hover-bg, rgba(0,0,0,0.06));
}
/* 进度条样式 - 水平 */
.met-progress {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 3px;
overflow: hidden;
background: var(--met-progress-bg, rgba(0,0,0,0.06));
border-radius: 0 0 12px 12px;
}
/* 进度条样式 - 垂直 */
.met-progress-v {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
overflow: hidden;
background: var(--met-progress-bg, rgba(0,0,0,0.06));
border-radius: 12px 0 0 12px;
}
/* 进度条指示器 - 水平 */
.met-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform-origin: left;
transform: scaleX(1);
}
/* 进度条指示器 - 垂直 */
.met-bar-v {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
transform-origin: bottom;
transform: scaleY(1);
}
/* 侧边指示器 */
.met-side {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
border-radius: 12px 0 0 12px;
}
/* 加载动画 */
.met-spin {
animation: met-rot 1s linear infinite;
transform-origin: 50% 50%;
}
/* 离开状态 */
.met-toast.met-leaving {
pointer-events: none;
z-index: 1;
}
/* 动画关键帧 */
@keyframes met-rot {
to {
transform: rotate(360deg);
}
}
/* === 进入动画:初始隐藏态 === */
.met-anim-slide.met-toast,
.met-anim-fade.met-toast,
.met-anim-scale.met-toast,
.met-anim-bounce.met-toast,
.met-anim-flip.met-toast,
.met-anim-rotate.met-toast,
.met-anim-zoom.met-toast,
.met-anim-slideUp.met-toast,
.met-anim-slideDown.met-toast,
.met-anim-slideLeft.met-toast,
.met-anim-slideRight.met-toast {
opacity: 0;
}
/* === 进入动画:@keyframes 定义 === */
@keyframes met-slide-in {
0% { transform: translateX(80px); opacity: 0; }
65% { transform: translateX(-6px); opacity: 1; }
100% { transform: translateX(0); opacity: 1; }
}
@keyframes met-fade-in {
0% { opacity: 0; filter: blur(3px); }
100% { opacity: 1; filter: blur(0); }
}
@keyframes met-scale-in {
0% { transform: scale(0.55); opacity: 0; }
65% { transform: scale(1.07); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes met-bounce-in {
0% { transform: translateY(-80px); opacity: 0; animation-timing-function: ease-in; }
45% { transform: translateY(14px); opacity: 1; animation-timing-function: ease-out; }
65% { transform: translateY(-12px); animation-timing-function: ease-in; }
82% { transform: translateY(4px); animation-timing-function: ease-out; }
100% { transform: translateY(0); opacity: 1; }
}
@keyframes met-flip-in {
0% { transform: perspective(500px) rotateX(-90deg); opacity: 0; }
60% { transform: perspective(500px) rotateX(8deg); opacity: 1; }
85% { transform: perspective(500px) rotateX(-2deg); }
100% { transform: perspective(500px) rotateX(0); opacity: 1; }
}
@keyframes met-rotate-in {
0% { transform: rotate(-25deg) scale(0.6); opacity: 0; }
60% { transform: rotate(4deg) scale(1.05); opacity: 1; }
85% { transform: rotate(-1deg) scale(0.98); }
100% { transform: rotate(0) scale(1); opacity: 1; }
}
@keyframes met-zoom-in {
0% { transform: scale(0.1); opacity: 0; }
50% { transform: scale(1.12); opacity: 1; }
75% { transform: scale(0.94); }
100% { transform: scale(1); opacity: 1; }
}
@keyframes met-slideUp-in {
0% { transform: translateY(60px); opacity: 0; }
70% { transform: translateY(-6px); opacity: 1; }
100% { transform: translateY(0); opacity: 1; }
}
@keyframes met-slideDown-in {
0% { transform: translateY(-60px); opacity: 0; }
70% { transform: translateY(6px); opacity: 1; }
100% { transform: translateY(0); opacity: 1; }
}
@keyframes met-slideLeft-in {
0% { transform: translateX(-90px); opacity: 0; }
65% { transform: translateX(6px); opacity: 1; }
100% { transform: translateX(0); opacity: 1; }
}
@keyframes met-slideRight-in {
0% { transform: translateX(90px); opacity: 0; }
65% { transform: translateX(-6px); opacity: 1; }
100% { transform: translateX(0); opacity: 1; }
}
/* === 进入动画:绑定到 .met-show === */
.met-anim-slide.met-toast.met-show { animation: met-slide-in 0.40s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-fade.met-toast.met-show { animation: met-fade-in 0.50s ease forwards; }
.met-anim-scale.met-toast.met-show { animation: met-scale-in 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; }
.met-anim-bounce.met-toast.met-show { animation: met-bounce-in 0.65s ease forwards; }
.met-anim-flip.met-toast.met-show { animation: met-flip-in 0.50s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-rotate.met-toast.met-show { animation: met-rotate-in 0.50s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-zoom.met-toast.met-show { animation: met-zoom-in 0.50s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; }
.met-anim-slideUp.met-toast.met-show { animation: met-slideUp-in 0.40s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-slideDown.met-toast.met-show { animation: met-slideDown-in 0.40s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-slideLeft.met-toast.met-show { animation: met-slideLeft-in 0.40s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.met-anim-slideRight.met-toast.met-show { animation: met-slideRight-in 0.40s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
/* 响应式设计 */
@media (prefers-reduced-motion: reduce) {
.met-toast {
transition: opacity 0.15s !important;
}
.met-toast.met-show {
animation: none !important;
opacity: 1 !important;
}
.met-spin {
animation-duration: 2.5s !important;
}
}
@media (max-width: 480px) {
.met-container {
padding: 12px !important;
}
.met-toast {
width: 100% !important;
min-width: 0 !important;
}
}
/* 暗色主题特定样式 */
.met-toast.met-theme-dark {
background: rgba(28, 32, 40, 0.94);
color: #e6e8eb;
border-color: rgba(255, 255, 255, 0.08);
box-shadow: 0 10px 36px -10px rgba(0, 0, 0, 0.6),
0 4px 14px -4px rgba(0, 0, 0, 0.4);
}
.met-toast.met-theme-dark:hover {
box-shadow: 0 14px 48px -8px rgba(0, 0, 0, 0.6),
0 6px 18px -4px rgba(0, 0, 0, 0.4) !important;
}
.met-toast.met-theme-dark .met-close:hover {
background: rgba(255, 255, 255, 0.08);
}
.met-toast.met-theme-dark .met-progress,
.met-toast.met-theme-dark .met-progress-v {
background: rgba(255, 255, 255, 0.08);
}
/* 亮色主题特定样式 */
.met-toast.met-theme-light {
background: rgba(255, 255, 255, 0.96);
color: #1f2937;
border-color: rgba(0, 0, 0, 0.06);
box-shadow: 0 10px 36px -10px rgba(0, 0, 0, 0.18),
0 4px 14px -4px rgba(0, 0, 0, 0.08);
}
.met-toast.met-theme-light:hover {
box-shadow: 0 14px 48px -10px rgba(0, 0, 0, 0.22),
0 6px 18px -4px rgba(0, 0, 0, 0.10) !important;
}
.met-toast.met-theme-light .met-close:hover {
background: rgba(0, 0, 0, 0.06);
}
.met-toast.met-theme-light .met-progress,
.met-toast.met-theme-light .met-progress-v {
background: rgba(0, 0, 0, 0.06);
}
/* 自定义主题支持 */
.met-toast[data-theme] {
--met-bg: var(--met-theme-bg);
--met-text: var(--met-theme-text);
--met-border: var(--met-theme-border);
--met-shadow: var(--met-theme-shadow);
}
/* 动画性能优化 */
.met-toast {
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
perspective: 1000;
}
/* 滚动条样式 */
.met-container::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.met-container::-webkit-scrollbar-track {
background: transparent;
}
.met-container::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.met-container::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
/* 打印样式 */
@media print {
.met-container {
display: none !important;
}
.met-toast {
display: none !important;
}
}
/* 高对比度模式 */
@media (forced-colors: active) {
.met-toast {
border: 2px solid ButtonText;
}
.met-close {
border: 1px solid ButtonText;
}
}
/* 焦点样式 */
.met-toast:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
.met-close:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* 禁用状态 */
.met-toast:disabled,
.met-toast[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
/* 加载状态 */
.met-toast.met-loading {
cursor: wait;
}
/* 成功状态 */
.met-toast.met-success {
border-left: 4px solid #10b981;
}
/* 错误状态 */
.met-toast.met-error {
border-left: 4px solid #ef4444;
}
/* 警告状态 */
.met-toast.met-warning {
border-left: 4px solid #f59e0b;
}
/* 信息状态 */
.met-toast.met-info {
border-left: 4px solid #3b82f6;
}
/* 加载状态 */
.met-toast.met-loading {
border-left: 4px solid #6366f1;
}
/* 进度条动画 */
.met-bar,
.met-bar-v {
transition: transform 0.1s linear;
}
/* 容器动画 */
.met-container {
transition: all 0.3s ease;
}
/* Toast进入动画 */
.met-toast.met-enter {
animation: met-enter 0.3s ease forwards;
}
/* Toast离开动画 */
.met-toast.met-leave {
animation: met-leave 0.3s ease forwards;
}
@keyframes met-enter {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes met-leave {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}
/* 响应式字体大小 */
@media (max-width: 768px) {
.met-toast {
font-size: 13px;
}
.met-title {
font-size: 13px;
}
.met-message {
font-size: 12px;
}
}
@media (max-width: 480px) {
.met-toast {
font-size: 12px;
padding: 12px 14px;
}
.met-title {
font-size: 12px;
}
.met-message {
font-size: 11px;
}
}
/* 无障碍支持 */
.met-toast[role="alert"] {
/* 警告类型Toast的特殊样式 */
}
.met-toast[role="status"] {
/* 状态类型Toast的特殊样式 */
}
/* 高对比度模式增强 */
@media (prefers-contrast: high) {
.met-toast {
border-width: 2px;
}
.met-close {
border: 1px solid currentColor;
}
}
/* 暗色模式自动检测 */
@media (prefers-color-scheme: dark) {
.met-theme-auto .met-toast {
background: rgba(28, 32, 40, 0.94);
color: #e6e8eb;
border-color: rgba(255, 255, 255, 0.08);
box-shadow: 0 10px 36px -10px rgba(0, 0, 0, 0.6),
0 4px 14px -4px rgba(0, 0, 0, 0.4);
}
.met-theme-auto .met-toast:hover {
box-shadow: 0 14px 48px -8px rgba(0, 0, 0, 0.6),
0 6px 18px -4px rgba(0, 0, 0, 0.4) !important;
}
.met-theme-auto .met-close:hover {
background: rgba(255, 255, 255, 0.08);
}
.met-theme-auto .met-progress,
.met-theme-auto .met-progress-v {
background: rgba(255, 255, 255, 0.08);
}
}
/* 亮色模式自动检测 */
@media (prefers-color-scheme: light) {
.met-theme-auto .met-toast {
background: rgba(255, 255, 255, 0.96);
color: #1f2937;
border-color: rgba(0, 0, 0, 0.06);
box-shadow: 0 10px 36px -10px rgba(0, 0, 0, 0.18),
0 4px 14px -4px rgba(0, 0, 0, 0.08);
}
.met-theme-auto .met-toast:hover {
box-shadow: 0 14px 48px -10px rgba(0, 0, 0, 0.22),
0 6px 18px -4px rgba(0, 0, 0, 0.10) !important;
}
.met-theme-auto .met-close:hover {
background: rgba(0, 0, 0, 0.06);
}
.met-theme-auto .met-progress,
.met-theme-auto .met-progress-v {
background: rgba(0, 0, 0, 0.06);
}
}
/* 动画性能优化 */
.met-toast {
transform: translateZ(0);
-webkit-transform: translateZ(0);
}
/* 触摸设备优化 */
@media (hover: none) {
.met-toast:hover {
box-shadow: inherit;
}
}
/* 滚动时固定位置 */
.met-container {
position: fixed;
z-index: 9999;
}
/* 全屏模式 */
:fullscreen .met-container {
z-index: 2147483647;
}
/* 画中画模式 */
:picture-in-picture .met-container {
z-index: 2147483647;
}
/* 安全区域适配 */
@supports (padding: max(0px)) {
.met-container {
padding: max(24px, env(safe-area-inset-top))
max(24px, env(safe-area-inset-right))
max(24px, env(safe-area-inset-bottom))
max(24px, env(safe-area-inset-left));
}
}
/* 暗色模式安全区域 */
@media (prefers-color-scheme: dark) {
@supports (padding: max(0px)) {
.met-container {
padding: max(24px, env(safe-area-inset-top))
max(24px, env(safe-area-inset-right))
max(24px, env(safe-area-inset-bottom))
max(24px, env(safe-area-inset-left));
}
}
}
/* 高分辨率屏幕优化 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.met-toast {
border-width: 0.5px;
}
}
/* 触摸设备优化 */
@media (hover: none) and (pointer: coarse) {
.met-toast {
min-height: 48px;
}
.met-close {
min-width: 48px;
min-height: 48px;
}
}
/* 键盘导航优化 */
.met-toast:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
.met-toast:focus:not(:focus-visible) {
outline: none;
}
.met-toast:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* 减少动画模式 */
@media (prefers-reduced-motion: reduce) {
.met-toast {
transition: opacity 0.15s ease !important;
animation: none !important;
}
.met-bar,
.met-bar-v {
transition: none !important;
}
.met-container {
transition: none !important;
}
}
/* 高对比度模式 */
@media (prefers-contrast: high) {
.met-toast {
border-width: 3px;
border-style: solid;
}
.met-close {
border: 2px solid currentColor;
border-radius: 4px;
}
.met-progress,
.met-progress-v {
height: 4px;
}
.met-bar,
.met-bar-v {
height: 100%;
}
}
/* 低分辨率屏幕优化 */
@media (max-resolution: 1dppx) {
.met-toast {
border-width: 1px;
}
}
/* 触摸设备优化 */
@media (hover: none) and (pointer: coarse) {
.met-toast {
min-height: 52px;
padding: 16px 18px;
}
.met-close {
min-width: 52px;
min-height: 52px;
padding: 8px;
}
.met-icon {
width: 28px;
height: 28px;
}
}
/* 大屏幕优化 */
@media (min-width: 1200px) {
.met-toast {
width: 400px;
}
}
/* 超大屏幕优化 */
@media (min-width: 1920px) {
.met-toast {
width: 440px;
}
}
/* 小屏幕优化 */
@media (max-width: 320px) {
.met-toast {
width: 100%;
min-width: 0;
padding: 12px 14px;
}
.met-container {
padding: 8px;
}
}
/* 横屏优化 */
@media (orientation: landscape) and (max-height: 500px) {
.met-container {
padding: 12px 24px;
}
.met-toast {
min-height: 44px;
padding: 10px 14px;
}
}
/* 竖屏优化 */
@media (orientation: portrait) and (max-width: 500px) {
.met-container {
padding: 12px;
}
.met-toast {
width: 100%;
min-width: 0;
}
}
/* 无障碍支持 */
.met-toast[aria-live="assertive"] {
/* 紧急通知样式 */
}
.met-toast[aria-live="polite"] {
/* 普通通知样式 */
}
/* 焦点陷阱 */
.met-toast:focus-within {
/* 包含焦点元素的Toast样式 */
}
/* 加载状态 */
.met-toast[aria-busy="true"] {
cursor: wait;
}
/* 禁用状态 */
.met-toast[aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
/* 隐藏状态 */
.met-toast[aria-hidden="true"] {
display: none;
}
/* 展开状态 */
.met-toast[aria-expanded="true"] {
/* 展开状态样式 */
}
/* 选中状态 */
.met-toast[aria-selected="true"] {
/* 选中状态样式 */
}
/* 按下状态 */
.met-toast:active {
transform: scale(0.98);
}
/* 加载状态动画 */
.met-toast.met-loading::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
animation: met-loading 1.5s infinite;
}
@keyframes met-loading {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
/* 成功状态动画 */
.met-toast.met-success::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #10b981;
border-radius: 12px 0 0 12px;
animation: met-success 0.3s ease;
}
@keyframes met-success {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
/* 错误状态动画 */
.met-toast.met-error::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #ef4444;
border-radius: 12px 0 0 12px;
animation: met-error 0.3s ease;
}
@keyframes met-error {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
/* 警告状态动画 */
.met-toast.met-warning::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #f59e0b;
border-radius: 12px 0 0 12px;
animation: met-warning 0.3s ease;
}
@keyframes met-warning {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
/* 信息状态动画 */
.met-toast.met-info::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #3b82f6;
border-radius: 12px 0 0 12px;
animation: met-info 0.3s ease;
}
@keyframes met-info {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
/* 加载状态动画 */
.met-toast.met-loading::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #6366f1;
border-radius: 12px 0 0 12px;
animation: met-loading-bar 1s infinite;
}
@keyframes met-loading-bar {
0% {
transform: scaleY(0);
transform-origin: top;
}
50% {
transform: scaleY(1);
transform-origin: top;
}
51% {
transform-origin: bottom;
}
100% {
transform: scaleY(0);
transform-origin: bottom;
}
}
/* 进度条脉冲动画 */
.met-bar,
.met-bar-v {
animation: met-pulse 2s infinite;
}
@keyframes met-pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.8;
}
}
/* Toast堆叠效果 */
.met-toast + .met-toast {
margin-top: 8px;
}
/* Toast分组 */
.met-toast-group {
display: flex;
flex-direction: column;
gap: 8px;
}
/* Toast头部 */
.met-toast-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
/* Toast底部 */
.met-toast-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
margin-top: 12px;
}
/* Toast操作按钮 */
.met-toast-action {
background: transparent;
border: 1px solid currentColor;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
opacity: 0.7;
transition: opacity 0.2s;
}
.met-toast-action:hover {
opacity: 1;
}
/* Toast进度文本 */
.met-toast-progress-text {
font-size: 12px;
opacity: 0.7;
margin-left: 8px;
}
/* Toast时间戳 */
.met-toast-timestamp {
font-size: 11px;
opacity: 0.5;
margin-left: auto;
}
/* Toast头像 */
.met-toast-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
/* Toast图片 */
.met-toast-image {
max-width: 100%;
max-height: 200px;
border-radius: 8px;
margin-top: 8px;
object-fit: cover;
}
/* Toast视频 */
.met-toast-video {
max-width: 100%;
max-height: 200px;
border-radius: 8px;
margin-top: 8px;
}
/* Toast音频 */
.met-toast-audio {
width: 100%;
margin-top: 8px;
}
/* Toast代码块 */
.met-toast-code {
background: rgba(0, 0, 0, 0.05);
padding: 8px 12px;
border-radius: 6px;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 12px;
margin-top: 8px;
overflow-x: auto;
}
.met-theme-dark .met-toast-code {
background: rgba(255, 255, 255, 0.05);
}
/* Toast引用 */
.met-toast-quote {
border-left: 3px solid currentColor;
padding-left: 12px;
margin-top: 8px;
opacity: 0.8;
}
/* Toast列表 */
.met-toast-list {
margin: 8px 0 0 0;
padding-left: 20px;
}
.met-toast-list li {
margin-bottom: 4px;
}
/* Toast表格 */
.met-toast-table {
width: 100%;
border-collapse: collapse;
margin-top: 8px;
font-size: 12px;
}
.met-toast-table th,
.met-toast-table td {
border: 1px solid rgba(0, 0, 0, 0.1);
padding: 6px 8px;
text-align: left;
}
.met-theme-dark .met-toast-table th,
.met-theme-dark .met-toast-table td {
border-color: rgba(255, 255, 255, 0.1);
}
.met-toast-table th {
background: rgba(0, 0, 0, 0.05);
font-weight: 600;
}
.met-theme-dark .met-toast-table th {
background: rgba(255, 255, 255, 0.05);
}
/* Toast分割线 */
.met-toast-divider {
height: 1px;
background: rgba(0, 0, 0, 0.1);
margin: 12px 0;
}
.met-theme-dark .met-toast-divider {
background: rgba(255, 255, 255, 0.1);
}
/* Toast标签 */
.met-toast-tag {
display: inline-block;
background: rgba(0, 0, 0, 0.05);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
margin-right: 4px;
margin-bottom: 4px;
}
.met-theme-dark .met-toast-tag {
background: rgba(255, 255, 255, 0.05);
}
/* Toast徽章 */
.met-toast-badge {
display: inline-flex;
align-items: center;
justify-content: center;
background: #3b82f6;
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
min-width: 20px;
height: 20px;
}
/* Toast进度环 */
.met-toast-progress-ring {
width: 40px;
height: 40px;
transform: rotate(-90deg);
}
.met-toast-progress-ring circle {
fill: none;
stroke-width: 3;
stroke-linecap: round;
}
.met-toast-progress-ring .met-progress-ring-bg {
stroke: rgba(0, 0, 0, 0.1);
}
.met-theme-dark .met-toast-progress-ring .met-progress-ring-bg {
stroke: rgba(255, 255, 255, 0.1);
}
.met-toast-progress-ring .met-progress-ring-fill {
stroke: #3b82f6;
stroke-dasharray: 100;
stroke-dashoffset: 100;
transition: stroke-dashoffset 0.3s ease;
}
/* Toast滑块 */
.met-toast-slider {
width: 100%;
height: 4px;
background: rgba(0, 0, 0, 0.1);
border-radius: 2px;
margin-top: 8px;
position: relative;
}
.met-theme-dark .met-toast-slider {
background: rgba(255, 255, 255, 0.1);
}
.met-toast-slider-fill {
position: absolute;
left: 0;
top: 0;
height: 100%;
background: #3b82f6;
border-radius: 2px;
transition: width 0.3s ease;
}
/* Toast开关 */
.met-toast-switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
margin-top: 8px;
}
.met-toast-switch input {
opacity: 0;
width: 0;
height: 0;
}
.met-toast-switch-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
border-radius: 20px;
transition: 0.3s;
}
.met-theme-dark .met-toast-switch-slider {
background: rgba(255, 255, 255, 0.1);
}
.met-toast-switch-slider:before {
content: '';
position: absolute;
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background: white;
border-radius: 50%;
transition: 0.3s;
}
.met-toast-switch input:checked + .met-toast-switch-slider {
background: #3b82f6;
}
.met-toast-switch input:checked + .met-toast-switch-slider:before {
transform: translateX(16px);
}
/* Toast复选框 */
.met-toast-checkbox {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
cursor: pointer;
}
.met-toast-checkbox input {
width: 16px;
height: 16px;
cursor: pointer;
}
/* Toast单选框 */
.met-toast-radio {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
cursor: pointer;
}
.met-toast-radio input {
width: 16px;
height: 16px;
cursor: pointer;
}
/* Toast输入框 */
.met-toast-input {
width: 100%;
padding: 8px 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
font-size: 13px;
margin-top: 8px;
background: transparent;
color: inherit;
}
.met-theme-dark .met-toast-input {
border-color: rgba(255, 255, 255, 0.1);
}
.met-toast-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
/* Toast文本域 */
.met-toast-textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
font-size: 13px;
margin-top: 8px;
background: transparent;
color: inherit;
resize: vertical;
min-height: 80px;
}
.met-theme-dark .met-toast-textarea {
border-color: rgba(255, 255, 255, 0.1);
}
.met-toast-textarea:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
/* Toast选择框 */
.met-toast-select {
width: 100%;
padding: 8px 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
font-size: 13px;
margin-top: 8px;
background: transparent;
color: inherit;
cursor: pointer;
}
.met-theme-dark .met-toast-select {
border-color: rgba(255, 255, 255, 0.1);
}
.met-toast-select:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
/* Toast下拉菜单 */
.met-toast-dropdown {
position: relative;
display: inline-block;
}
.met-toast-dropdown-content {
display: none;
position: absolute;
background: ${theme.bg};
min-width: 160px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
border-radius: 8px;
z-index: 1;
padding: 8px 0;
margin-top: 4px;
}
.met-theme-dark .met-toast-dropdown-content {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}
.met-toast-dropdown:hover .met-toast-dropdown-content {
display: block;
}
.met-toast-dropdown-item {
padding: 8px 16px;
cursor: pointer;
font-size: 13px;
}
.met-toast-dropdown-item:hover {
background: rgba(0, 0, 0, 0.05);
}
.met-theme-dark .met-toast-dropdown-item:hover {
background: rgba(255, 255, 255, 0.05);
}
/* Toast工具提示 */
.met-toast-tooltip {
position: relative;
display: inline-block;
}
.met-toast-tooltip .met-toast-tooltip-text {
visibility: hidden;
width: 120px;
background: ${theme.bg};
color: ${theme.text};
text-align: center;
border-radius: 6px;
padding: 6px 8px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
font-size: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.met-theme-dark .met-toast-tooltip .met-toast-tooltip-text {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.met-toast-tooltip:hover .met-toast-tooltip-text {
visibility: visible;
opacity: 1;
}
/* Toast弹出框 */
.met-toast-popover {
position: relative;
display: inline-block;
}
.met-toast-popover .met-toast-popover-content {
visibility: hidden;
background: ${theme.bg};
color: ${theme.text};
border-radius: 8px;
padding: 12px 16px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
min-width: 200px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.met-theme-dark .met-toast-popover .met-toast-popover-content {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.met-toast-popover:hover .met-toast-popover-content {
visibility: visible;
opacity: 1;
}
/* Toast模态框 */
.met-toast-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.met-toast-modal-content {
background: ${theme.bg};
color: ${theme.text};
border-radius: 12px;
padding: 24px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
/* Toast抽屉 */
.met-toast-drawer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 10000;
}
.met-toast-drawer-content {
position: absolute;
top: 0;
right: 0;
width: 300px;
height: 100%;
background: ${theme.bg};
color: ${theme.text};
padding: 24px;
transform: translateX(100%);
transition: transform 0.3s ease;
}
.met-toast-drawer.open .met-toast-drawer-content {
transform: translateX(0);
}
/* Toast通知栏 */
.met-toast-notification-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: ${theme.bg};
color: ${theme.text};
padding: 12px 24px;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 9999;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Toast状态栏 */
.met-toast-status-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: ${theme.bg};
color: ${theme.text};
padding: 8px 24px;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 9999;
font-size: 12px;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
}
`;
};
/**
* 注入样式
* @param {Object} theme - 主题配置
*/
export const injectStyles = (theme = THEMES.light) => {
// 检查是否在浏览器环境
if (typeof document === 'undefined') return;
// 检查是否已注入
if (styleElement && document.getElementById('metona-toast-styles')) {
return;
}
// 生成CSS
const css = generateCSS(theme);
// 创建样式元素
styleElement = document.createElement('style');
styleElement.id = 'metona-toast-styles';
styleElement.textContent = css.replace(/\s+/g, ' ');
// 添加到文档头
document.head.appendChild(styleElement);
// 缓存样式
injectedStyles = css;
};
/**
* 更新样式
* @param {Object} theme - 新主题配置
*/
export const updateStyles = (theme) => {
if (!styleElement) {
injectStyles(theme);
return;
}
const css = generateCSS(theme);
styleElement.textContent = css.replace(/\s+/g, ' ');
injectedStyles = css;
};
/**
* 移除样式
*/
export const removeStyles = () => {
if (styleElement && styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
styleElement = null;
injectedStyles = null;
};
/**
* 获取当前样式
* @returns {string|null} 当前CSS字符串
*/
export const getStyles = () => {
return injectedStyles;
};
/**
* 检查样式是否已注入
* @returns {boolean} 是否已注入
*/
export const isStylesInjected = () => {
return !!styleElement && !!document.getElementById('metona-toast-styles');
};
/**
* 重置样式
*/
export const resetStyles = () => {
removeStyles();
injectStyles();
};
/**
* 添加自定义CSS
* @param {string} css - CSS字符串
*/
export const addCustomCSS = (css) => {
if (!styleElement) {
injectStyles();
}
const customStyle = document.createElement('style');
customStyle.id = 'metona-toast-custom-styles';
customStyle.textContent = css;
document.head.appendChild(customStyle);
};
/**
* 移除自定义CSS
*/
export const removeCustomCSS = () => {
const customStyle = document.getElementById('metona-toast-custom-styles');
if (customStyle && customStyle.parentNode) {
customStyle.parentNode.removeChild(customStyle);
}
};
/**
* 生成主题变量
* @param {Object} theme - 主题配置
* @returns {string} CSS变量字符串
*/
export const generateThemeVariables = (theme) => {
return `
:root {
--met-theme-bg: ${theme.bg};
--met-theme-text: ${theme.text};
--met-theme-border: ${theme.border};
--met-theme-shadow: ${theme.shadow};
--met-theme-hover-shadow: ${theme.hoverShadow};
--met-theme-progress-bg: ${theme.progressBg};
--met-theme-close-hover-bg: ${theme.closeHoverBg};
}
`;
};
/**
* 应用主题变量
* @param {Object} theme - 主题配置
*/
export const applyThemeVariables = (theme) => {
const css = generateThemeVariables(theme);
addCustomCSS(css);
};
/**
* 清除主题变量
*/
export const clearThemeVariables = () => {
removeCustomCSS();
};
/**
* 获取系统主题
* @returns {string} 系统主题
*/
export const getSystemTheme = () => {
if (typeof window === 'undefined') return 'light';
return window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
};
/**
* 监听系统主题变化
* @param {Function} callback - 回调函数
* @returns {Function} 取消监听函数
*/
export const watchSystemTheme = (callback) => {
if (typeof window === 'undefined' || !window.matchMedia) {
return () => {};
}
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e) => {
callback(e.matches ? 'dark' : 'light');
};
mediaQuery.addEventListener('change', handler);
return () => {
mediaQuery.removeEventListener('change', handler);
};
};
/**
* 自动应用系统主题
* @returns {Function} 取消监听函数
*/
export const autoApplySystemTheme = () => {
const applyTheme = (theme) => {
updateStyles(THEMES[theme]);
applyThemeVariables(THEMES[theme]);
};
// 应用当前系统主题
applyTheme(getSystemTheme());
// 监听系统主题变化
return watchSystemTheme(applyTheme);
};