dishui 0.0.81 → 0.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/index.js +6 -6
- package/dist/components/Accordion/index.js.map +1 -1
- package/dist/components/Card/index.d.ts +7 -4
- package/dist/components/Card/index.js +98 -65
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/EmptyState/index.js +4 -4
- package/dist/components/EmptyState/index.js.map +1 -1
- package/dist/components/List/index.js +5 -5
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Modal/index.js +6 -6
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/Page/index.js +1 -1
- package/dist/components/Page/index.js.map +1 -1
- package/dist/components/PageHeader/index.js +6 -6
- package/dist/components/PageHeader/index.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/styles/themeStyles.js +6 -6
- package/dist/styles/themeStyles.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Card/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\nimport { CardMetric, type CardMetricProps } from './Metric';\nexport type { CardMetricProps, CardMetricItem } from './Metric';\n\nexport type CardProps = {\n children: React.ReactNode;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error';\n bordered?: boolean;\n dash?: boolean;\n side?: boolean;\n imageFull?: boolean;\n compact?: boolean;\n hover?: boolean;\n glass?: boolean; // 毛玻璃效果\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n className?: string;\n};\n\nconst Card = ({\n children,\n variant,\n bordered = false,\n dash = false,\n side = false,\n imageFull = false,\n compact = false,\n hover = true, // 默认启用 hover 效果\n glass = true, // 默认启用毛玻璃效果\n size = 'sm',\n className = '',\n}: CardProps) => {\n const baseClass = 'card';\n\n // 默认样式:如果没有指定 variant,则应用毛玻璃效果和阴影\n // 同时添加边界样式 (border + shadow)\n const defaultStyles = glass && !variant ? 'glass dishui-card-glass shadow-lg' : '';\n\n // 如果指定了 variant,使用该 variant 的背景色,并添加阴影\n const variantClass = variant ? `bg-${variant} text-${variant}-content shadow-md` : '';\n\n const borderedClass = bordered ? 'card-bordered' : '';\n const dashClass = dash ? 'card-dash' : '';\n const sideClass = side ? 'card-side' : '';\n const imageFullClass = imageFull ? 'image-full' : '';\n const compactClass = compact ? 'card-compact' : '';\n const hoverClass = hover ? 'hover:shadow-xl transition-shadow duration-300' : '';\n const sizeClass = size ? `card-${size}` : '';\n\n // 宽度策略:\n // 1. 如果用户指定了 w-/max-w-/min-w-,则完全使用用户的设置\n // 2. 否则不设置固定宽度,让 Card 自然适应父容器(Grid 中自动填充,Flex 中根据内容)\n const hasWidthClass = className.includes('w-') || className.match(/max-w-|min-w-/);\n const widthClass = ''; // 不设置默认宽度,让布局决定\n\n const cardClasses = cn(\n baseClass,\n widthClass,\n defaultStyles,\n variantClass,\n borderedClass,\n dashClass,\n sideClass,\n imageFullClass,\n compactClass,\n hoverClass,\n sizeClass,\n className\n );\n\n return (\n <div className={cardClasses}>\n {children}\n </div>\n );\n};\n\n// Card.Title 组件\nexport type CardTitleProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst CardTitle = ({\n children,\n className = '',\n}: CardTitleProps) => {\n const titleClasses = cn('card-title', className);\n\n return (\n <h2 className={titleClasses}>\n {children}\n </h2>\n );\n};\n\n// Card.Body 组件\nexport type CardBodyProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst CardBody = ({\n children,\n className = '',\n}: CardBodyProps) => {\n const bodyClasses = cn('card-body', className);\n\n return (\n <div className={bodyClasses}>\n {children}\n </div>\n );\n};\n\n// Card.Actions 组件\nexport type CardActionsProps = {\n children: React.ReactNode;\n className?: string;\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';\n};\n\nconst CardActions = ({\n children,\n className = '',\n justify = 'end',\n}: CardActionsProps) => {\n const actionsClasses = cn(\n 'card-actions',\n justify && `justify-${justify}`,\n className\n );\n\n return (\n <div className={actionsClasses}>\n {children}\n </div>\n );\n};\n\n// Card.Figure 组件\nexport type CardFigureProps = {\n children: React.ReactNode;\n className?: string;\n /**\n * 图片填充方式(作用于内部 <img>):\n * - `cover` 等比裁切填满(默认,“撑满”)\n * - `contain` 等比完整展示,可能留白\n * - `fill` 拉伸填满(不保持比例)\n */\n fit?: 'cover' | 'contain' | 'fill';\n /** 图片撑满整个 figure(w/h 100%)。card-side 时建议开启。 */\n fill?: boolean;\n /** 指定 figure 高度(数字按 px,支持 \"16rem\"/\"50%\" 等);内部图片自动填满该高度 */\n height?: number | string;\n};\n\nconst FIT_CLASS: Record<NonNullable<CardFigureProps['fit']>, string> = {\n cover: '[&>img]:object-cover',\n contain: '[&>img]:object-contain',\n fill: '[&>img]:object-fill',\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst CardFigure = ({\n children,\n className = '',\n fit = 'cover',\n fill = false,\n height,\n}: CardFigureProps) => {\n const figureClasses = cn(\n 'figure',\n FIT_CLASS[fit],\n // 撑满:figure 与内部 img 都占满可用空间\n fill && 'w-full h-full [&>img]:h-full [&>img]:w-full',\n // 指定高度:figure 定高,内部图片自动填满\n height !== undefined && 'w-full [&>img]:h-full [&>img]:w-full',\n className\n );\n\n return (\n <figure className={figureClasses} style={height !== undefined ? { height: toCssSize(height) } : undefined}>\n {children}\n </figure>\n );\n};\n\n// ─── 内部工具:复用基础 Card 的类名拼装逻辑 ──────────────────\n\ninterface CardClassOptions {\n variant?: CardProps['variant'];\n bordered?: boolean;\n glass?: boolean;\n className?: string;\n}\n\nconst buildCardClasses = ({ variant, bordered = false, glass = true, className = '' }: CardClassOptions) => {\n const defaultStyles = glass && !variant ? 'glass dishui-card-glass shadow-lg' : '';\n const variantClass = variant ? `bg-${variant} text-${variant}-content shadow-md` : '';\n return cn('card', defaultStyles, variantClass, bordered && 'card-bordered', className);\n};\n\nconst ClockGlyph: React.FC<{ className?: string }> = ({ className }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className={cn('h-4 w-4', className)} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path strokeLinecap=\"round\" d=\"M12 7v5l3 2\" />\n </svg>\n);\n\n// ─── Card.Feature:特性卡(图标 + 标题 + 描述) ───────────────\n\nexport type CardFeatureProps = {\n /** Icon node (usually a lucide / inline svg), rendered inside a tinted container. */\n icon?: React.ReactNode;\n /** Feature title. */\n title: React.ReactNode;\n /** Feature description rendered under the title. */\n description?: React.ReactNode;\n /** `vertical` centers everything with the icon on top; `horizontal` puts the icon at the left. */\n layout?: 'vertical' | 'horizontal';\n /** Icon container shape. Defaults to `circle` (vertical) / `square` (horizontal). */\n iconShape?: 'circle' | 'square';\n /** Extra content rendered under the description. */\n children?: React.ReactNode;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardFeature: React.FC<CardFeatureProps> = ({\n icon,\n title,\n description,\n layout = 'vertical',\n iconShape,\n children,\n variant,\n bordered,\n glass = true,\n className = '',\n onClick,\n}) => {\n const shape = iconShape ?? (layout === 'vertical' ? 'circle' : 'square');\n const iconBox = icon && (\n <span\n className={cn(\n 'flex shrink-0 items-center justify-center bg-primary/10 text-primary',\n layout === 'vertical' ? 'size-16' : 'size-12',\n shape === 'circle' ? 'rounded-full' : 'rounded-xl',\n )}\n >\n {icon}\n </span>\n );\n\n return (\n <div\n className={buildCardClasses({ variant, bordered, glass, className })}\n onClick={onClick}\n >\n {layout === 'horizontal' ? (\n <div className=\"card-body flex-row items-center gap-4\">\n {iconBox}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-base-content\">{title}</h3>\n {description && (\n <p className=\"mt-0.5 text-sm text-base-content/70\">{description}</p>\n )}\n {children}\n </div>\n </div>\n ) : (\n <div className=\"card-body items-center gap-3 text-center\">\n {iconBox}\n <h3 className=\"text-lg font-bold text-base-content\">{title}</h3>\n {description && (\n <p className=\"text-sm leading-relaxed text-base-content/70\">{description}</p>\n )}\n {children}\n </div>\n )}\n </div>\n );\n};\n\n// ─── Card.Quote:引用卡(引用文本 + 作者 / 日期 + 装饰引号) ──\n\nexport type CardQuoteProps = {\n /** The quoted text. */\n children: React.ReactNode;\n /** Author name (rendered in primary color). */\n author?: React.ReactNode;\n /** Avatar node (e.g. an `<img>` or initial circle) shown before the author. */\n avatar?: React.ReactNode;\n /** Date string/node rendered at the right side of the footer. */\n date?: React.ReactNode;\n /** Hide the decorative quote glyph in the top-right corner. */\n quoteMark?: boolean;\n /** Hide the divider between quote and footer. */\n divider?: boolean;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardQuote: React.FC<CardQuoteProps> = ({\n children,\n author,\n avatar,\n date,\n quoteMark = true,\n divider = true,\n variant,\n bordered,\n glass = true,\n className = '',\n onClick,\n}) => (\n <div\n className={cn(\n buildCardClasses({ variant, bordered, glass, className }),\n 'relative overflow-hidden',\n )}\n onClick={onClick}\n >\n {quoteMark && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute right-5 top-2 select-none font-serif text-6xl leading-none text-primary/25\"\n >\n ”\n </span>\n )}\n <div className=\"card-body gap-0 p-6\">\n <div className={cn('relative z-10 min-w-0 wrap-anywhere text-lg text-base-content', quoteMark && 'pe-10')}>{children}</div>\n <div\n className={cn(\n 'mt-4 flex flex-wrap items-center justify-between gap-3',\n divider && 'border-t border-base-300 pt-3.5',\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2.5\">\n {avatar && <span className=\"size-8 shrink-0 overflow-hidden rounded-full\">{avatar}</span>}\n {author && (\n <span className=\"truncate text-sm font-medium text-primary\">{author}</span>\n )}\n </div>\n {date && (\n <span className=\"flex shrink-0 items-center gap-1.5 text-sm text-base-content/55\">\n <ClockGlyph />\n {date}\n </span>\n )}\n </div>\n </div>\n </div>\n);\n\n// ─── Card.Meta:元信息卡(标题 + 徽章 + 描述 + 左右页脚) ────\n\nexport type CardMetaProps = {\n /** Card title (top-left). */\n title: React.ReactNode;\n /** Badge / tag node (top-right, e.g. a visibility badge). */\n badge?: React.ReactNode;\n /** Body content under the title row. */\n children?: React.ReactNode;\n /** Footer left slot (e.g. `@author`). */\n footerLeft?: React.ReactNode;\n /** Footer right slot (e.g. a date). */\n footerRight?: React.ReactNode;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardMeta: React.FC<CardMetaProps> = ({\n title,\n badge,\n children,\n footerLeft,\n footerRight,\n variant,\n bordered,\n glass = true,\n className = '',\n onClick,\n}) => (\n <div\n className={buildCardClasses({ variant, bordered, glass, className })}\n onClick={onClick}\n >\n <div className=\"card-body gap-2.5 p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 className=\"min-w-0 flex-1 truncate text-lg font-bold text-base-content\">{title}</h3>\n {badge && <span className=\"shrink-0\">{badge}</span>}\n </div>\n {children && <div className=\"text-sm text-base-content/75\">{children}</div>}\n {(footerLeft || footerRight) && (\n <div className=\"mt-1.5 flex items-center justify-between gap-3 text-sm text-base-content/55\">\n <span className=\"min-w-0 truncate\">{footerLeft}</span>\n <span className=\"shrink-0\">{footerRight}</span>\n </div>\n )}\n </div>\n </div>\n);\n\n// ─── Card.Info:指标卡(标签 + 数值 + 可选图标 / hint / 加载态) ──\n\nexport type CardInfoTone = 'primary' | 'success' | 'warning' | 'info' | 'error' | 'neutral';\n\nimport './Info.css';\n\nexport type CardInfoProps = {\n /** Metric name. */\n label: React.ReactNode;\n /** Metric value (number or short text). */\n value: React.ReactNode;\n /** Icon slot. Use Icon to control its size, color and background independently. */\n icon?: React.ReactNode;\n /** `top` keeps the icon beside the label; `left` / `right` flank the content. */\n iconPosition?: 'top' | 'left' | 'right';\n /** Stacked content (default), compact horizontal row, or two-column summary. */\n layout?: 'stacked' | 'horizontal' | 'split';\n /** Content alignment (default left). */\n align?: 'left' | 'center' | 'right';\n /** Additional multi-line content below the summary. */\n children?: React.ReactNode;\n /** Footer slot separated by a subtle divider. */\n footer?: React.ReactNode;\n /** @deprecated Use Icon styles instead. Shape of the optional neutral compatibility box. */\n iconShape?: 'square' | 'circle';\n /** @deprecated Use Icon styles instead. Optional neutral icon box (default false). */\n iconBoxed?: boolean;\n /** Card surface and border tone (default neutral). Does not style the icon. */\n tone?: CardInfoTone;\n /** Small hint text below the value (e.g. deltas, update time). */\n hint?: React.ReactNode;\n /** Loading state: value renders as a skeleton. */\n loading?: boolean;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border (default true). */\n bordered?: boolean;\n /** Enable the glass background (default true, matching Card). */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\n\nconst CardInfo: React.FC<CardInfoProps> = ({\n label,\n value,\n icon,\n iconPosition = 'top',\n iconShape = 'square',\n iconBoxed = false,\n tone = 'neutral',\n layout = 'stacked',\n align = 'left',\n children,\n footer,\n hint,\n loading = false,\n variant,\n bordered = true,\n glass = true,\n className = '',\n onClick,\n}) => {\n const cardClasses = buildCardClasses({\n variant, bordered, glass,\n className: cn('blade-card-info', 'p-4', className),\n });\n const iconNode = icon != null && (\n <span className={cn(\n 'blade-card-info__icon',\n iconBoxed && (iconShape === 'circle' ? 'rounded-full' : 'rounded-lg'),\n iconBoxed && 'bg-base-content/5',\n )}>\n {icon}\n </span>\n );\n\n return (\n <div\n className={cardClasses}\n data-layout={layout}\n data-align={align}\n data-tone={variant ? undefined : tone}\n data-bordered={bordered}\n data-glass={glass}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n aria-busy={loading || undefined}\n onClick={onClick}\n onKeyDown={onClick ? (event) => {\n // Let controls inside content/footer handle their own keyboard events.\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (!event.repeat) event.currentTarget.click();\n }\n } : undefined}\n >\n <div className=\"blade-card-info__body\">\n {iconPosition === 'left' && iconNode}\n <div className=\"blade-card-info__content\">\n <div className=\"blade-card-info__summary\">\n <div className=\"blade-card-info__header\">\n <div className=\"blade-card-info__label\">{label}</div>\n {iconPosition === 'top' && iconNode}\n </div>\n <div className=\"blade-card-info__reading\">\n <div className=\"blade-card-info__value\">\n {loading ? <span className=\"blade-card-info__skeleton\" aria-hidden=\"true\" /> : value}\n </div>\n {hint != null && <div className=\"blade-card-info__hint\">{hint}</div>}\n </div>\n </div>\n {children != null && <div className=\"blade-card-info__details\">{children}</div>}\n </div>\n {iconPosition === 'right' && iconNode}\n </div>\n {footer != null && <div className=\"blade-card-info__footer\">{footer}</div>}\n </div>\n );\n};\n\n// ─── Card.Stat:统计面板(Grafana 风格:超大数值 + 迷你图 / 角标 / 填充色) ───\n\nexport type CardStatProps = {\n /** 面板标签 */\n label: React.ReactNode;\n /** 面板数值(数字或短文本) */\n value: React.ReactNode;\n /** 色调(决定数值文字色 / 角标三角 / filled 底色) */\n tone?: CardInfoTone;\n /** 加载态:值显示为骨架 */\n loading?: boolean;\n className?: string;\n /** 数值单位后缀(如 `K`、`%`),以小号字体渲染在数值右侧。 */\n unit?: React.ReactNode;\n /** 面板数值字号,默认 `xl`。 */\n valueSize?: 'md' | 'lg' | 'xl' | '2xl';\n /** 面板内容对齐,默认 `left`。 */\n align?: 'left' | 'center';\n /** 直接传入迷你图节点(优先于 sparklineData)。 */\n sparkline?: React.ReactNode;\n /** 迷你图数据:传入数值数组后自动渲染 sparkline。 */\n sparklineData?: number[];\n /** 迷你图类型:折线 / 面积 / 柱状。 */\n sparklineType?: 'line' | 'area' | 'bar';\n /** 迷你图高度(px)。 */\n sparklineHeight?: number;\n /** 右上角状态三角(Grafana 阈值指示)。 */\n indicator?: boolean;\n /** 整卡填充 tone 底色、文字用对应 content 色(图 RabbitMQ 风格)。 */\n filled?: boolean;\n /** 点击回调(面板常用于跳转详情)。 */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst STAT_TONE_TEXT_CLASS: Record<NonNullable<CardStatProps['tone']>, string> = {\n primary: 'text-primary',\n success: 'text-success',\n warning: 'text-warning',\n info: 'text-info',\n error: 'text-error',\n neutral: 'text-base-content/70',\n};\n\n/** filled 模式:整卡 tone 底色 + 对应 content 文字色 */\nconst STAT_FILLED_CLASS: Record<NonNullable<CardStatProps['tone']>, string> = {\n primary: 'bg-primary text-primary-content',\n success: 'bg-success text-success-content',\n warning: 'bg-warning text-warning-content',\n info: 'bg-info text-info-content',\n error: 'bg-error text-error-content',\n neutral: 'bg-neutral text-neutral-content',\n};\n\nconst STAT_VALUE_SIZE_CLASS: Record<NonNullable<CardStatProps['valueSize']> & string, string> = {\n md: 'text-2xl',\n lg: 'text-4xl',\n xl: 'text-5xl',\n '2xl': 'text-6xl',\n};\n\nconst statCssVarColor = (tone: NonNullable<CardStatProps['tone']>) =>\n tone === 'neutral' ? 'var(--color-base-content)' : `var(--color-${tone})`;\n\n/** 由数值数组生成迷你图(viewBox 拉伸铺满,描边不随宽度变形) */\nconst MiniSparkline: React.FC<{\n data: number[];\n type: 'line' | 'area' | 'bar';\n height: number;\n}> = ({ data, type, height }) => {\n if (!data || data.length < 2) return null;\n const w = 100;\n const pad = 3;\n const min = Math.min(...data);\n const max = Math.max(...data);\n const range = max - min || 1;\n const px = (i: number) => (i / (data.length - 1)) * w;\n const py = (v: number) => height - pad - ((v - min) / range) * (height - pad * 2);\n const points = data.map((v, i) => `${px(i).toFixed(2)},${py(v).toFixed(2)}`);\n\n if (type === 'bar') {\n const bw = w / data.length;\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n {data.map((v, i) => (\n <rect\n key={i}\n x={px(i) - bw / 2}\n y={py(v)}\n width={bw * 0.8}\n height={Math.max(1, height - pad - py(v))}\n fill=\"currentColor\"\n />\n ))}\n </svg>\n );\n }\n\n if (type === 'area') {\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n <path\n d={`M0,${height} L${points.join(' L')} L${w},${height} Z`}\n fill=\"currentColor\"\n opacity={0.18}\n />\n <polyline\n points={points.join(' ')}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n );\n }\n\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n <polyline\n points={points.join(' ')}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n );\n};\n\n/**\n * 统计面板:Grafana stat panel 风格的「标签 + 超大数值 + 迷你图」小卡,\n * 支持单位后缀、折线 / 面积 / 柱状迷你图、角标状态三角与整卡填充色。\n *\n * 简单的「标签 + 数值 + 图标」指标卡请使用 `Card.Info`。\n */\nconst CardStat: React.FC<CardStatProps> = ({\n label,\n value,\n tone = 'primary',\n loading = false,\n className = '',\n unit,\n valueSize = 'xl',\n align = 'left',\n sparkline,\n sparklineData,\n sparklineType = 'line',\n sparklineHeight = 36,\n indicator = false,\n filled = false,\n onClick,\n}) => {\n const sparklineNode =\n sparkline ??\n (sparklineData && sparklineData.length >= 2 ? (\n <MiniSparkline data={sparklineData} type={sparklineType} height={sparklineHeight} />\n ) : null);\n\n const textColor = filled ? '' : STAT_TONE_TEXT_CLASS[tone];\n const mutedText = filled ? 'opacity-80' : 'text-base-content/60';\n\n return (\n <div\n onClick={onClick}\n className={`relative flex flex-col overflow-hidden rounded-lg p-3 ${\n filled ? STAT_FILLED_CLASS[tone] : 'border border-base-300/60 bg-base-200'\n } ${onClick ? 'cursor-pointer transition-colors hover:border-primary/40' : ''} ${className}`.trim()}\n >\n {indicator && !filled && (\n <span\n aria-hidden=\"true\"\n className=\"absolute right-0 top-0 h-0 w-0\"\n style={{\n borderTop: '14px solid ' + statCssVarColor(tone),\n borderLeft: '14px solid transparent',\n }}\n />\n )}\n <div\n className={`truncate text-xs font-medium ${mutedText} ${align === 'center' ? 'text-center' : ''}`.trim()}\n >\n {label}\n </div>\n <div\n className={`flex min-h-[3rem] flex-1 items-center py-1 ${align === 'center' ? 'justify-center' : 'justify-start'}`}\n >\n {loading ? (\n <span\n className={`inline-block h-8 w-16 animate-pulse rounded ${\n filled ? 'bg-white/25' : 'bg-base-content/10'\n }`}\n />\n ) : (\n <span className={`leading-none tracking-tight ${STAT_VALUE_SIZE_CLASS[valueSize]} font-semibold ${filled ? '' : textColor}`.trim()}>\n {value}\n {unit !== undefined && unit !== null && unit !== '' && (\n <span className=\"ml-0.5 text-[0.45em] font-semibold\">{unit}</span>\n )}\n </span>\n )}\n </div>\n {sparklineNode && (\n <div className={`-mx-3 -mb-3 mt-1 ${filled ? 'text-white' : STAT_TONE_TEXT_CLASS[tone]}`}>\n {sparklineNode}\n </div>\n )}\n </div>\n );\n};\n\n// 扩展Card组件,添加子组件\ninterface CardCompound extends React.FC<CardProps> {\n Metric: React.FC<CardMetricProps>;\n Title: React.FC<CardTitleProps>;\n Body: React.FC<CardBodyProps>;\n Actions: React.FC<CardActionsProps>;\n Figure: React.FC<CardFigureProps>;\n /** 特性卡:图标 + 标题 + 描述(垂直居中 / 横向两种布局) */\n Feature: React.FC<CardFeatureProps>;\n /** 引用卡:引用文本 + 作者头像 / 日期 + 装饰引号 */\n Quote: React.FC<CardQuoteProps>;\n /** 元信息卡:标题 + 徽章 + 描述 + 左右页脚 */\n Meta: React.FC<CardMetaProps>;\n /** 指标卡:标签 + 数值 + 可选图标 / hint / 加载态(仪表盘统计卡片) */\n Info: React.FC<CardInfoProps>;\n /** 统计面板:Grafana 风格超大数值 + 迷你折线 / 面积 / 柱状图 / 角标 / 填充色 */\n Stat: React.FC<CardStatProps>;\n}\n\nconst CardWithCompounds = Card as CardCompound;\nCardWithCompounds.Title = CardTitle;\nCardWithCompounds.Body = CardBody;\nCardWithCompounds.Actions = CardActions;\nCardWithCompounds.Figure = CardFigure;\nCardWithCompounds.Feature = CardFeature;\nCardWithCompounds.Quote = CardQuote;\nCardWithCompounds.Meta = CardMeta;\nCardWithCompounds.Info = CardInfo;\nCardWithCompounds.Stat = CardStat;\nCardWithCompounds.Metric = CardMetric;\n\nexport default CardWithCompounds; "],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Card/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\nimport { CardMetric, type CardMetricProps } from './Metric';\nexport type { CardMetricProps, CardMetricItem } from './Metric';\n\nexport type CardProps = {\n children: React.ReactNode;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error';\n bordered?: boolean;\n dash?: boolean;\n side?: boolean;\n imageFull?: boolean;\n compact?: boolean;\n hover?: boolean;\n glass?: boolean; // 毛玻璃效果\n /** Background opacity only (0..1, default 0.15). Non-finite values use the default. */\n glassOpacity?: number;\n style?: React.CSSProperties;\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n className?: string;\n};\n\nconst Card = ({\n children,\n variant,\n bordered = false,\n dash = false,\n side = false,\n imageFull = false,\n compact = false,\n hover = true, // 默认启用 hover 效果\n glass = true, // 默认启用毛玻璃效果\n glassOpacity,\n style,\n size = 'sm',\n className = '',\n}: CardProps) => {\n const baseClass = 'card';\n\n const defaultStyles = glass && !variant ? 'dishui-card-glass' : '';\n\n // 如果指定了 variant,使用该 variant 的背景色,并添加阴影\n const variantClass = variant ? `bg-${variant} text-${variant}-content shadow-md` : '';\n\n const borderedClass = bordered ? 'card-bordered' : '';\n const dashClass = dash ? 'card-dash' : '';\n const sideClass = side ? 'card-side' : '';\n const imageFullClass = imageFull ? 'image-full' : '';\n const compactClass = compact ? 'card-compact' : '';\n const hoverClass = hover ? 'hover:shadow-md transition-shadow duration-200 motion-reduce:transition-none' : '';\n const sizeClass = size ? `card-${size}` : '';\n\n // 宽度策略:\n // 1. 如果用户指定了 w-/max-w-/min-w-,则完全使用用户的设置\n // 2. 否则不设置固定宽度,让 Card 自然适应父容器(Grid 中自动填充,Flex 中根据内容)\n const hasWidthClass = className.includes('w-') || className.match(/max-w-|min-w-/);\n const widthClass = ''; // 不设置默认宽度,让布局决定\n\n const cardClasses = cn(\n baseClass,\n widthClass,\n defaultStyles,\n variantClass,\n borderedClass,\n dashClass,\n sideClass,\n imageFullClass,\n compactClass,\n hoverClass,\n sizeClass,\n className\n );\n\n return (\n <div className={cardClasses} style={buildCardStyle({ variant, glass, glassOpacity, style })}>\n {children}\n </div>\n );\n};\n\n// Card.Title 组件\nexport type CardTitleProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst CardTitle = ({\n children,\n className = '',\n}: CardTitleProps) => {\n const titleClasses = cn('card-title', className);\n\n return (\n <h2 className={titleClasses}>\n {children}\n </h2>\n );\n};\n\n// Card.Body 组件\nexport type CardBodyProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst CardBody = ({\n children,\n className = '',\n}: CardBodyProps) => {\n const bodyClasses = cn('card-body', className);\n\n return (\n <div className={bodyClasses}>\n {children}\n </div>\n );\n};\n\n// Card.Actions 组件\nexport type CardActionsProps = {\n children: React.ReactNode;\n className?: string;\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';\n};\n\nconst CardActions = ({\n children,\n className = '',\n justify = 'end',\n}: CardActionsProps) => {\n const actionsClasses = cn(\n 'card-actions',\n justify && `justify-${justify}`,\n className\n );\n\n return (\n <div className={actionsClasses}>\n {children}\n </div>\n );\n};\n\n// Card.Figure 组件\nexport type CardFigureProps = {\n children: React.ReactNode;\n className?: string;\n /**\n * 图片填充方式(作用于内部 <img>):\n * - `cover` 等比裁切填满(默认,“撑满”)\n * - `contain` 等比完整展示,可能留白\n * - `fill` 拉伸填满(不保持比例)\n */\n fit?: 'cover' | 'contain' | 'fill';\n /** 图片撑满整个 figure(w/h 100%)。card-side 时建议开启。 */\n fill?: boolean;\n /** 指定 figure 高度(数字按 px,支持 \"16rem\"/\"50%\" 等);内部图片自动填满该高度 */\n height?: number | string;\n};\n\nconst FIT_CLASS: Record<NonNullable<CardFigureProps['fit']>, string> = {\n cover: '[&>img]:object-cover',\n contain: '[&>img]:object-contain',\n fill: '[&>img]:object-fill',\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst CardFigure = ({\n children,\n className = '',\n fit = 'cover',\n fill = false,\n height,\n}: CardFigureProps) => {\n const figureClasses = cn(\n 'figure',\n FIT_CLASS[fit],\n // 撑满:figure 与内部 img 都占满可用空间\n fill && 'w-full h-full [&>img]:h-full [&>img]:w-full',\n // 指定高度:figure 定高,内部图片自动填满\n height !== undefined && 'w-full [&>img]:h-full [&>img]:w-full',\n className\n );\n\n return (\n <figure className={figureClasses} style={height !== undefined ? { height: toCssSize(height) } : undefined}>\n {children}\n </figure>\n );\n};\n\n// ─── 内部工具:复用基础 Card 的类名拼装逻辑 ──────────────────\n\ninterface CardClassOptions {\n variant?: CardProps['variant'];\n bordered?: boolean;\n glass?: boolean;\n className?: string;\n}\n\nconst buildCardClasses = ({ variant, bordered = false, glass = true, className = '' }: CardClassOptions) => {\n const defaultStyles = glass && !variant ? 'dishui-card-glass' : '';\n const variantClass = variant ? `bg-${variant} text-${variant}-content shadow-md` : '';\n return cn('card', defaultStyles, variantClass, bordered && 'card-bordered', className);\n};\n\nconst buildCardStyle = ({\n variant,\n glass = true,\n glassOpacity = 0.15,\n style,\n}: Pick<CardProps, 'variant' | 'glass' | 'glassOpacity' | 'style'>): React.CSSProperties | undefined => {\n if (!glass || variant) return style;\n const opacity = Number.isFinite(glassOpacity) ? Math.min(1, Math.max(0, glassOpacity)) : 0.15;\n return { '--dishui-card-glass-opacity': `${opacity * 100}%`, ...style } as React.CSSProperties;\n};\n\nconst ClockGlyph: React.FC<{ className?: string }> = ({ className }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className={cn('h-4 w-4', className)} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path strokeLinecap=\"round\" d=\"M12 7v5l3 2\" />\n </svg>\n);\n\n// ─── Card.Feature:特性卡(图标 + 标题 + 描述) ───────────────\n\nexport type CardFeatureProps = Pick<CardProps, 'glassOpacity' | 'style'> & {\n /** Icon node (usually a lucide / inline svg), rendered inside a tinted container. */\n icon?: React.ReactNode;\n /** Feature title. */\n title: React.ReactNode;\n /** Feature description rendered under the title. */\n description?: React.ReactNode;\n /** `vertical` centers everything with the icon on top; `horizontal` puts the icon at the left. */\n layout?: 'vertical' | 'horizontal';\n /** Icon container shape. Defaults to `circle` (vertical) / `square` (horizontal). */\n iconShape?: 'circle' | 'square';\n /** Extra content rendered under the description. */\n children?: React.ReactNode;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardFeature: React.FC<CardFeatureProps> = ({\n icon,\n title,\n description,\n layout = 'vertical',\n iconShape,\n children,\n variant,\n bordered,\n glass = true,\n glassOpacity,\n style,\n className = '',\n onClick,\n}) => {\n const shape = iconShape ?? (layout === 'vertical' ? 'circle' : 'square');\n const iconBox = icon && (\n <span\n className={cn(\n 'flex shrink-0 items-center justify-center bg-primary/10 text-primary',\n layout === 'vertical' ? 'size-16' : 'size-12',\n shape === 'circle' ? 'rounded-full' : 'rounded-xl',\n )}\n >\n {icon}\n </span>\n );\n\n return (\n <div\n className={buildCardClasses({ variant, bordered, glass, className })}\n style={buildCardStyle({ variant, glass, glassOpacity, style })}\n onClick={onClick}\n >\n {layout === 'horizontal' ? (\n <div className=\"card-body flex-row items-center gap-4\">\n {iconBox}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-base-content\">{title}</h3>\n {description && (\n <p className=\"mt-0.5 text-sm text-base-content/70\">{description}</p>\n )}\n {children}\n </div>\n </div>\n ) : (\n <div className=\"card-body items-center gap-3 text-center\">\n {iconBox}\n <h3 className=\"text-lg font-bold text-base-content\">{title}</h3>\n {description && (\n <p className=\"text-sm leading-relaxed text-base-content/70\">{description}</p>\n )}\n {children}\n </div>\n )}\n </div>\n );\n};\n\n// ─── Card.Quote:引用卡(引用文本 + 作者 / 日期 + 装饰引号) ──\n\nexport type CardQuoteProps = Pick<CardProps, 'glassOpacity' | 'style'> & {\n /** The quoted text. */\n children: React.ReactNode;\n /** Author name (rendered in primary color). */\n author?: React.ReactNode;\n /** Avatar node (e.g. an `<img>` or initial circle) shown before the author. */\n avatar?: React.ReactNode;\n /** Date string/node rendered at the right side of the footer. */\n date?: React.ReactNode;\n /** Hide the decorative quote glyph in the top-right corner. */\n quoteMark?: boolean;\n /** Hide the divider between quote and footer. */\n divider?: boolean;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardQuote: React.FC<CardQuoteProps> = ({\n children,\n author,\n avatar,\n date,\n quoteMark = true,\n divider = true,\n variant,\n bordered,\n glass = true,\n glassOpacity,\n style,\n className = '',\n onClick,\n}) => (\n <div\n className={cn(\n buildCardClasses({ variant, bordered, glass, className }),\n 'relative overflow-hidden',\n )}\n style={buildCardStyle({ variant, glass, glassOpacity, style })}\n onClick={onClick}\n >\n {quoteMark && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute right-5 top-2 select-none font-serif text-6xl leading-none text-primary/25\"\n >\n ”\n </span>\n )}\n <div className=\"card-body gap-0 p-6\">\n <div className={cn('relative z-10 min-w-0 wrap-anywhere text-lg text-base-content', quoteMark && 'pe-10')}>{children}</div>\n <div\n className={cn(\n 'mt-4 flex flex-wrap items-center justify-between gap-3',\n divider && 'border-t border-base-300 pt-3.5',\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2.5\">\n {avatar && <span className=\"size-8 shrink-0 overflow-hidden rounded-full\">{avatar}</span>}\n {author && (\n <span className=\"truncate text-sm font-medium text-primary\">{author}</span>\n )}\n </div>\n {date && (\n <span className=\"flex shrink-0 items-center gap-1.5 text-sm text-base-content/55\">\n <ClockGlyph />\n {date}\n </span>\n )}\n </div>\n </div>\n </div>\n);\n\n// ─── Card.Meta:元信息卡(标题 + 徽章 + 描述 + 左右页脚) ────\n\nexport type CardMetaProps = Pick<CardProps, 'glassOpacity' | 'style'> & {\n /** Card title (top-left). */\n title: React.ReactNode;\n /** Badge / tag node (top-right, e.g. a visibility badge). */\n badge?: React.ReactNode;\n /** Body content under the title row. */\n children?: React.ReactNode;\n /** Footer left slot (e.g. `@author`). */\n footerLeft?: React.ReactNode;\n /** Footer right slot (e.g. a date). */\n footerRight?: React.ReactNode;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border. */\n bordered?: boolean;\n /** Disable the default glass background. */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst CardMeta: React.FC<CardMetaProps> = ({\n title,\n badge,\n children,\n footerLeft,\n footerRight,\n variant,\n bordered,\n glass = true,\n glassOpacity,\n style,\n className = '',\n onClick,\n}) => (\n <div\n className={buildCardClasses({ variant, bordered, glass, className })}\n style={buildCardStyle({ variant, glass, glassOpacity, style })}\n onClick={onClick}\n >\n <div className=\"card-body gap-2.5 p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 className=\"min-w-0 flex-1 truncate text-lg font-bold text-base-content\">{title}</h3>\n {badge && <span className=\"shrink-0\">{badge}</span>}\n </div>\n {children && <div className=\"text-sm text-base-content/75\">{children}</div>}\n {(footerLeft || footerRight) && (\n <div className=\"mt-1.5 flex items-center justify-between gap-3 text-sm text-base-content/55\">\n <span className=\"min-w-0 truncate\">{footerLeft}</span>\n <span className=\"shrink-0\">{footerRight}</span>\n </div>\n )}\n </div>\n </div>\n);\n\n// ─── Card.Info:指标卡(标签 + 数值 + 可选图标 / hint / 加载态) ──\n\nexport type CardInfoTone = 'primary' | 'success' | 'warning' | 'info' | 'error' | 'neutral';\n\nimport './Info.css';\n\nexport type CardInfoProps = Pick<CardProps, 'glassOpacity' | 'style'> & {\n /** Metric name. */\n label: React.ReactNode;\n /** Metric value (number or short text). */\n value: React.ReactNode;\n /** Icon slot. Use Icon to control its size, color and background independently. */\n icon?: React.ReactNode;\n /** `top` keeps the icon beside the label; `left` / `right` flank the content. */\n iconPosition?: 'top' | 'left' | 'right';\n /** Stacked content (default), compact horizontal row, or two-column summary. */\n layout?: 'stacked' | 'horizontal' | 'split';\n /** Content alignment (default left). */\n align?: 'left' | 'center' | 'right';\n /** Additional multi-line content below the summary. */\n children?: React.ReactNode;\n /** Footer slot separated by a subtle divider. */\n footer?: React.ReactNode;\n /** @deprecated Use Icon styles instead. Shape of the optional neutral compatibility box. */\n iconShape?: 'square' | 'circle';\n /** @deprecated Use Icon styles instead. Optional neutral icon box (default false). */\n iconBoxed?: boolean;\n /** Card surface and border tone (default neutral). Does not style the icon. */\n tone?: CardInfoTone;\n /** Small hint text below the value (e.g. deltas, update time). */\n hint?: React.ReactNode;\n /** Loading state: value renders as a skeleton. */\n loading?: boolean;\n /** Color variant (daisyUI). */\n variant?: CardProps['variant'];\n /** Show a border (default true). */\n bordered?: boolean;\n /** Enable the glass background (default true, matching Card). */\n glass?: boolean;\n /** Extra class names on the card. */\n className?: string;\n /** Click handler. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\n\nconst CardInfo: React.FC<CardInfoProps> = ({\n label,\n value,\n icon,\n iconPosition = 'top',\n iconShape = 'square',\n iconBoxed = false,\n tone = 'neutral',\n layout = 'stacked',\n align = 'left',\n children,\n footer,\n hint,\n loading = false,\n variant,\n bordered = true,\n glass = true,\n glassOpacity,\n style,\n className = '',\n onClick,\n}) => {\n const cardClasses = buildCardClasses({\n variant, bordered, glass,\n className: cn('blade-card-info', 'p-4', className),\n });\n const iconNode = icon != null && (\n <span className={cn(\n 'blade-card-info__icon',\n iconBoxed && (iconShape === 'circle' ? 'rounded-full' : 'rounded-lg'),\n iconBoxed && 'bg-base-content/5',\n )}>\n {icon}\n </span>\n );\n\n return (\n <div\n className={cardClasses}\n style={buildCardStyle({ variant, glass, glassOpacity, style })}\n data-layout={layout}\n data-align={align}\n data-tone={variant ? undefined : tone}\n data-bordered={bordered}\n data-glass={glass}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n aria-busy={loading || undefined}\n onClick={onClick}\n onKeyDown={onClick ? (event) => {\n // Let controls inside content/footer handle their own keyboard events.\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (!event.repeat) event.currentTarget.click();\n }\n } : undefined}\n >\n <div className=\"blade-card-info__body\">\n {iconPosition === 'left' && iconNode}\n <div className=\"blade-card-info__content\">\n <div className=\"blade-card-info__summary\">\n <div className=\"blade-card-info__header\">\n <div className=\"blade-card-info__label\">{label}</div>\n {iconPosition === 'top' && iconNode}\n </div>\n <div className=\"blade-card-info__reading\">\n <div className=\"blade-card-info__value\">\n {loading ? <span className=\"blade-card-info__skeleton\" aria-hidden=\"true\" /> : value}\n </div>\n {hint != null && <div className=\"blade-card-info__hint\">{hint}</div>}\n </div>\n </div>\n {children != null && <div className=\"blade-card-info__details\">{children}</div>}\n </div>\n {iconPosition === 'right' && iconNode}\n </div>\n {footer != null && <div className=\"blade-card-info__footer\">{footer}</div>}\n </div>\n );\n};\n\n// ─── Card.Stat:统计面板(Grafana 风格:超大数值 + 迷你图 / 角标 / 填充色) ───\n\nexport type CardStatProps = {\n /** 面板标签 */\n label: React.ReactNode;\n /** 面板数值(数字或短文本) */\n value: React.ReactNode;\n /** 色调(决定数值文字色 / 角标三角 / filled 底色) */\n tone?: CardInfoTone;\n /** 加载态:值显示为骨架 */\n loading?: boolean;\n className?: string;\n /** 数值单位后缀(如 `K`、`%`),以小号字体渲染在数值右侧。 */\n unit?: React.ReactNode;\n /** 面板数值字号,默认 `xl`。 */\n valueSize?: 'md' | 'lg' | 'xl' | '2xl';\n /** 面板内容对齐,默认 `left`。 */\n align?: 'left' | 'center';\n /** 直接传入迷你图节点(优先于 sparklineData)。 */\n sparkline?: React.ReactNode;\n /** 迷你图数据:传入数值数组后自动渲染 sparkline。 */\n sparklineData?: number[];\n /** 迷你图类型:折线 / 面积 / 柱状。 */\n sparklineType?: 'line' | 'area' | 'bar';\n /** 迷你图高度(px)。 */\n sparklineHeight?: number;\n /** 右上角状态三角(Grafana 阈值指示)。 */\n indicator?: boolean;\n /** 整卡填充 tone 底色、文字用对应 content 色(图 RabbitMQ 风格)。 */\n filled?: boolean;\n /** 点击回调(面板常用于跳转详情)。 */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\nconst STAT_TONE_TEXT_CLASS: Record<NonNullable<CardStatProps['tone']>, string> = {\n primary: 'text-primary',\n success: 'text-success',\n warning: 'text-warning',\n info: 'text-info',\n error: 'text-error',\n neutral: 'text-base-content/70',\n};\n\n/** filled 模式:整卡 tone 底色 + 对应 content 文字色 */\nconst STAT_FILLED_CLASS: Record<NonNullable<CardStatProps['tone']>, string> = {\n primary: 'bg-primary text-primary-content',\n success: 'bg-success text-success-content',\n warning: 'bg-warning text-warning-content',\n info: 'bg-info text-info-content',\n error: 'bg-error text-error-content',\n neutral: 'bg-neutral text-neutral-content',\n};\n\nconst STAT_VALUE_SIZE_CLASS: Record<NonNullable<CardStatProps['valueSize']> & string, string> = {\n md: 'text-2xl',\n lg: 'text-4xl',\n xl: 'text-5xl',\n '2xl': 'text-6xl',\n};\n\nconst statCssVarColor = (tone: NonNullable<CardStatProps['tone']>) =>\n tone === 'neutral' ? 'var(--color-base-content)' : `var(--color-${tone})`;\n\n/** 由数值数组生成迷你图(viewBox 拉伸铺满,描边不随宽度变形) */\nconst MiniSparkline: React.FC<{\n data: number[];\n type: 'line' | 'area' | 'bar';\n height: number;\n}> = ({ data, type, height }) => {\n if (!data || data.length < 2) return null;\n const w = 100;\n const pad = 3;\n const min = Math.min(...data);\n const max = Math.max(...data);\n const range = max - min || 1;\n const px = (i: number) => (i / (data.length - 1)) * w;\n const py = (v: number) => height - pad - ((v - min) / range) * (height - pad * 2);\n const points = data.map((v, i) => `${px(i).toFixed(2)},${py(v).toFixed(2)}`);\n\n if (type === 'bar') {\n const bw = w / data.length;\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n {data.map((v, i) => (\n <rect\n key={i}\n x={px(i) - bw / 2}\n y={py(v)}\n width={bw * 0.8}\n height={Math.max(1, height - pad - py(v))}\n fill=\"currentColor\"\n />\n ))}\n </svg>\n );\n }\n\n if (type === 'area') {\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n <path\n d={`M0,${height} L${points.join(' L')} L${w},${height} Z`}\n fill=\"currentColor\"\n opacity={0.18}\n />\n <polyline\n points={points.join(' ')}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n );\n }\n\n return (\n <svg\n viewBox={`0 0 ${w} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"block w-full\"\n style={{ height }}\n aria-hidden=\"true\"\n >\n <polyline\n points={points.join(' ')}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n );\n};\n\n/**\n * 统计面板:Grafana stat panel 风格的「标签 + 超大数值 + 迷你图」小卡,\n * 支持单位后缀、折线 / 面积 / 柱状迷你图、角标状态三角与整卡填充色。\n *\n * 简单的「标签 + 数值 + 图标」指标卡请使用 `Card.Info`。\n */\nconst CardStat: React.FC<CardStatProps> = ({\n label,\n value,\n tone = 'primary',\n loading = false,\n className = '',\n unit,\n valueSize = 'xl',\n align = 'left',\n sparkline,\n sparklineData,\n sparklineType = 'line',\n sparklineHeight = 36,\n indicator = false,\n filled = false,\n onClick,\n}) => {\n const sparklineNode =\n sparkline ??\n (sparklineData && sparklineData.length >= 2 ? (\n <MiniSparkline data={sparklineData} type={sparklineType} height={sparklineHeight} />\n ) : null);\n\n const textColor = filled ? '' : STAT_TONE_TEXT_CLASS[tone];\n const mutedText = filled ? 'opacity-80' : 'text-base-content/60';\n\n return (\n <div\n onClick={onClick}\n className={`relative flex flex-col overflow-hidden rounded-lg p-3 ${\n filled ? STAT_FILLED_CLASS[tone] : 'border border-base-300/60 bg-base-200'\n } ${onClick ? 'cursor-pointer transition-colors hover:border-primary/40' : ''} ${className}`.trim()}\n >\n {indicator && !filled && (\n <span\n aria-hidden=\"true\"\n className=\"absolute right-0 top-0 h-0 w-0\"\n style={{\n borderTop: '14px solid ' + statCssVarColor(tone),\n borderLeft: '14px solid transparent',\n }}\n />\n )}\n <div\n className={`truncate text-xs font-medium ${mutedText} ${align === 'center' ? 'text-center' : ''}`.trim()}\n >\n {label}\n </div>\n <div\n className={`flex min-h-[3rem] flex-1 items-center py-1 ${align === 'center' ? 'justify-center' : 'justify-start'}`}\n >\n {loading ? (\n <span\n className={`inline-block h-8 w-16 animate-pulse rounded ${\n filled ? 'bg-white/25' : 'bg-base-content/10'\n }`}\n />\n ) : (\n <span className={`leading-none tracking-tight ${STAT_VALUE_SIZE_CLASS[valueSize]} font-semibold ${filled ? '' : textColor}`.trim()}>\n {value}\n {unit !== undefined && unit !== null && unit !== '' && (\n <span className=\"ml-0.5 text-[0.45em] font-semibold\">{unit}</span>\n )}\n </span>\n )}\n </div>\n {sparklineNode && (\n <div className={`-mx-3 -mb-3 mt-1 ${filled ? 'text-white' : STAT_TONE_TEXT_CLASS[tone]}`}>\n {sparklineNode}\n </div>\n )}\n </div>\n );\n};\n\n// 扩展Card组件,添加子组件\ninterface CardCompound extends React.FC<CardProps> {\n Metric: React.FC<CardMetricProps>;\n Title: React.FC<CardTitleProps>;\n Body: React.FC<CardBodyProps>;\n Actions: React.FC<CardActionsProps>;\n Figure: React.FC<CardFigureProps>;\n /** 特性卡:图标 + 标题 + 描述(垂直居中 / 横向两种布局) */\n Feature: React.FC<CardFeatureProps>;\n /** 引用卡:引用文本 + 作者头像 / 日期 + 装饰引号 */\n Quote: React.FC<CardQuoteProps>;\n /** 元信息卡:标题 + 徽章 + 描述 + 左右页脚 */\n Meta: React.FC<CardMetaProps>;\n /** 指标卡:标签 + 数值 + 可选图标 / hint / 加载态(仪表盘统计卡片) */\n Info: React.FC<CardInfoProps>;\n /** 统计面板:Grafana 风格超大数值 + 迷你折线 / 面积 / 柱状图 / 角标 / 填充色 */\n Stat: React.FC<CardStatProps>;\n}\n\nconst CardWithCompounds = Card as CardCompound;\nCardWithCompounds.Title = CardTitle;\nCardWithCompounds.Body = CardBody;\nCardWithCompounds.Actions = CardActions;\nCardWithCompounds.Figure = CardFigure;\nCardWithCompounds.Feature = CardFeature;\nCardWithCompounds.Quote = CardQuote;\nCardWithCompounds.Meta = CardMeta;\nCardWithCompounds.Info = CardInfo;\nCardWithCompounds.Stat = CardStat;\nCardWithCompounds.Metric = CardMetric;\n\nexport default CardWithCompounds; "],"mappings":"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"}
|
|
@@ -2,18 +2,18 @@ import "react";
|
|
|
2
2
|
import { jsx as e, jsxs as t } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/EmptyState/index.tsx
|
|
4
4
|
var n = ({ icon: n, title: r, description: i, action: a, fill: o = !0, className: s = "" }) => /* @__PURE__ */ t("div", {
|
|
5
|
-
className: `flex flex-col items-center justify-center rounded-
|
|
5
|
+
className: `dishui-empty-state flex min-w-0 flex-col items-center justify-center rounded-lg border border-dashed border-base-300 bg-base-100 px-6 py-12 text-center text-body leading-5 ${o ? "h-full w-full" : ""} ${s}`.trim(),
|
|
6
6
|
children: [
|
|
7
7
|
n && /* @__PURE__ */ e("span", {
|
|
8
|
-
className: "mb-3 flex size-
|
|
8
|
+
className: "mb-3 flex size-10 items-center justify-center rounded-lg bg-base-200 text-base-content/70 [&>svg]:h-5 [&>svg]:w-5",
|
|
9
9
|
children: n
|
|
10
10
|
}),
|
|
11
11
|
/* @__PURE__ */ e("p", {
|
|
12
|
-
className: "text-
|
|
12
|
+
className: "max-w-full break-words text-body font-medium text-base-content",
|
|
13
13
|
children: r
|
|
14
14
|
}),
|
|
15
15
|
i && /* @__PURE__ */ e("p", {
|
|
16
|
-
className: "mt-1 max-w-sm text-
|
|
16
|
+
className: "mt-1 max-w-sm break-words text-body-sm leading-5 text-base-content/70",
|
|
17
17
|
children: i
|
|
18
18
|
}),
|
|
19
19
|
a && /* @__PURE__ */ e("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/EmptyState/index.tsx"],"sourcesContent":["import React from 'react';\n\nexport type EmptyStateProps = {\n /** 主图标(通常为 lucide 图标) */\n icon?: React.ReactNode;\n /** 主标题 */\n title: React.ReactNode;\n /** 补充说明 */\n description?: React.ReactNode;\n /** 底部操作(按钮等) */\n action?: React.ReactNode;\n /** 是否默认填满所在区域(宽高 100%,内容居中);默认 true */\n fill?: boolean;\n className?: string;\n};\n\n/**\n * 空状态:虚线边框卡片 + 图标 + 标题 + 说明,统一列表为空时的展示。\n * 默认填满所在区域(fill),在固定高度的容器内自动垂直居中。\n */\nconst EmptyState = ({\n icon,\n title,\n description,\n action,\n fill = true,\n className = '',\n}: EmptyStateProps) => {\n return (\n <div\n className={`flex flex-col items-center justify-center rounded-
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/EmptyState/index.tsx"],"sourcesContent":["import React from 'react';\n\nexport type EmptyStateProps = {\n /** 主图标(通常为 lucide 图标) */\n icon?: React.ReactNode;\n /** 主标题 */\n title: React.ReactNode;\n /** 补充说明 */\n description?: React.ReactNode;\n /** 底部操作(按钮等) */\n action?: React.ReactNode;\n /** 是否默认填满所在区域(宽高 100%,内容居中);默认 true */\n fill?: boolean;\n className?: string;\n};\n\n/**\n * 空状态:虚线边框卡片 + 图标 + 标题 + 说明,统一列表为空时的展示。\n * 默认填满所在区域(fill),在固定高度的容器内自动垂直居中。\n */\nconst EmptyState = ({\n icon,\n title,\n description,\n action,\n fill = true,\n className = '',\n}: EmptyStateProps) => {\n return (\n <div\n className={`dishui-empty-state flex min-w-0 flex-col items-center justify-center rounded-lg border border-dashed border-base-300 bg-base-100 px-6 py-12 text-center text-body leading-5 ${fill ? 'h-full w-full' : ''} ${className}`.trim()}\n >\n {icon && (\n <span className=\"mb-3 flex size-10 items-center justify-center rounded-lg bg-base-200 text-base-content/70 [&>svg]:h-5 [&>svg]:w-5\">\n {icon}\n </span>\n )}\n <p className=\"max-w-full break-words text-body font-medium text-base-content\">{title}</p>\n {description && (\n <p className=\"mt-1 max-w-sm break-words text-body-sm leading-5 text-base-content/70\">{description}</p>\n )}\n {action && <div className=\"mt-4\">{action}</div>}\n </div>\n );\n};\n\nexport default EmptyState;\n"],"mappings":";;;AAoBA,IAAM,KAAc,EAChB,SACA,UACA,gBACA,WACA,UAAO,IACP,eAAY,SAGR,kBAAC,OAAD;CACI,WAAW,+KAA+K,IAAO,kBAAkB,GAAG,GAAG,IAAY,KAAK;CAD9O,UAAA;EAGK,KACG,kBAAC,QAAD;GAAM,WAAU;GACX,UAAA;EACC,CAAA;EAEV,kBAAC,KAAD;GAAG,WAAU;GAAkE,UAAA;EAAS,CAAA;EACvF,KACG,kBAAC,KAAD;GAAG,WAAU;GAAyE,UAAA;EAAe,CAAA;EAExG,KAAU,kBAAC,OAAD;GAAK,WAAU;GAAQ,UAAA;EAAY,CAAA;CAC7C"}
|
|
@@ -3,7 +3,7 @@ import "react";
|
|
|
3
3
|
import { jsx as t } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/List/index.tsx
|
|
5
5
|
var n = ({ children: n, className: r = "", onClick: i, onMouseEnter: a, onMouseLeave: o }) => {
|
|
6
|
-
let s = e("list", r);
|
|
6
|
+
let s = e("dishui-list list text-body leading-5 text-base-content", r);
|
|
7
7
|
return /* @__PURE__ */ t("ul", {
|
|
8
8
|
className: s,
|
|
9
9
|
onClick: i,
|
|
@@ -12,7 +12,7 @@ var n = ({ children: n, className: r = "", onClick: i, onMouseEnter: a, onMouseL
|
|
|
12
12
|
children: n
|
|
13
13
|
});
|
|
14
14
|
}, r = ({ children: n, className: r = "", selected: i = !1, onClick: a, onDoubleClick: o, onMouseEnter: s, onMouseLeave: c, onContextMenu: l }) => {
|
|
15
|
-
let u = e("list-row", i && "bg-base-200", r);
|
|
15
|
+
let u = e("dishui-list-row list-row min-h-10 gap-3 rounded-none px-3 py-2 text-body transition-colors duration-150 motion-reduce:transition-none", i && "bg-base-200", r);
|
|
16
16
|
return /* @__PURE__ */ t("li", {
|
|
17
17
|
className: u,
|
|
18
18
|
onClick: a,
|
|
@@ -23,19 +23,19 @@ var n = ({ children: n, className: r = "", onClick: i, onMouseEnter: a, onMouseL
|
|
|
23
23
|
children: n
|
|
24
24
|
});
|
|
25
25
|
}, i = ({ children: n, className: r = "" }) => {
|
|
26
|
-
let i = e("list-col-wrap", r);
|
|
26
|
+
let i = e("list-col-wrap min-w-0 break-words", r);
|
|
27
27
|
return /* @__PURE__ */ t("div", {
|
|
28
28
|
className: i,
|
|
29
29
|
children: n
|
|
30
30
|
});
|
|
31
31
|
}, a = ({ children: n, className: r = "" }) => {
|
|
32
|
-
let i = e("list-col-grow", r);
|
|
32
|
+
let i = e("list-col-grow min-w-0 break-words", r);
|
|
33
33
|
return /* @__PURE__ */ t("div", {
|
|
34
34
|
className: i,
|
|
35
35
|
children: n
|
|
36
36
|
});
|
|
37
37
|
}, o = ({ children: n, className: r = "" }) => {
|
|
38
|
-
let i = e("
|
|
38
|
+
let i = e("dishui-list-header px-3 py-2", "text-body-sm font-medium leading-5", "text-base-content/70", r);
|
|
39
39
|
return /* @__PURE__ */ t("li", {
|
|
40
40
|
className: i,
|
|
41
41
|
children: n
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/List/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\n\nexport type ListProps = {\n children: React.ReactNode;\n className?: string;\n onClick?: (e: React.MouseEvent) => void;\n onMouseEnter?: (e: React.MouseEvent) => void;\n onMouseLeave?: (e: React.MouseEvent) => void;\n};\n\nconst List = ({\n children,\n className = '',\n onClick,\n onMouseEnter,\n onMouseLeave,\n}: ListProps) => {\n const baseClass = 'list';\n \n const listClasses = cn(baseClass, className);\n\n return (\n <ul \n className={listClasses}\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {children}\n </ul>\n );\n};\n\n// List.Row 组件\nexport type ListRowProps = {\n children: React.ReactNode;\n className?: string;\n selected?: boolean;\n onClick?: (e: React.MouseEvent) => void;\n onDoubleClick?: (e: React.MouseEvent) => void;\n onMouseEnter?: (e: React.MouseEvent) => void;\n onMouseLeave?: (e: React.MouseEvent) => void;\n onContextMenu?: (e: React.MouseEvent) => void;\n};\n\nconst ListRow = ({\n children,\n className = '',\n selected = false,\n onClick,\n onDoubleClick,\n onMouseEnter,\n onMouseLeave,\n onContextMenu,\n}: ListRowProps) => {\n const baseClass = 'list-row';\n \n const rowClasses = cn(baseClass, selected && 'bg-base-200', className);\n\n return (\n <li \n className={rowClasses}\n onClick={onClick}\n onDoubleClick={onDoubleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onContextMenu={onContextMenu}\n >\n {children}\n </li>\n );\n};\n\n// List.ColWrap 组件\nexport type ListColWrapProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListColWrap = ({\n children,\n className = '',\n}: ListColWrapProps) => {\n const baseClass = 'list-col-wrap';\n \n const colWrapClasses = cn(baseClass, className);\n\n return (\n <div className={colWrapClasses}>\n {children}\n </div>\n );\n};\n\n// List.ColGrow 组件\nexport type ListColGrowProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListColGrow = ({\n children,\n className = '',\n}: ListColGrowProps) => {\n const baseClass = 'list-col-grow';\n \n const colGrowClasses = cn(baseClass, className);\n\n return (\n <div className={colGrowClasses}>\n {children}\n </div>\n );\n};\n\n// List.Header 组件\nexport type ListHeaderProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListHeader = ({\n children,\n className = '',\n}: ListHeaderProps) => {\n const headerClasses = cn(\n '
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/List/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\n\nexport type ListProps = {\n children: React.ReactNode;\n className?: string;\n onClick?: (e: React.MouseEvent) => void;\n onMouseEnter?: (e: React.MouseEvent) => void;\n onMouseLeave?: (e: React.MouseEvent) => void;\n};\n\nconst List = ({\n children,\n className = '',\n onClick,\n onMouseEnter,\n onMouseLeave,\n}: ListProps) => {\n const baseClass = 'dishui-list list text-body leading-5 text-base-content';\n \n const listClasses = cn(baseClass, className);\n\n return (\n <ul \n className={listClasses}\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {children}\n </ul>\n );\n};\n\n// List.Row 组件\nexport type ListRowProps = {\n children: React.ReactNode;\n className?: string;\n selected?: boolean;\n onClick?: (e: React.MouseEvent) => void;\n onDoubleClick?: (e: React.MouseEvent) => void;\n onMouseEnter?: (e: React.MouseEvent) => void;\n onMouseLeave?: (e: React.MouseEvent) => void;\n onContextMenu?: (e: React.MouseEvent) => void;\n};\n\nconst ListRow = ({\n children,\n className = '',\n selected = false,\n onClick,\n onDoubleClick,\n onMouseEnter,\n onMouseLeave,\n onContextMenu,\n}: ListRowProps) => {\n const baseClass = 'dishui-list-row list-row min-h-10 gap-3 rounded-none px-3 py-2 text-body transition-colors duration-150 motion-reduce:transition-none';\n \n const rowClasses = cn(baseClass, selected && 'bg-base-200', className);\n\n return (\n <li \n className={rowClasses}\n onClick={onClick}\n onDoubleClick={onDoubleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onContextMenu={onContextMenu}\n >\n {children}\n </li>\n );\n};\n\n// List.ColWrap 组件\nexport type ListColWrapProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListColWrap = ({\n children,\n className = '',\n}: ListColWrapProps) => {\n const baseClass = 'list-col-wrap min-w-0 break-words';\n \n const colWrapClasses = cn(baseClass, className);\n\n return (\n <div className={colWrapClasses}>\n {children}\n </div>\n );\n};\n\n// List.ColGrow 组件\nexport type ListColGrowProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListColGrow = ({\n children,\n className = '',\n}: ListColGrowProps) => {\n const baseClass = 'list-col-grow min-w-0 break-words';\n \n const colGrowClasses = cn(baseClass, className);\n\n return (\n <div className={colGrowClasses}>\n {children}\n </div>\n );\n};\n\n// List.Header 组件\nexport type ListHeaderProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ListHeader = ({\n children,\n className = '',\n}: ListHeaderProps) => {\n const headerClasses = cn(\n 'dishui-list-header px-3 py-2',\n 'text-body-sm font-medium leading-5',\n 'text-base-content/70',\n className\n );\n\n return (\n <li className={headerClasses}>\n {children}\n </li>\n );\n};\n\n// 扩展List组件,添加Row、ColWrap和ColGrow子组件\ninterface ListCompound extends React.FC<ListProps> {\n Row: React.FC<ListRowProps>;\n ColWrap: React.FC<ListColWrapProps>;\n ColGrow: React.FC<ListColGrowProps>;\n Header: React.FC<ListHeaderProps>;\n}\n\nconst ListWithCompounds = List as ListCompound;\nListWithCompounds.Row = ListRow;\nListWithCompounds.ColWrap = ListColWrap;\nListWithCompounds.ColGrow = ListColGrow;\nListWithCompounds.Header = ListHeader;\n\nexport default ListWithCompounds; "],"mappings":";;;;AAWA,IAAM,KAAQ,EACZ,aACA,eAAY,IACZ,YACA,iBACA,sBACe;CAGf,IAAM,IAAc,EAAG,0DAAW,CAAS;CAE3C,OACE,kBAAC,MAAD;EACE,WAAW;EACF;EACK;EACA;EAEb;CACC,CAAA;AAER,GAcM,KAAW,EACf,aACA,eAAY,IACZ,cAAW,IACX,YACA,kBACA,iBACA,iBACA,uBACkB;CAGlB,IAAM,IAAa,EAAG,yIAAW,KAAY,eAAe,CAAS;CAErE,OACE,kBAAC,MAAD;EACE,WAAW;EACF;EACM;EACD;EACA;EACC;EAEd;CACC,CAAA;AAER,GAQM,KAAe,EACnB,aACA,eAAY,SACU;CAGtB,IAAM,IAAiB,EAAG,qCAAW,CAAS;CAE9C,OACE,kBAAC,OAAD;EAAK,WAAW;EACb;CACE,CAAA;AAET,GAQM,KAAe,EACnB,aACA,eAAY,SACU;CAGtB,IAAM,IAAiB,EAAG,qCAAW,CAAS;CAE9C,OACE,kBAAC,OAAD;EAAK,WAAW;EACb;CACE,CAAA;AAET,GAQM,KAAc,EAClB,aACA,eAAY,SACS;CACrB,IAAM,IAAgB,EACpB,gCACA,sCACA,wBACA,CACF;CAEA,OACE,kBAAC,MAAD;EAAI,WAAW;EACZ;CACC,CAAA;AAER,GAUM,IAAoB;AAC1B,EAAkB,MAAM,GACxB,EAAkB,UAAU,GAC5B,EAAkB,UAAU,GAC5B,EAAkB,SAAS"}
|
|
@@ -34,25 +34,25 @@ var r = {
|
|
|
34
34
|
}), /* @__PURE__ */ n("div", {
|
|
35
35
|
role: "dialog",
|
|
36
36
|
"aria-modal": "true",
|
|
37
|
-
className: `card relative flex w-full flex-col bg-base-100 shadow-
|
|
37
|
+
className: `dishui-modal card relative flex w-full min-w-0 flex-col rounded-xl border border-base-300 bg-base-100 text-body leading-5 text-base-content shadow-md ${y} ${g}`.trim(),
|
|
38
38
|
style: v,
|
|
39
39
|
children: [
|
|
40
40
|
(s || h) && /* @__PURE__ */ n("div", {
|
|
41
|
-
className: "flex shrink-0 items-start justify-between gap-3 border-b border-base-300 px-5 py-3
|
|
41
|
+
className: "flex shrink-0 items-start justify-between gap-3 border-b border-base-300 px-5 py-3",
|
|
42
42
|
children: [/* @__PURE__ */ n("div", {
|
|
43
43
|
className: "min-w-0",
|
|
44
44
|
children: [s && /* @__PURE__ */ t("h3", {
|
|
45
|
-
className: "
|
|
45
|
+
className: "break-words text-body font-semibold text-base-content",
|
|
46
46
|
children: s
|
|
47
47
|
}), c && /* @__PURE__ */ t("p", {
|
|
48
|
-
className: "mt-
|
|
48
|
+
className: "mt-1 text-body-sm leading-5 text-base-content/70",
|
|
49
49
|
children: c
|
|
50
50
|
})]
|
|
51
51
|
}), h && /* @__PURE__ */ t("button", {
|
|
52
52
|
type: "button",
|
|
53
53
|
onClick: o,
|
|
54
54
|
"aria-label": "Close",
|
|
55
|
-
className: "shrink-0 rounded-md p-1 text-base-content/
|
|
55
|
+
className: "shrink-0 rounded-md p-1 text-base-content/70 transition-colors duration-150 motion-reduce:transition-none hover:bg-base-200 hover:text-base-content focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current",
|
|
56
56
|
children: /* @__PURE__ */ t("svg", {
|
|
57
57
|
className: "h-4 w-4",
|
|
58
58
|
fill: "none",
|
|
@@ -72,7 +72,7 @@ var r = {
|
|
|
72
72
|
children: l
|
|
73
73
|
}),
|
|
74
74
|
u && /* @__PURE__ */ t("div", {
|
|
75
|
-
className: "flex shrink-0 justify-end gap-2 border-t border-base-300 px-5 py-3
|
|
75
|
+
className: "flex shrink-0 flex-wrap justify-end gap-2 border-t border-base-300 px-5 py-3",
|
|
76
76
|
children: u
|
|
77
77
|
})
|
|
78
78
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Modal/index.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\n\nexport type ModalProps = {\n /** 是否打开 */\n open: boolean;\n /** 关闭回调(遮罩点击 / Esc / 关闭按钮触发) */\n onClose: () => void;\n /** 标题 */\n title?: React.ReactNode;\n /** 标题下的说明文字 */\n description?: React.ReactNode;\n /** 正文内容 */\n children?: React.ReactNode;\n /** 底部操作区(默认右对齐) */\n footer?: React.ReactNode;\n /** 尺寸(预设):sm / md / lg / xl / full */\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';\n /** 自定义宽度:支持百分比/像素等 CSS 长度(如 \"80%\"、640)。优先于 size。 */\n width?: number | string;\n /** 自定义高度:支持百分比/像素等 CSS 长度(如 \"80%\"、480)。 */\n height?: number | string;\n /** 点击遮罩是否关闭(默认 true) */\n closeOnOverlay?: boolean;\n /** 是否显示右上角关闭按钮(默认 true) */\n showClose?: boolean;\n className?: string;\n};\n\nconst SIZE_CLASS: Record<NonNullable<ModalProps['size']>, string> = {\n sm: 'max-w-sm',\n md: 'max-w-lg',\n lg: 'max-w-2xl',\n xl: 'max-w-5xl',\n full: 'max-w-[95vw]',\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\n/**\n * 轻量模态框:遮罩 + 居中面板,支持 Esc 关闭、遮罩点击关闭与滚动锁定。\n */\nconst Modal = ({\n open,\n onClose,\n title,\n description,\n children,\n footer,\n size = 'md',\n width,\n height,\n closeOnOverlay = true,\n showClose = true,\n className = '',\n}: ModalProps) => {\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n window.addEventListener('keydown', onKey);\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n window.removeEventListener('keydown', onKey);\n document.body.style.overflow = prevOverflow;\n };\n }, [open, onClose]);\n\n if (!open) return null;\n\n // 自定义宽高:传入时优先于 size 预设;支持百分比。\n const hasCustomWidth = width !== undefined;\n const panelStyle: React.CSSProperties = {\n width: hasCustomWidth ? toCssSize(width) : undefined,\n maxWidth: hasCustomWidth ? '95vw' : undefined,\n height: toCssSize(height),\n maxHeight: '92vh',\n };\n // 面板尺寸类:未自定义宽度时才应用 max-w-* 预设。\n const sizeClass = hasCustomWidth ? '' : SIZE_CLASS[size];\n // 正文区:固定高度时自适应充满并内部滚动;否则限制最大高度。\n const bodyClass = height !== undefined\n ? 'min-h-0 flex-1 overflow-y-auto px-5 py-4'\n : 'max-h-[70vh] overflow-y-auto px-5 py-4';\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div\n className=\"absolute inset-0\"\n onClick={closeOnOverlay ? onClose : undefined}\n aria-hidden\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n className={`card relative flex w-full flex-col bg-base-100 shadow-
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Modal/index.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\n\nexport type ModalProps = {\n /** 是否打开 */\n open: boolean;\n /** 关闭回调(遮罩点击 / Esc / 关闭按钮触发) */\n onClose: () => void;\n /** 标题 */\n title?: React.ReactNode;\n /** 标题下的说明文字 */\n description?: React.ReactNode;\n /** 正文内容 */\n children?: React.ReactNode;\n /** 底部操作区(默认右对齐) */\n footer?: React.ReactNode;\n /** 尺寸(预设):sm / md / lg / xl / full */\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';\n /** 自定义宽度:支持百分比/像素等 CSS 长度(如 \"80%\"、640)。优先于 size。 */\n width?: number | string;\n /** 自定义高度:支持百分比/像素等 CSS 长度(如 \"80%\"、480)。 */\n height?: number | string;\n /** 点击遮罩是否关闭(默认 true) */\n closeOnOverlay?: boolean;\n /** 是否显示右上角关闭按钮(默认 true) */\n showClose?: boolean;\n className?: string;\n};\n\nconst SIZE_CLASS: Record<NonNullable<ModalProps['size']>, string> = {\n sm: 'max-w-sm',\n md: 'max-w-lg',\n lg: 'max-w-2xl',\n xl: 'max-w-5xl',\n full: 'max-w-[95vw]',\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\n/**\n * 轻量模态框:遮罩 + 居中面板,支持 Esc 关闭、遮罩点击关闭与滚动锁定。\n */\nconst Modal = ({\n open,\n onClose,\n title,\n description,\n children,\n footer,\n size = 'md',\n width,\n height,\n closeOnOverlay = true,\n showClose = true,\n className = '',\n}: ModalProps) => {\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n window.addEventListener('keydown', onKey);\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n window.removeEventListener('keydown', onKey);\n document.body.style.overflow = prevOverflow;\n };\n }, [open, onClose]);\n\n if (!open) return null;\n\n // 自定义宽高:传入时优先于 size 预设;支持百分比。\n const hasCustomWidth = width !== undefined;\n const panelStyle: React.CSSProperties = {\n width: hasCustomWidth ? toCssSize(width) : undefined,\n maxWidth: hasCustomWidth ? '95vw' : undefined,\n height: toCssSize(height),\n maxHeight: '92vh',\n };\n // 面板尺寸类:未自定义宽度时才应用 max-w-* 预设。\n const sizeClass = hasCustomWidth ? '' : SIZE_CLASS[size];\n // 正文区:固定高度时自适应充满并内部滚动;否则限制最大高度。\n const bodyClass = height !== undefined\n ? 'min-h-0 flex-1 overflow-y-auto px-5 py-4'\n : 'max-h-[70vh] overflow-y-auto px-5 py-4';\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div\n className=\"absolute inset-0\"\n onClick={closeOnOverlay ? onClose : undefined}\n aria-hidden\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n className={`dishui-modal card relative flex w-full min-w-0 flex-col rounded-xl border border-base-300 bg-base-100 text-body leading-5 text-base-content shadow-md ${sizeClass} ${className}`.trim()}\n style={panelStyle}\n >\n {(title || showClose) && (\n <div className=\"flex shrink-0 items-start justify-between gap-3 border-b border-base-300 px-5 py-3\">\n <div className=\"min-w-0\">\n {title && (\n <h3 className=\"break-words text-body font-semibold text-base-content\">\n {title}\n </h3>\n )}\n {description && (\n <p className=\"mt-1 text-body-sm leading-5 text-base-content/70\">{description}</p>\n )}\n </div>\n {showClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"shrink-0 rounded-md p-1 text-base-content/70 transition-colors duration-150 motion-reduce:transition-none hover:bg-base-200 hover:text-base-content focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current\"\n >\n <svg\n className=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n )}\n </div>\n )}\n <div className={bodyClass}>{children}</div>\n {footer && (\n <div className=\"flex shrink-0 flex-wrap justify-end gap-2 border-t border-base-300 px-5 py-3\">\n {footer}\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default Modal;\n"],"mappings":";;;AA4BA,IAAM,IAA8D;CAChE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;AACV,GAEM,KAAa,MACf,OAAO,KAAU,WAAW,GAAG,EAAM,MAAM,GAKzC,KAAS,EACX,SACA,YACA,UACA,gBACA,aACA,WACA,UAAO,MACP,UACA,WACA,oBAAiB,IACjB,eAAY,IACZ,eAAY,SACE;CAed,IAdA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,KAAS,MAAqB;GAChC,AAAI,EAAE,QAAQ,YAAU,EAAQ;EACpC;EACA,OAAO,iBAAiB,WAAW,CAAK;EACxC,IAAM,IAAe,SAAS,KAAK,MAAM;EAEzC,OADA,SAAS,KAAK,MAAM,WAAW,gBAClB;GAET,AADA,OAAO,oBAAoB,WAAW,CAAK,GAC3C,SAAS,KAAK,MAAM,WAAW;EACnC;CACJ,GAAG,CAAC,GAAM,CAAO,CAAC,GAEd,CAAC,GAAM,OAAO;CAGlB,IAAM,IAAiB,MAAU,KAAA,GAC3B,IAAkC;EACpC,OAAO,IAAiB,EAAU,CAAK,IAAI,KAAA;EAC3C,UAAU,IAAiB,SAAS,KAAA;EACpC,QAAQ,EAAU,CAAM;EACxB,WAAW;CACf,GAEM,IAAY,IAAiB,KAAK,EAAW,IAE7C,IAAY,MAAW,KAAA,IAEvB,2CADA;CAGN,OACI,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACI,kBAAC,OAAD;GACI,WAAU;GACV,SAAS,IAAiB,IAAU,KAAA;GACpC,eAAA;EACH,CAAA,GACD,kBAAC,OAAD;GACI,MAAK;GACL,cAAW;GACX,WAAW,yJAAyJ,EAAU,GAAG,IAAY,KAAK;GAClM,OAAO;GAJX,UAAA;KAMM,KAAS,MACP,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACI,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACK,KACG,kBAAC,MAAD;OAAI,WAAU;OACT,UAAA;MACD,CAAA,GAEP,KACG,kBAAC,KAAD;OAAG,WAAU;OAAoD,UAAA;MAAe,CAAA,CAEnF;KACJ,CAAA,GAAA,KACG,kBAAC,UAAD;MACI,MAAK;MACL,SAAS;MACT,cAAW;MACX,WAAU;MAEV,UAAA,kBAAC,OAAD;OACI,WAAU;OACV,MAAK;OACL,SAAQ;OACR,QAAO;OACP,aAAa;OAEb,UAAA,kBAAC,QAAD;QACI,eAAc;QACd,gBAAe;QACf,GAAE;OACL,CAAA;MACA,CAAA;KACD,CAAA,CAEX;;IAET,kBAAC,OAAD;KAAK,WAAW;KAAY;IAAc,CAAA;IACzC,KACG,kBAAC,OAAD;KAAK,WAAU;KACV,UAAA;IACA,CAAA;GAER;EACJ,CAAA,CAAA;;AAEb"}
|
|
@@ -4,7 +4,7 @@ import "react";
|
|
|
4
4
|
import { jsx as n } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/Page/index.tsx
|
|
6
6
|
var r = (e) => typeof e == "number" ? `${e}px` : e, i = ({ fullHeight: t = !1, className: r = "", children: i, ...a }) => /* @__PURE__ */ n("div", {
|
|
7
|
-
className: e("dishui-page flex w-full flex-col", t && "h-full min-h-0", r),
|
|
7
|
+
className: e("dishui-page flex w-full min-w-0 flex-col text-body leading-5 text-base-content", t && "h-full min-h-0", r),
|
|
8
8
|
...a,
|
|
9
9
|
children: i
|
|
10
10
|
}), a = (e) => /* @__PURE__ */ n(t, { ...e }), o = ({ gap: t = 16, scroll: i = !1, className: a = "", style: o, children: s, ...c }) => /* @__PURE__ */ n("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Page/index.tsx"],"sourcesContent":["import React from 'react';\nimport PageHeader, { PageHeaderProps } from '../PageHeader';\nimport { cn } from '../../utils/cn';\n\n/**\n * Page 布局脚手架\n * ----------------------------------------------------------------------------\n * 为管理后台等页面提供统一的「页头 + 主体 + 左右侧栏」骨架,配合 PageHeader、\n * Card.Info 等组件使用。所有区域都是可选的,可按需组合。\n *\n * 典型结构:\n * <Page fullHeight>\n * <Page.Header title=\"用户管理\" actions={<Button>新建</Button>} />\n * <Page.Body scroll>\n * <Page.Left width={240}>筛选器</Page.Left>\n * <Page.Main scroll>主内容</Page.Main>\n * <Page.Right width={320}>详情</Page.Right>\n * </Page.Body>\n * </Page>\n */\n\nexport type PageProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 是否占满父容器高度并启用内部滚动(主体区滚动,页头固定) */\n fullHeight?: boolean;\n};\n\nexport type PageBodyProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 主体内的间距(left/main/right 之间),数字按 px 处理,默认 16 */\n gap?: number | string;\n /** 是否让主体区在剩余空间内滚动(配合 Page fullHeight 使用) */\n scroll?: boolean;\n};\n\nexport type PageAsideProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 侧栏宽度,数字按 px 处理,默认 260 */\n width?: number | string;\n /** 是否显示分隔边框 */\n bordered?: boolean;\n /** 侧栏内部纵向滚动自适应 */\n scroll?: boolean;\n /** 小屏(<768px)下是否隐藏(侧栏通常在移动端折叠) */\n hideOnMobile?: boolean;\n};\n\nexport type PageMainProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 主内容区是否在剩余空间内纵向滚动 */\n scroll?: boolean;\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Page = ({ fullHeight = false, className = '', children, ...rest }: PageProps) => (\n <div\n className={cn(\n 'dishui-page flex w-full flex-col',\n fullHeight && 'h-full min-h-0',\n className,\n )}\n {...rest}\n >\n {children}\n </div>\n);\n\n/** 页头:直接复用 PageHeader,作为 Page.Header 使用 */\nconst PageHeaderSlot = (props: PageHeaderProps) => <PageHeader {...props} />;\n\nconst PageBody = ({ gap = 16, scroll = false, className = '', style, children, ...rest }: PageBodyProps) => (\n <div\n className={cn(\n 'dishui-page-body flex w-full flex-col md:flex-row',\n scroll && 'min-h-0 flex-1',\n className,\n )}\n style={{ gap: toCssSize(gap), ...style }}\n {...rest}\n >\n {children}\n </div>\n);\n\nconst PageLeft = ({\n width = 260,\n bordered = true,\n scroll = true,\n hideOnMobile = false,\n className = '',\n style,\n children,\n ...rest\n}: PageAsideProps) => (\n <aside\n className={cn(\n 'dishui-page-left w-full shrink-0 md:w-(--dishui-aside-w)',\n bordered && 'md:border-r md:border-base-300 md:pr-4',\n scroll && 'md:min-h-0 md:overflow-y-auto',\n hideOnMobile && 'hidden md:block',\n className,\n )}\n style={{ ['--dishui-aside-w' as string]: toCssSize(width), ...style }}\n {...rest}\n >\n {children}\n </aside>\n);\n\nconst PageRight = ({\n width = 320,\n bordered = true,\n scroll = true,\n hideOnMobile = false,\n className = '',\n style,\n children,\n ...rest\n}: PageAsideProps) => (\n <aside\n className={cn(\n 'dishui-page-right w-full shrink-0 md:w-(--dishui-aside-w)',\n bordered && 'md:border-l md:border-base-300 md:pl-4',\n scroll && 'md:min-h-0 md:overflow-y-auto',\n hideOnMobile && 'hidden md:block',\n className,\n )}\n style={{ ['--dishui-aside-w' as string]: toCssSize(width), ...style }}\n {...rest}\n >\n {children}\n </aside>\n);\n\nconst PageMain = ({ scroll = false, className = '', children, ...rest }: PageMainProps) => (\n <main\n className={cn(\n 'dishui-page-main min-w-0 flex-1',\n scroll && 'min-h-0 overflow-y-auto',\n className,\n )}\n {...rest}\n >\n {children}\n </main>\n);\n\ninterface PageCompound extends React.FC<PageProps> {\n Header: typeof PageHeaderSlot;\n Body: typeof PageBody;\n Left: typeof PageLeft;\n Right: typeof PageRight;\n Main: typeof PageMain;\n}\n\nconst PageWithCompounds = Page as PageCompound;\nPageWithCompounds.Header = PageHeaderSlot;\nPageWithCompounds.Body = PageBody;\nPageWithCompounds.Left = PageLeft;\nPageWithCompounds.Right = PageRight;\nPageWithCompounds.Main = PageMain;\n\nexport { PageBody, PageLeft, PageRight, PageMain };\nexport default PageWithCompounds;\n"],"mappings":";;;;;AAiDA,IAAM,KAAa,MACf,OAAO,KAAU,WAAW,GAAG,EAAM,MAAM,GAEzC,KAAQ,EAAE,gBAAa,IAAO,eAAY,IAAI,aAAU,GAAG,QAC7D,kBAAC,OAAD;CACI,WAAW,EACP,
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Page/index.tsx"],"sourcesContent":["import React from 'react';\nimport PageHeader, { PageHeaderProps } from '../PageHeader';\nimport { cn } from '../../utils/cn';\n\n/**\n * Page 布局脚手架\n * ----------------------------------------------------------------------------\n * 为管理后台等页面提供统一的「页头 + 主体 + 左右侧栏」骨架,配合 PageHeader、\n * Card.Info 等组件使用。所有区域都是可选的,可按需组合。\n *\n * 典型结构:\n * <Page fullHeight>\n * <Page.Header title=\"用户管理\" actions={<Button>新建</Button>} />\n * <Page.Body scroll>\n * <Page.Left width={240}>筛选器</Page.Left>\n * <Page.Main scroll>主内容</Page.Main>\n * <Page.Right width={320}>详情</Page.Right>\n * </Page.Body>\n * </Page>\n */\n\nexport type PageProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 是否占满父容器高度并启用内部滚动(主体区滚动,页头固定) */\n fullHeight?: boolean;\n};\n\nexport type PageBodyProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 主体内的间距(left/main/right 之间),数字按 px 处理,默认 16 */\n gap?: number | string;\n /** 是否让主体区在剩余空间内滚动(配合 Page fullHeight 使用) */\n scroll?: boolean;\n};\n\nexport type PageAsideProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 侧栏宽度,数字按 px 处理,默认 260 */\n width?: number | string;\n /** 是否显示分隔边框 */\n bordered?: boolean;\n /** 侧栏内部纵向滚动自适应 */\n scroll?: boolean;\n /** 小屏(<768px)下是否隐藏(侧栏通常在移动端折叠) */\n hideOnMobile?: boolean;\n};\n\nexport type PageMainProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 主内容区是否在剩余空间内纵向滚动 */\n scroll?: boolean;\n};\n\nconst toCssSize = (value: number | string | undefined): string | undefined =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Page = ({ fullHeight = false, className = '', children, ...rest }: PageProps) => (\n <div\n className={cn(\n 'dishui-page flex w-full min-w-0 flex-col text-body leading-5 text-base-content',\n fullHeight && 'h-full min-h-0',\n className,\n )}\n {...rest}\n >\n {children}\n </div>\n);\n\n/** 页头:直接复用 PageHeader,作为 Page.Header 使用 */\nconst PageHeaderSlot = (props: PageHeaderProps) => <PageHeader {...props} />;\n\nconst PageBody = ({ gap = 16, scroll = false, className = '', style, children, ...rest }: PageBodyProps) => (\n <div\n className={cn(\n 'dishui-page-body flex w-full flex-col md:flex-row',\n scroll && 'min-h-0 flex-1',\n className,\n )}\n style={{ gap: toCssSize(gap), ...style }}\n {...rest}\n >\n {children}\n </div>\n);\n\nconst PageLeft = ({\n width = 260,\n bordered = true,\n scroll = true,\n hideOnMobile = false,\n className = '',\n style,\n children,\n ...rest\n}: PageAsideProps) => (\n <aside\n className={cn(\n 'dishui-page-left w-full shrink-0 md:w-(--dishui-aside-w)',\n bordered && 'md:border-r md:border-base-300 md:pr-4',\n scroll && 'md:min-h-0 md:overflow-y-auto',\n hideOnMobile && 'hidden md:block',\n className,\n )}\n style={{ ['--dishui-aside-w' as string]: toCssSize(width), ...style }}\n {...rest}\n >\n {children}\n </aside>\n);\n\nconst PageRight = ({\n width = 320,\n bordered = true,\n scroll = true,\n hideOnMobile = false,\n className = '',\n style,\n children,\n ...rest\n}: PageAsideProps) => (\n <aside\n className={cn(\n 'dishui-page-right w-full shrink-0 md:w-(--dishui-aside-w)',\n bordered && 'md:border-l md:border-base-300 md:pl-4',\n scroll && 'md:min-h-0 md:overflow-y-auto',\n hideOnMobile && 'hidden md:block',\n className,\n )}\n style={{ ['--dishui-aside-w' as string]: toCssSize(width), ...style }}\n {...rest}\n >\n {children}\n </aside>\n);\n\nconst PageMain = ({ scroll = false, className = '', children, ...rest }: PageMainProps) => (\n <main\n className={cn(\n 'dishui-page-main min-w-0 flex-1',\n scroll && 'min-h-0 overflow-y-auto',\n className,\n )}\n {...rest}\n >\n {children}\n </main>\n);\n\ninterface PageCompound extends React.FC<PageProps> {\n Header: typeof PageHeaderSlot;\n Body: typeof PageBody;\n Left: typeof PageLeft;\n Right: typeof PageRight;\n Main: typeof PageMain;\n}\n\nconst PageWithCompounds = Page as PageCompound;\nPageWithCompounds.Header = PageHeaderSlot;\nPageWithCompounds.Body = PageBody;\nPageWithCompounds.Left = PageLeft;\nPageWithCompounds.Right = PageRight;\nPageWithCompounds.Main = PageMain;\n\nexport { PageBody, PageLeft, PageRight, PageMain };\nexport default PageWithCompounds;\n"],"mappings":";;;;;AAiDA,IAAM,KAAa,MACf,OAAO,KAAU,WAAW,GAAG,EAAM,MAAM,GAEzC,KAAQ,EAAE,gBAAa,IAAO,eAAY,IAAI,aAAU,GAAG,QAC7D,kBAAC,OAAD;CACI,WAAW,EACP,kFACA,KAAc,kBACd,CACJ;CACA,GAAI;CAEH;AACA,CAAA,GAIH,KAAkB,MAA2B,kBAAC,GAAD,EAAY,GAAI,EAAQ,CAAA,GAErE,KAAY,EAAE,SAAM,IAAI,YAAS,IAAO,eAAY,IAAI,UAAO,aAAU,GAAG,QAC9E,kBAAC,OAAD;CACI,WAAW,EACP,qDACA,KAAU,kBACV,CACJ;CACA,OAAO;EAAE,KAAK,EAAU,CAAG;EAAG,GAAG;CAAM;CACvC,GAAI;CAEH;AACA,CAAA,GAGH,KAAY,EACd,WAAQ,KACR,cAAW,IACX,YAAS,IACT,kBAAe,IACf,eAAY,IACZ,UACA,aACA,GAAG,QAEH,kBAAC,SAAD;CACI,WAAW,EACP,4DACA,KAAY,0CACZ,KAAU,iCACV,KAAgB,mBAChB,CACJ;CACA,OAAO;EAAG,oBAA+B,EAAU,CAAK;EAAG,GAAG;CAAM;CACpE,GAAI;CAEH;AACE,CAAA,GAGL,KAAa,EACf,WAAQ,KACR,cAAW,IACX,YAAS,IACT,kBAAe,IACf,eAAY,IACZ,UACA,aACA,GAAG,QAEH,kBAAC,SAAD;CACI,WAAW,EACP,6DACA,KAAY,0CACZ,KAAU,iCACV,KAAgB,mBAChB,CACJ;CACA,OAAO;EAAG,oBAA+B,EAAU,CAAK;EAAG,GAAG;CAAM;CACpE,GAAI;CAEH;AACE,CAAA,GAGL,KAAY,EAAE,YAAS,IAAO,eAAY,IAAI,aAAU,GAAG,QAC7D,kBAAC,QAAD;CACI,WAAW,EACP,mCACA,KAAU,2BACV,CACJ;CACA,GAAI;CAEH;AACC,CAAA,GAWJ,IAAoB;AAC1B,EAAkB,SAAS,GAC3B,EAAkB,OAAO,GACzB,EAAkB,OAAO,GACzB,EAAkB,QAAQ,GAC1B,EAAkB,OAAO"}
|
|
@@ -2,26 +2,26 @@ import "react";
|
|
|
2
2
|
import { jsx as e, jsxs as t } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/PageHeader/index.tsx
|
|
4
4
|
var n = ({ title: n, description: r, icon: i, actions: a, compact: o = !0, className: s = "" }) => {
|
|
5
|
-
let c = o ? "text-lg" : "text-
|
|
5
|
+
let c = o ? "text-lg" : "text-xl", l = o ? "[&>svg]:h-5 [&>svg]:w-5" : "[&>svg]:h-6 [&>svg]:w-6";
|
|
6
6
|
return /* @__PURE__ */ t("div", {
|
|
7
|
-
className: `flex flex-wrap items-start justify-between gap-3 ${o ? "mb-
|
|
7
|
+
className: `dishui-page-header flex flex-wrap items-start justify-between gap-3 text-body ${o ? "mb-4" : "mb-6"} ${s}`.trim(),
|
|
8
8
|
children: [/* @__PURE__ */ t("div", {
|
|
9
9
|
className: "min-w-0",
|
|
10
10
|
children: [/* @__PURE__ */ t("div", {
|
|
11
11
|
className: "flex items-center gap-2.5",
|
|
12
12
|
children: [i && /* @__PURE__ */ e("span", {
|
|
13
|
-
className: `shrink-0 text-
|
|
13
|
+
className: `shrink-0 text-base-content/70 ${l}`,
|
|
14
14
|
children: i
|
|
15
15
|
}), /* @__PURE__ */ e("h1", {
|
|
16
|
-
className: `truncate font-
|
|
16
|
+
className: `truncate font-semibold text-base-content ${c}`,
|
|
17
17
|
children: n
|
|
18
18
|
})]
|
|
19
19
|
}), r && /* @__PURE__ */ e("p", {
|
|
20
|
-
className: "mt-
|
|
20
|
+
className: "mt-1 text-body-sm leading-5 text-base-content/70",
|
|
21
21
|
children: r
|
|
22
22
|
})]
|
|
23
23
|
}), a && /* @__PURE__ */ e("div", {
|
|
24
|
-
className: "flex
|
|
24
|
+
className: "flex flex-wrap items-center gap-2",
|
|
25
25
|
children: a
|
|
26
26
|
})]
|
|
27
27
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/PageHeader/index.tsx"],"sourcesContent":["import React from 'react';\n\nexport type PageHeaderProps = {\n /** 页面标题 */\n title: React.ReactNode;\n /** 标题下方的描述文字 */\n description?: React.ReactNode;\n /** 标题左侧的图标(通常为 lucide 图标) */\n icon?: React.ReactNode;\n /** 右侧操作区(按钮组等) */\n actions?: React.ReactNode;\n /** 紧凑模式:标题与间距更小 */\n compact?: boolean;\n className?: string;\n};\n\n/**\n * 页面头部:统一管理后台等页面的标题 / 描述 / 操作区排版。\n * 相比页面内手写的 `text-3xl` 标题,默认字号更精致(text-lg)。\n */\nconst PageHeader = ({\n title,\n description,\n icon,\n actions,\n compact = true,\n className = '',\n}: PageHeaderProps) => {\n const titleSize = compact ? 'text-lg' : 'text-
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/PageHeader/index.tsx"],"sourcesContent":["import React from 'react';\n\nexport type PageHeaderProps = {\n /** 页面标题 */\n title: React.ReactNode;\n /** 标题下方的描述文字 */\n description?: React.ReactNode;\n /** 标题左侧的图标(通常为 lucide 图标) */\n icon?: React.ReactNode;\n /** 右侧操作区(按钮组等) */\n actions?: React.ReactNode;\n /** 紧凑模式:标题与间距更小 */\n compact?: boolean;\n className?: string;\n};\n\n/**\n * 页面头部:统一管理后台等页面的标题 / 描述 / 操作区排版。\n * 相比页面内手写的 `text-3xl` 标题,默认字号更精致(text-lg)。\n */\nconst PageHeader = ({\n title,\n description,\n icon,\n actions,\n compact = true,\n className = '',\n}: PageHeaderProps) => {\n const titleSize = compact ? 'text-lg' : 'text-xl';\n const iconSize = compact ? '[&>svg]:h-5 [&>svg]:w-5' : '[&>svg]:h-6 [&>svg]:w-6';\n const spacing = compact ? 'mb-4' : 'mb-6';\n\n return (\n <div className={`dishui-page-header flex flex-wrap items-start justify-between gap-3 text-body ${spacing} ${className}`.trim()}>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <span className={`shrink-0 text-base-content/70 ${iconSize}`}>{icon}</span>\n )}\n <h1 className={`truncate font-semibold text-base-content ${titleSize}`}>{title}</h1>\n </div>\n {description && (\n <p className=\"mt-1 text-body-sm leading-5 text-base-content/70\">{description}</p>\n )}\n </div>\n {actions && <div className=\"flex flex-wrap items-center gap-2\">{actions}</div>}\n </div>\n );\n};\n\nexport default PageHeader;\n"],"mappings":";;;AAoBA,IAAM,KAAc,EAChB,UACA,gBACA,SACA,YACA,aAAU,IACV,eAAY,SACO;CACnB,IAAM,IAAY,IAAU,YAAY,WAClC,IAAW,IAAU,4BAA4B;CAGvD,OACI,kBAAC,OAAD;EAAK,WAAW,iFAHJ,IAAU,SAAS,OAG0E,GAAG,IAAY,KAAK;EAA7H,UAAA,CACI,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACI,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACK,KACG,kBAAC,QAAD;KAAM,WAAW,iCAAiC;KAAa,UAAA;IAAW,CAAA,GAE9E,kBAAC,MAAD;KAAI,WAAW,4CAA4C;KAAc,UAAA;IAAU,CAAA,CAClF;GACJ,CAAA,GAAA,KACG,kBAAC,KAAD;IAAG,WAAU;IAAoD,UAAA;GAAe,CAAA,CAEnF;EACJ,CAAA,GAAA,KAAW,kBAAC,OAAD;GAAK,WAAU;GAAqC,UAAA;EAAa,CAAA,CAC5E;;AAEb"}
|