[feat] number-ball #1
@@ -69,6 +69,7 @@ export default function Page() {
|
||||
number={n}
|
||||
state={actions.getBallState(n)}
|
||||
colorIndex={actions.getBallColorIndex(n)}
|
||||
dimmed={actions.getBallDimmed(n)}
|
||||
size="large"
|
||||
/>
|
||||
))}
|
||||
|
||||
+15
-4
@@ -88,6 +88,7 @@ export interface RollCallActions {
|
||||
clearResult: () => void
|
||||
getBallState: (n: number) => "idle" | "highlighted" | "used"
|
||||
getBallColorIndex: (n: number) => number | undefined
|
||||
getBallDimmed: (n: number) => boolean
|
||||
}
|
||||
|
||||
export const COLORS = [
|
||||
@@ -112,7 +113,7 @@ export const COLORS = [
|
||||
|
||||
export function useRollCallLogic(): [RollCallState, RollCallActions] {
|
||||
const [mode, setMode] = useState<Mode>("single")
|
||||
const [totalTasks, setTotalTasks] = useState(20)
|
||||
const [totalTasks, setTotalTasks] = useState(35)
|
||||
const [pickCount, setPickCount] = useState(1)
|
||||
const [rounds, setRounds] = useState(1)
|
||||
const [allowRepeat, setAllowRepeat] = useState(false)
|
||||
@@ -251,14 +252,15 @@ export function useRollCallLogic(): [RollCallState, RollCallActions] {
|
||||
|
||||
const getBallState = useCallback((n: number): "idle" | "highlighted" | "used" => {
|
||||
if (mode === "single") {
|
||||
const isInRecords = selectedRecords.some(r => r.number === n)
|
||||
if (isInRecords) return "highlighted"
|
||||
// Only highlight current round's selections
|
||||
const isCurrentRound = singleHighlighted.includes(n)
|
||||
if (isCurrentRound) return "highlighted"
|
||||
return "idle"
|
||||
}
|
||||
if (highlighted.has(n)) return "highlighted"
|
||||
if (hasResult && usedAll.size > 0 && !usedAll.has(n)) return "used"
|
||||
return "idle"
|
||||
}, [mode, selectedRecords, highlighted, hasResult, usedAll])
|
||||
}, [mode, singleHighlighted, highlighted, hasResult, usedAll])
|
||||
|
||||
const getBallColorIndex = useCallback((n: number): number | undefined => {
|
||||
if (mode === "single") {
|
||||
@@ -275,6 +277,14 @@ export function useRollCallLogic(): [RollCallState, RollCallActions] {
|
||||
return undefined
|
||||
}, [mode, selectedRecords, groups])
|
||||
|
||||
const getBallDimmed = useCallback((n: number): boolean => {
|
||||
// Only dim in single mode when there are highlighted numbers
|
||||
if (mode === "single" && singleHighlighted.length > 0) {
|
||||
return !singleHighlighted.includes(n)
|
||||
}
|
||||
return false
|
||||
}, [mode, singleHighlighted])
|
||||
|
||||
const state: RollCallState = {
|
||||
mode,
|
||||
totalTasks,
|
||||
@@ -308,6 +318,7 @@ export function useRollCallLogic(): [RollCallState, RollCallActions] {
|
||||
clearResult,
|
||||
getBallState,
|
||||
getBallColorIndex,
|
||||
getBallDimmed,
|
||||
}
|
||||
|
||||
return [state, actions]
|
||||
|
||||
@@ -8,19 +8,23 @@ interface NumberBallProps {
|
||||
state: "idle" | "highlighted" | "used"
|
||||
colorIndex?: number
|
||||
size?: "normal" | "large"
|
||||
dimmed?: boolean
|
||||
}
|
||||
|
||||
export function NumberBall({ number, state, colorIndex, size = "normal" }: NumberBallProps) {
|
||||
export function NumberBall({ number, state, colorIndex, size = "normal", dimmed = false }: NumberBallProps) {
|
||||
const isHighlighted = state === "highlighted"
|
||||
const borderColor = isHighlighted && colorIndex !== undefined
|
||||
const hasColor = colorIndex !== undefined
|
||||
|
||||
const borderColor = hasColor
|
||||
? COLORS[colorIndex]
|
||||
: state === "idle" ? "#e2e6ea"
|
||||
: "#eef0f2"
|
||||
const bgColor = isHighlighted && colorIndex !== undefined
|
||||
const bgColor = isHighlighted && hasColor
|
||||
? COLORS[colorIndex]
|
||||
: "transparent"
|
||||
const textColor = isHighlighted && colorIndex !== undefined
|
||||
const textColor = isHighlighted && hasColor
|
||||
? "#ffffff"
|
||||
: hasColor ? COLORS[colorIndex]
|
||||
: state === "idle" ? "#8a95a1"
|
||||
: "#cdd2d8"
|
||||
|
||||
@@ -30,6 +34,7 @@ export function NumberBall({ number, state, colorIndex, size = "normal" }: Numbe
|
||||
"flex items-center justify-center font-mono select-none transition-all duration-150",
|
||||
size === "normal" && "w-8 h-8 text-sm",
|
||||
size === "large" && "w-14 h-14 text-lg",
|
||||
dimmed && !isHighlighted && "opacity-30",
|
||||
)}
|
||||
style={{
|
||||
border: `1px solid ${borderColor}`,
|
||||
|
||||
@@ -8,13 +8,7 @@ interface SingleModePanelProps {
|
||||
|
||||
export function SingleModePanel({ singleBatches }: SingleModePanelProps) {
|
||||
if (singleBatches.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-12 gap-3">
|
||||
<div className="text-[10px] tracking-[0.2em] text-[#cdd2d8] font-mono uppercase">
|
||||
— AWAITING INPUT —
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
// Reverse to show newest first
|
||||
|
||||
Reference in New Issue
Block a user