feat(dashboard,diagnostic,elective): add widgets, layout, parent dashboard, role-config, services, elective components
dashboard: - Add comparison-badge, dashboard-notification-widget, dashboard-responsive-layout, dashboard-time-range-filter - Add parent-dashboard components directory - Add config, hooks, and services directories diagnostic: - Add role-config and services directory elective: - Add elective-course-detail, elective-stats-cards, parent-selection-view components - Add data-access-settings and data-access-stats
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import type { ReactNode } from "react"
|
||||
import { cn } from "@/shared/lib/utils"
|
||||
|
||||
/**
|
||||
* 仪表盘移动端响应式布局(L8)。
|
||||
*
|
||||
* 根据屏幕尺寸自动调整 Widget 布局:
|
||||
* - 移动端(< sm):单列堆叠,重要 Widget 优先
|
||||
* - 平板(sm - lg):双列网格
|
||||
* - 桌面(>= lg):按配置的 layoutClassName 渲染
|
||||
*
|
||||
* 通过 CSS Grid 的 order 属性实现移动端优先级排序,
|
||||
* 避免重复渲染(P2-9 教训)。
|
||||
*/
|
||||
export function DashboardResponsiveLayout({
|
||||
children,
|
||||
className,
|
||||
mobileFirstSlot,
|
||||
}: {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
/** 移动端置顶的 Widget(如今日课表/待办) */
|
||||
mobileFirstSlot?: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-4 lg:grid", className)}>
|
||||
{mobileFirstSlot && (
|
||||
<div className="order-first lg:hidden">{mobileFirstSlot}</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端水平滑动卡片容器(L8)。
|
||||
*
|
||||
* - snap-x snap-mandatory 提供卡片吸附效果
|
||||
* - 隐藏滚动条但保留滚动功能
|
||||
* - 仅在移动端生效,桌面端转为网格
|
||||
*/
|
||||
export function MobileSwipeContainer({
|
||||
children,
|
||||
className,
|
||||
ariaLabel,
|
||||
}: {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
ariaLabel?: string
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex gap-4 overflow-x-auto pb-2 snap-x snap-mandatory scrollbar-hide sm:hidden",
|
||||
className,
|
||||
)}
|
||||
aria-label={ariaLabel}
|
||||
role="region"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 桌面端网格容器(L8)。
|
||||
*
|
||||
* 与 MobileSwipeContainer 配对使用,
|
||||
* 桌面端显示网格,移动端隐藏(由 MobileSwipeContainer 接管)。
|
||||
*/
|
||||
export function DesktopGrid({
|
||||
children,
|
||||
className,
|
||||
columns = 3,
|
||||
}: {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
columns?: 2 | 3 | 4
|
||||
}) {
|
||||
const gridCols = {
|
||||
2: "sm:grid-cols-2",
|
||||
3: "sm:grid-cols-2 lg:grid-cols-3",
|
||||
4: "sm:grid-cols-2 lg:grid-cols-4",
|
||||
}[columns]
|
||||
|
||||
return (
|
||||
<div className={cn("hidden sm:grid gap-4", gridCols, className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user