fix(ui): 模板选择预览条宽度缩短至1/3,素材卡片改为点击播放交互
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4m45s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 5m1s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m6s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 5m26s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 5m49s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 6m40s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m41s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 7m50s
AI Code Review / AI Code Review (pull_request) Successful in 7m54s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m28s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 9m22s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m25s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 11m18s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m10s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 11m29s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m36s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m56s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m55s
CI/CD Pipeline / Integration Tests (push) Successful in 7m8s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m33s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 4m1s
CI/CD Pipeline / Unit Tests (push) Successful in 16m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m53s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 16m48s
CI/CD Pipeline / CI Gate (pull_request) Successful in 24s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 479h15m12s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 479h15m13s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 479h15m14s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 479h15m15s
CI/CD Pipeline / Production Browser E2E (push) Failing after 479h15m56s
CI/CD Pipeline / Canary Release to Production (push) Failing after 479h15m57s
CI/CD Pipeline / Deploy Production (push) Failing after 479h15m58s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 479h16m2s
CI/CD Pipeline / CI Gate (push) Failing after 479h16m38s
CI/CD Pipeline / Build Production Web Image (push) Failing after 479h16m4s
CI/CD Pipeline / Build Production API Image (push) Failing after 479h16m6s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 479h23m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 479h23m30s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 479h23m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 479h28m57s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 479h32m0s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 479h32m2s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 479h32m48s
CI/CD Pipeline / PR Build Web Image (push) Failing after 479h32m50s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 479h33m12s
CI/CD Pipeline / PR Build API Image (push) Failing after 479h32m52s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 479h33m14s
CI/CD Pipeline / Frontend Lint (push) Failing after 479h33m10s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 479h33m34s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 479h33m16s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 479h38m20s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 479h49m12s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 479h49m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 480h3m10s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4m45s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 5m1s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m6s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 5m26s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 5m49s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 6m40s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m41s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 7m50s
AI Code Review / AI Code Review (pull_request) Successful in 7m54s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m28s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 9m22s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m25s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 11m18s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m10s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 11m29s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m36s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m56s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m55s
CI/CD Pipeline / Integration Tests (push) Successful in 7m8s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m33s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 4m1s
CI/CD Pipeline / Unit Tests (push) Successful in 16m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m53s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 16m48s
CI/CD Pipeline / CI Gate (pull_request) Successful in 24s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 479h15m12s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 479h15m13s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 479h15m14s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 479h15m15s
CI/CD Pipeline / Production Browser E2E (push) Failing after 479h15m56s
CI/CD Pipeline / Canary Release to Production (push) Failing after 479h15m57s
CI/CD Pipeline / Deploy Production (push) Failing after 479h15m58s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 479h16m2s
CI/CD Pipeline / CI Gate (push) Failing after 479h16m38s
CI/CD Pipeline / Build Production Web Image (push) Failing after 479h16m4s
CI/CD Pipeline / Build Production API Image (push) Failing after 479h16m6s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 479h23m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 479h23m30s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 479h23m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 479h28m57s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 479h32m0s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 479h32m2s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 479h32m48s
CI/CD Pipeline / PR Build Web Image (push) Failing after 479h32m50s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 479h33m12s
CI/CD Pipeline / PR Build API Image (push) Failing after 479h32m52s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 479h33m14s
CI/CD Pipeline / Frontend Lint (push) Failing after 479h33m10s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 479h33m34s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 479h33m16s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 479h38m20s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 479h49m12s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 479h49m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 480h3m10s
- generate.css: xx-choice-thumb 宽度从 100% 改为 33%(max-width 80px),居中显示 - ManualMaterialList.tsx: 重构为独立 MaterialCard 组件 - 移除 hover 自动播放逻辑 - 默认显示视频封面/缩略图(非黑色) - 点击播放按钮开始播放,播放时隐藏按钮 - 播放中点击视频区域可暂停并恢复按钮 - 卡片内嵌播放,无弹窗
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* 手动选择素材列表 — 竖屏 9:16 卡片网格
|
||||
* 交互:默认显示封面,点击播放按钮播放,播放中隐藏按钮,点击视频区域暂停
|
||||
*/
|
||||
import React, { useRef, useCallback } from "react"
|
||||
import React, { useRef, useState, useCallback } from "react"
|
||||
import { Typography } from "antd"
|
||||
import type { AssetItem } from "@/api/assets"
|
||||
|
||||
@@ -22,37 +23,230 @@ const fmtDuration = (seconds?: number): string => {
|
||||
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`
|
||||
}
|
||||
|
||||
/** 单个素材卡片 */
|
||||
const MaterialCard: React.FC<{
|
||||
asset: AssetItem
|
||||
checked: boolean
|
||||
onToggle: () => void
|
||||
}> = ({ asset, checked, onToggle }) => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const isVideo = asset.mime_type?.startsWith("video/") ?? false
|
||||
const thumbSrc = asset.thumbnail_url || undefined
|
||||
|
||||
const handlePlayToggle = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
const video = videoRef.current
|
||||
if (!video || !isVideo) return
|
||||
|
||||
if (isPlaying) {
|
||||
video.pause()
|
||||
setIsPlaying(false)
|
||||
} else {
|
||||
video.play().catch(() => {})
|
||||
setIsPlaying(true)
|
||||
}
|
||||
},
|
||||
[isPlaying, isVideo],
|
||||
)
|
||||
|
||||
const handleVideoEnded = useCallback(() => {
|
||||
setIsPlaying(false)
|
||||
}, [])
|
||||
|
||||
const handleCardClick = useCallback(() => {
|
||||
// 如果视频正在播放,点击卡片空白区域暂停视频
|
||||
if (isPlaying) {
|
||||
const video = videoRef.current
|
||||
if (video) {
|
||||
video.pause()
|
||||
setIsPlaying(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
onToggle()
|
||||
}, [isPlaying, onToggle])
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={handleCardClick}
|
||||
style={{
|
||||
position: "relative",
|
||||
aspectRatio: "9 / 16",
|
||||
borderRadius: 10,
|
||||
overflow: "hidden",
|
||||
cursor: "pointer",
|
||||
border: checked ? "2px solid var(--primary-color, #4f46e5)" : "2px solid transparent",
|
||||
boxShadow: checked ? "0 0 0 2px rgba(79, 70, 229, 0.2)" : "0 1px 3px rgba(0, 0, 0, 0.1)",
|
||||
background: "#1e293b",
|
||||
transition: "all 0.15s ease",
|
||||
}}
|
||||
>
|
||||
{/* 视频元素 */}
|
||||
{isVideo && asset.file_url ? (
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={asset.file_url}
|
||||
poster={thumbSrc}
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
onEnded={handleVideoEnded}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
display: "block",
|
||||
}}
|
||||
/>
|
||||
) : thumbSrc ? (
|
||||
<img
|
||||
src={thumbSrc}
|
||||
alt={asset.name}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
display: "block",
|
||||
}}
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement
|
||||
target.style.display = "none"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "linear-gradient(135deg, #334155, #1e293b)",
|
||||
color: "rgba(255,255,255,0.5)",
|
||||
fontSize: 28,
|
||||
}}
|
||||
>
|
||||
{isVideo ? "🎬" : "🎵"}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 底部渐变遮罩 */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: "50%",
|
||||
background: "linear-gradient(0deg, rgba(0,0,0,0.6) 0%, transparent 100%)",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 播放按钮 — 播放中隐藏 */}
|
||||
{!isPlaying && (
|
||||
<div
|
||||
onClick={handlePlayToggle}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: "50%",
|
||||
background: "rgba(99, 102, 241, 0.85)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
zIndex: 3,
|
||||
transition: "opacity 0.2s ease",
|
||||
}}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 文件名(左下角) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
left: 6,
|
||||
right: 50,
|
||||
color: "white",
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
textShadow: "0 1px 2px rgba(0,0,0,0.5)",
|
||||
pointerEvents: "none",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{asset.name}
|
||||
</div>
|
||||
|
||||
{/* 时长(右下角) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
right: 6,
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
color: "white",
|
||||
padding: "1px 5px",
|
||||
borderRadius: 3,
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
pointerEvents: "none",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{fmtDuration(asset.duration)}
|
||||
</div>
|
||||
|
||||
{/* 选中勾选标记(左上角) */}
|
||||
{checked && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 6,
|
||||
left: 6,
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: "50%",
|
||||
background: "var(--primary-color, #4f46e5)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "white",
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
zIndex: 2,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
✓
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ManualMaterialList: React.FC<ManualMaterialListProps> = ({
|
||||
materials,
|
||||
materialsLoading,
|
||||
selectedMaterials,
|
||||
onToggle,
|
||||
}) => {
|
||||
const activeVideoRef = useRef<HTMLVideoElement | null>(null)
|
||||
|
||||
const handleVideoMouseEnter = useCallback((e: React.MouseEvent<HTMLVideoElement>) => {
|
||||
const video = e.currentTarget
|
||||
if (
|
||||
activeVideoRef.current &&
|
||||
activeVideoRef.current !== video &&
|
||||
document.body.contains(activeVideoRef.current)
|
||||
) {
|
||||
activeVideoRef.current.pause()
|
||||
activeVideoRef.current.currentTime = 0
|
||||
}
|
||||
activeVideoRef.current = video
|
||||
video.play().catch(() => {})
|
||||
}, [])
|
||||
|
||||
const handleVideoMouseLeave = useCallback((e: React.MouseEvent<HTMLVideoElement>) => {
|
||||
const video = e.currentTarget
|
||||
video.pause()
|
||||
video.currentTime = 0
|
||||
if (activeVideoRef.current === video) {
|
||||
activeVideoRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 14 }}>
|
||||
{materialsLoading ? (
|
||||
@@ -69,183 +263,14 @@ const ManualMaterialList: React.FC<ManualMaterialListProps> = ({
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
{materials.items.map((m) => {
|
||||
const checked = selectedMaterials.includes(m.id)
|
||||
const isVideo = m.mime_type?.startsWith("video/") ?? false
|
||||
const thumbSrc = m.thumbnail_url || undefined
|
||||
return (
|
||||
<div
|
||||
key={m.id}
|
||||
onClick={() => onToggle(m.id)}
|
||||
style={{
|
||||
position: "relative",
|
||||
aspectRatio: "9 / 16",
|
||||
borderRadius: 10,
|
||||
overflow: "hidden",
|
||||
cursor: "pointer",
|
||||
border: checked
|
||||
? "2px solid var(--primary-color, #4f46e5)"
|
||||
: "2px solid transparent",
|
||||
boxShadow: checked
|
||||
? "0 0 0 2px rgba(79, 70, 229, 0.2)"
|
||||
: "0 1px 3px rgba(0, 0, 0, 0.1)",
|
||||
background: "#1e293b",
|
||||
transition: "all 0.15s ease",
|
||||
}}
|
||||
>
|
||||
{/* 视频/图片封面 */}
|
||||
{isVideo && m.file_url ? (
|
||||
<video
|
||||
src={m.file_url}
|
||||
poster={m.thumbnail_url || undefined}
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="none"
|
||||
onMouseEnter={handleVideoMouseEnter}
|
||||
onMouseLeave={handleVideoMouseLeave}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
display: "block",
|
||||
}}
|
||||
/>
|
||||
) : thumbSrc ? (
|
||||
<img
|
||||
src={thumbSrc}
|
||||
alt={m.name}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
display: "block",
|
||||
}}
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement
|
||||
target.style.display = "none"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "linear-gradient(135deg, #334155, #1e293b)",
|
||||
color: "rgba(255,255,255,0.5)",
|
||||
fontSize: 28,
|
||||
}}
|
||||
>
|
||||
{isVideo ? "🎬" : "🎵"}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 底部渐变遮罩 */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: "50%",
|
||||
background: "linear-gradient(0deg, rgba(0,0,0,0.6) 0%, transparent 100%)",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 播放按钮(中央) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: "50%",
|
||||
background: "rgba(99, 102, 241, 0.85)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* 文件名(左下角) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
left: 6,
|
||||
right: 50,
|
||||
color: "white",
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
textShadow: "0 1px 2px rgba(0,0,0,0.5)",
|
||||
pointerEvents: "none",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{m.name}
|
||||
</div>
|
||||
|
||||
{/* 时长(右下角) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
right: 6,
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
color: "white",
|
||||
padding: "1px 5px",
|
||||
borderRadius: 3,
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
pointerEvents: "none",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{fmtDuration(m.duration)}
|
||||
</div>
|
||||
|
||||
{/* 选中勾选标记(左上角) */}
|
||||
{checked && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 6,
|
||||
left: 6,
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: "50%",
|
||||
background: "var(--primary-color, #4f46e5)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "white",
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
zIndex: 2,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
✓
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{materials.items.map((asset) => (
|
||||
<MaterialCard
|
||||
key={asset.id}
|
||||
asset={asset}
|
||||
checked={selectedMaterials.includes(asset.id)}
|
||||
onToggle={() => onToggle(asset.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -219,6 +219,8 @@
|
||||
}
|
||||
|
||||
.xx-choice-thumb {
|
||||
width: 33%;
|
||||
max-width: 80px;
|
||||
height: 36px;
|
||||
border-radius: var(--radius-sm);
|
||||
display: grid;
|
||||
@@ -226,7 +228,7 @@
|
||||
color: var(--text-inverse);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
margin: 0 auto 6px;
|
||||
}
|
||||
|
||||
.xx-choice-item h4 {
|
||||
|
||||
Reference in New Issue
Block a user