import React, { createContext, useContext, useMemo } from 'react' export interface ScaleContextValue { scale: number /** Full grid cell size: 18 × scale. Used for layout/positioning. */ slotSize: number /** Item content area size: slotSize - 2 × borderPx. This is the slot div size. */ contentSize: number /** 1 unscaled pixel, Math.max(1, Math.round(scale)). Offset from grid cell to content area. */ borderPx: number fontSize: number borderRadius: number pixelSize: number getCSSVars(): React.CSSProperties } const BASE_SLOT_SIZE = 18 const ScaleContext = createContext({ scale: 2, slotSize: BASE_SLOT_SIZE * 2, contentSize: BASE_SLOT_SIZE * 2 - 4, borderPx: 2, fontSize: 12, borderRadius: 0, pixelSize: 2, getCSSVars: () => ({}), }) export function useScale(): ScaleContextValue { return useContext(ScaleContext) } interface ScaleProviderProps { scale?: number children: React.ReactNode } export function ScaleProvider({ scale = 2, children }: ScaleProviderProps) { const value = useMemo(() => { const slotSize = BASE_SLOT_SIZE * scale const fontSize = 7 * scale const pixelSize = scale const borderPx = Math.max(1, Math.round(scale)) const contentSize = slotSize - 2 * borderPx return { scale, slotSize, contentSize, borderPx, fontSize, borderRadius: 0, pixelSize, getCSSVars: () => ({ '--mc-scale': scale, '--mc-slot-size': `${slotSize}px`, '--mc-font-size': `${fontSize}px`, '--mc-pixel': `${pixelSize}px`, '--mc-border': `${borderPx}px`, '--mc-gap': `${2 * pixelSize}px`, '--mc-padding': `${4 * pixelSize}px`, '--mc-count-font': `${Math.round(6 * scale)}px`, } as React.CSSProperties), } }, [scale]) return (
{children}
) }