diff --git a/webui/src/components/form/form.module.css b/webui/src/components/form/form.module.css index bf673cac..54638847 100644 --- a/webui/src/components/form/form.module.css +++ b/webui/src/components/form/form.module.css @@ -129,6 +129,120 @@ color: #fff; } +.checkbox { + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + flex-shrink: 0; + box-sizing: border-box; + margin: 0; + border: 2px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + background: transparent; + color: #000; + cursor: pointer; + transition: + border-color 0.18s ease, + background 0.18s ease, + box-shadow 0.18s ease, + transform 0.18s ease; + color-scheme: dark; +} + +.checkbox[data-checked] { + border-color: rgb(var(--accent-light-rgb)); + background: rgb(var(--accent-light-rgb)); +} + +.checkbox[data-focused] { + outline: none; + box-shadow: 0 0 0 3px rgba(var(--accent-light-rgb), 0.14); +} + +.checkboxIndicator { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + opacity: 0; + transition: opacity 0.18s ease; +} + +.checkbox[data-checked] .checkboxIndicator { + opacity: 1; +} + +.checkboxIcon { + color: #000; + font-size: 12px; + font-weight: 700; + line-height: 1; + transform: translateY(-0.5px) scaleX(1.18); +} + +.rangeRoot { + display: inline-flex; + flex: 0 0 160px; + width: 160px; + min-width: 160px; + color-scheme: dark; +} + +.rangeControl { + display: flex; + align-items: center; + width: 100%; + min-height: 28px; + cursor: pointer; +} + +.rangeTrack { + position: relative; + width: 100%; + height: 8px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 999px; + background: rgba(255, 255, 255, 0.08); + box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.04); +} + +.rangeIndicator { + height: 100%; + border-radius: inherit; + background: linear-gradient( + 90deg, + rgba(var(--accent-rgb), 0.95), + rgba(var(--accent-light-rgb), 0.95) + ); +} + +.rangeThumb { + width: 16px; + height: 16px; + border: 1px solid rgba(255, 255, 255, 0.24); + border-radius: 50%; + background: + radial-gradient(circle at 30% 28%, rgba(255, 255, 255, 0.3), transparent 46%), + rgb(var(--accent-light-rgb)); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); + transition: + transform 0.18s ease, + box-shadow 0.18s ease; +} + +.rangeThumb:hover { + transform: scale(1.04); +} + +.rangeThumb:focus-visible { + box-shadow: + 0 0 0 4px rgba(var(--accent-light-rgb), 0.14), + 0 2px 8px rgba(0, 0, 0, 0.28); +} + .optionCardGroup { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); diff --git a/webui/src/components/form/form.test.tsx b/webui/src/components/form/form.test.tsx index 3488a2f6..2feb7a2e 100644 --- a/webui/src/components/form/form.test.tsx +++ b/webui/src/components/form/form.test.tsx @@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest'; import { Button, + Checkbox, FormActions, FormError, FormField, @@ -11,6 +12,7 @@ import { OptionButtonGroup, OptionCard, OptionCardGroup, + RangeInput, Select, TextArea, TextInput, @@ -22,6 +24,8 @@ function FormDemo() { const [category, setCategory] = useState<'wrong_cover' | 'wrong_metadata'>('wrong_cover'); const [priority, setPriority] = useState<'low' | 'normal' | 'high'>('normal'); const [status, setStatus] = useState('open'); + const [archive, setArchive] = useState(false); + const [confidence, setConfidence] = useState(90); return (