feat:组件拆分和性能优化
This commit is contained in:
286
app/components/Common.jsx
Normal file
286
app/components/Common.jsx
Normal file
@@ -0,0 +1,286 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import zhifubaoImg from "../assets/zhifubao.jpg";
|
||||
import weixinImg from "../assets/weixin.jpg";
|
||||
import { CalendarIcon, MinusIcon, PlusIcon } from './Icons';
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.tz.setDefault('Asia/Shanghai');
|
||||
|
||||
const TZ = 'Asia/Shanghai';
|
||||
const nowInTz = () => dayjs().tz(TZ);
|
||||
const toTz = (input) => (input ? dayjs.tz(input, TZ) : nowInTz());
|
||||
const formatDate = (input) => toTz(input).format('YYYY-MM-DD');
|
||||
|
||||
export function DatePicker({ value, onChange }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [currentMonth, setCurrentMonth] = useState(() => value ? toTz(value) : nowInTz());
|
||||
|
||||
useEffect(() => {
|
||||
const close = () => setIsOpen(false);
|
||||
if (isOpen) window.addEventListener('click', close);
|
||||
return () => window.removeEventListener('click', close);
|
||||
}, [isOpen]);
|
||||
|
||||
const year = currentMonth.year();
|
||||
const month = currentMonth.month();
|
||||
|
||||
const handlePrevMonth = (e) => {
|
||||
e.stopPropagation();
|
||||
setCurrentMonth(currentMonth.subtract(1, 'month').startOf('month'));
|
||||
};
|
||||
|
||||
const handleNextMonth = (e) => {
|
||||
e.stopPropagation();
|
||||
setCurrentMonth(currentMonth.add(1, 'month').startOf('month'));
|
||||
};
|
||||
|
||||
const handleSelect = (e, day) => {
|
||||
e.stopPropagation();
|
||||
const dateStr = formatDate(`${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`);
|
||||
|
||||
const today = nowInTz().startOf('day');
|
||||
const selectedDate = toTz(dateStr).startOf('day');
|
||||
|
||||
if (selectedDate.isAfter(today)) return;
|
||||
|
||||
onChange(dateStr);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const daysInMonth = currentMonth.daysInMonth();
|
||||
const firstDayOfWeek = currentMonth.startOf('month').day();
|
||||
|
||||
const days = [];
|
||||
for (let i = 0; i < firstDayOfWeek; i++) days.push(null);
|
||||
for (let i = 1; i <= daysInMonth; i++) days.push(i);
|
||||
|
||||
return (
|
||||
<div className="date-picker" style={{ position: 'relative' }} onClick={(e) => e.stopPropagation()}>
|
||||
<div
|
||||
className="input-trigger"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0 12px',
|
||||
height: '40px',
|
||||
background: 'rgba(0,0,0,0.2)',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
border: '1px solid transparent',
|
||||
transition: 'all 0.2s'
|
||||
}}
|
||||
>
|
||||
<span>{value || '选择日期'}</span>
|
||||
<CalendarIcon width="16" height="16" className="muted" />
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
className="glass card"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
marginTop: 8,
|
||||
padding: 12,
|
||||
zIndex: 10,
|
||||
background: 'rgba(30, 41, 59, 0.95)',
|
||||
backdropFilter: 'blur(12px)',
|
||||
border: '1px solid rgba(255,255,255,0.1)'
|
||||
}}
|
||||
>
|
||||
<div className="calendar-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
||||
<button type="button" onClick={handlePrevMonth} className="icon-button" style={{ width: 24, height: 24 }}><</button>
|
||||
<span style={{ fontWeight: 600 }}>{year}年 {month + 1}月</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleNextMonth}
|
||||
className="icon-button"
|
||||
style={{ width: 24, height: 24 }}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="calendar-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4, textAlign: 'center' }}>
|
||||
{['日', '一', '二', '三', '四', '五', '六'].map(d => (
|
||||
<div key={d} className="muted" style={{ fontSize: '12px', marginBottom: 4 }}>{d}</div>
|
||||
))}
|
||||
{days.map((d, i) => {
|
||||
if (!d) return <div key={i} />;
|
||||
const dateStr = formatDate(`${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`);
|
||||
const isSelected = value === dateStr;
|
||||
const today = nowInTz().startOf('day');
|
||||
const current = toTz(dateStr).startOf('day');
|
||||
const isToday = current.isSame(today);
|
||||
const isFuture = current.isAfter(today);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
onClick={(e) => !isFuture && handleSelect(e, d)}
|
||||
style={{
|
||||
height: 28,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '13px',
|
||||
borderRadius: '6px',
|
||||
cursor: isFuture ? 'not-allowed' : 'pointer',
|
||||
background: isSelected ? 'var(--primary)' : isToday ? 'rgba(255,255,255,0.1)' : 'transparent',
|
||||
color: isFuture ? 'var(--muted)' : isSelected ? '#000' : 'var(--text)',
|
||||
fontWeight: isSelected || isToday ? 600 : 400,
|
||||
opacity: isFuture ? 0.3 : 1
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!isSelected && !isFuture) e.currentTarget.style.background = 'rgba(255,255,255,0.1)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!isSelected && !isFuture) e.currentTarget.style.background = isToday ? 'rgba(255,255,255,0.1)' : 'transparent';
|
||||
}}
|
||||
>
|
||||
{d}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DonateTabs() {
|
||||
const [method, setMethod] = useState('wechat');
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 }}>
|
||||
<div className="tabs glass" style={{ padding: 4, borderRadius: 12, width: '100%', display: 'flex' }}>
|
||||
<button
|
||||
onClick={() => setMethod('alipay')}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '8px 0',
|
||||
border: 'none',
|
||||
background: method === 'alipay' ? 'rgba(34, 211, 238, 0.15)' : 'transparent',
|
||||
color: method === 'alipay' ? 'var(--primary)' : 'var(--muted)',
|
||||
borderRadius: 8,
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontWeight: 600,
|
||||
transition: 'all 0.2s ease'
|
||||
}}
|
||||
>
|
||||
支付宝
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setMethod('wechat')}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '8px 0',
|
||||
border: 'none',
|
||||
background: method === 'wechat' ? 'rgba(34, 211, 238, 0.15)' : 'transparent',
|
||||
color: method === 'wechat' ? 'var(--primary)' : 'var(--muted)',
|
||||
borderRadius: 8,
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontWeight: 600,
|
||||
transition: 'all 0.2s ease'
|
||||
}}
|
||||
>
|
||||
微信支付
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: 200,
|
||||
height: 200,
|
||||
background: 'white',
|
||||
borderRadius: 12,
|
||||
padding: 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
{method === 'alipay' ? (
|
||||
<img
|
||||
src={zhifubaoImg.src}
|
||||
alt="支付宝收款码"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={weixinImg.src}
|
||||
alt="微信收款码"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NumericInput({ value, onChange, step = 1, min = 0, placeholder }) {
|
||||
const decimals = String(step).includes('.') ? String(step).split('.')[1].length : 0;
|
||||
const fmt = (n) => Number(n).toFixed(decimals);
|
||||
const inc = () => {
|
||||
const v = parseFloat(value);
|
||||
const base = isNaN(v) ? 0 : v;
|
||||
const next = base + step;
|
||||
onChange(fmt(next));
|
||||
};
|
||||
const dec = () => {
|
||||
const v = parseFloat(value);
|
||||
const base = isNaN(v) ? 0 : v;
|
||||
const next = Math.max(min, base - step);
|
||||
onChange(fmt(next));
|
||||
};
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
className="input no-zoom"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
style={{ width: '100%', paddingRight: 56 }}
|
||||
/>
|
||||
<div style={{ position: 'absolute', right: 6, top: 6, display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
<button className="icon-button" type="button" onClick={inc} style={{ width: 44, height: 16, padding: 0 }}>
|
||||
<PlusIcon width="14" height="14" />
|
||||
</button>
|
||||
<button className="icon-button" type="button" onClick={dec} style={{ width: 44, height: 16, padding: 0 }}>
|
||||
<MinusIcon width="14" height="14" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Stat({ label, value, delta }) {
|
||||
const dir = delta > 0 ? 'up' : delta < 0 ? 'down' : '';
|
||||
return (
|
||||
<div className="stat" style={{ flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
||||
<span className="label" style={{ fontSize: '11px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{label}</span>
|
||||
<span className={`value ${dir}`} style={{ fontSize: '15px', lineHeight: 1.2, whiteSpace: 'nowrap' }}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
190
app/components/Icons.jsx
Normal file
190
app/components/Icons.jsx
Normal file
@@ -0,0 +1,190 @@
|
||||
'use client';
|
||||
|
||||
export function PlusIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function UpdateIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points="7 10 12 15 17 10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TrashIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M3 6h18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M8 6l1-2h6l1 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M6 6l1 13a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-13" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M10 11v6M14 11v6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function SettingsIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M19.4 15a7.97 7.97 0 0 0 .1-2l2-1.5-2-3.5-2.3.5a8.02 8.02 0 0 0-1.7-1l-.4-2.3h-4l-.4 2.3a8.02 8.02 0 0 0-1.7 1l-2.3-.5-2 3.5 2 1.5a7.97 7.97 0 0 0 .1 2l-2 1.5 2 3.5 2.3-.5a8.02 8.02 0 0 0 1.7 1l.4 2.3h4l.4-2.3a8.02 8.02 0 0 0 1.7-1l2.3.5 2-3.5-2-1.5z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CloudIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M20 17.5a4.5 4.5 0 0 0-1.5-8.77A6 6 0 1 0 6 16.5H18a3.5 3.5 0 0 0 2-6.4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function RefreshIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M4 12a8 8 0 0 1 12.5-6.9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M16 5h3v3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M20 12a8 8 0 0 1-12.5 6.9" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M8 19H5v-3" stroke="currentColor" strokeWidth="2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChevronIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function SortIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M3 7h18M6 12h12M9 17h6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function UserIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<circle cx="12" cy="8" r="4" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M4 20c0-4 4-6 8-6s8 2 8 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogoutIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points="16 17 21 12 16 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<line x1="21" y1="12" x2="9" y2="12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function LoginIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points="10 17 15 12 10 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<line x1="15" y1="12" x2="3" y2="12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function MailIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M22 6l-10 7L2 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function GridIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" strokeWidth="2" />
|
||||
<rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" strokeWidth="2" />
|
||||
<rect x="14" y="14" width="7" height="7" rx="1" stroke="currentColor" strokeWidth="2" />
|
||||
<rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" strokeWidth="2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CloseIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ExitIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ListIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function DragIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M4 8h16M4 12h16M4 16h16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function FolderPlusIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M9 13h6m-3-3v6m-9-4V5a2 2 0 0 1 2-2h4l2 3h6a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function StarIcon({ filled, ...props }) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill={filled ? "var(--accent)" : "none"}>
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="var(--accent)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CalendarIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<line x1="16" y1="2" x2="16" y2="6"></line>
|
||||
<line x1="8" y1="2" x2="8" y2="6"></line>
|
||||
<line x1="3" y1="10" x2="21" y2="10"></line>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function MinusIcon(props) {
|
||||
return (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user