feat(ui): compact Color Wheels & Match panel with MiniSlider rulers and 4-column color bands
This commit is contained in:
+112
-77
@@ -4028,7 +4028,7 @@ export function Workspace() {
|
||||
<div>
|
||||
<span className="dev-box-title" style={{ marginBottom: '6px', display: 'block' }}>COLOR BANDS</span>
|
||||
<ChipRow
|
||||
grid
|
||||
grid4
|
||||
chips={HSL_BANDS.map((b) => {
|
||||
const band = recipe.adjustments.hslBands?.[b.id];
|
||||
const moved = !!band && (band[0] !== 0 || band[1] !== 0 || band[2] !== 0);
|
||||
@@ -4050,7 +4050,7 @@ export function Workspace() {
|
||||
|
||||
{hslBand && (
|
||||
<div className="dev-subgroup" style={{ marginTop: '4px' }}>
|
||||
<div className="dev-subgroup-title" style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<div className="dev-subgroup-title" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span>BAND: {HSL_BANDS.find((b) => b.id === hslBand)?.label}</span>
|
||||
{(recipe.adjustments.hslBands?.[hslBand]?.[0] || recipe.adjustments.hslBands?.[hslBand]?.[1] || recipe.adjustments.hslBands?.[hslBand]?.[2]) ? (
|
||||
<button
|
||||
@@ -4082,39 +4082,45 @@ export function Workspace() {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Hue</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-30}
|
||||
max={30}
|
||||
value={bandVals[0]}
|
||||
onChange={(e) => updateBand(0, Number(e.target.value))}
|
||||
/>
|
||||
<span className="dev-row-value">{bandVals[0] > 0 ? `+${bandVals[0]}` : bandVals[0]}</span>
|
||||
</div>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Saturation</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-100}
|
||||
max={100}
|
||||
value={bandVals[1]}
|
||||
onChange={(e) => updateBand(1, Number(e.target.value))}
|
||||
/>
|
||||
<span className="dev-row-value">{bandVals[1] > 0 ? `+${bandVals[1]}` : bandVals[1]}</span>
|
||||
</div>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Luminance</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-100}
|
||||
max={100}
|
||||
value={bandVals[2]}
|
||||
onChange={(e) => updateBand(2, Number(e.target.value))}
|
||||
/>
|
||||
<span className="dev-row-value">{bandVals[2] > 0 ? `+${bandVals[2]}` : bandVals[2]}</span>
|
||||
</div>
|
||||
<MiniSlider
|
||||
label="Hue"
|
||||
value={bandVals[0]}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix={`dev-hsl-${hslBand}`}
|
||||
dataKey={`dev-hsl-${hslBand}-hue`}
|
||||
onChange={(v) => updateBand(0, v)}
|
||||
onReset={() => updateBand(0, 0)}
|
||||
/>
|
||||
<MiniSlider
|
||||
label="Saturation"
|
||||
value={bandVals[1]}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix={`dev-hsl-${hslBand}`}
|
||||
dataKey={`dev-hsl-${hslBand}-sat`}
|
||||
onChange={(v) => updateBand(1, v)}
|
||||
onReset={() => updateBand(1, 0)}
|
||||
/>
|
||||
<MiniSlider
|
||||
label="Luminance"
|
||||
value={bandVals[2]}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix={`dev-hsl-${hslBand}`}
|
||||
dataKey={`dev-hsl-${hslBand}-lum`}
|
||||
onChange={(v) => updateBand(2, v)}
|
||||
onReset={() => updateBand(2, 0)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
@@ -4122,49 +4128,78 @@ export function Workspace() {
|
||||
)}
|
||||
|
||||
<div className="dev-subgroup" style={{ marginTop: '4px' }}>
|
||||
<div className="dev-subgroup-title">IMAGE MASTER</div>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Hue</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-180}
|
||||
max={180}
|
||||
value={recipe.adjustments.hslHue ?? 0}
|
||||
onChange={(e) => {
|
||||
remember();
|
||||
setAdjustment({ hslHue: Number(e.target.value) });
|
||||
}}
|
||||
/>
|
||||
<span className="dev-row-value">{(recipe.adjustments.hslHue ?? 0) > 0 ? `+${recipe.adjustments.hslHue}` : recipe.adjustments.hslHue ?? 0}</span>
|
||||
</div>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Saturation</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-100}
|
||||
max={100}
|
||||
value={recipe.adjustments.hslSat ?? 0}
|
||||
onChange={(e) => {
|
||||
remember();
|
||||
setAdjustment({ hslSat: Number(e.target.value) });
|
||||
}}
|
||||
/>
|
||||
<span className="dev-row-value">{(recipe.adjustments.hslSat ?? 0) > 0 ? `+${recipe.adjustments.hslSat}` : recipe.adjustments.hslSat ?? 0}</span>
|
||||
</div>
|
||||
<div className="dev-row">
|
||||
<span className="dev-row-label">Luminance</span>
|
||||
<input
|
||||
type="range"
|
||||
min={-100}
|
||||
max={100}
|
||||
value={recipe.adjustments.hslLum ?? 0}
|
||||
onChange={(e) => {
|
||||
remember();
|
||||
setAdjustment({ hslLum: Number(e.target.value) });
|
||||
}}
|
||||
/>
|
||||
<span className="dev-row-value">{(recipe.adjustments.hslLum ?? 0) > 0 ? `+${recipe.adjustments.hslLum}` : recipe.adjustments.hslLum ?? 0}</span>
|
||||
<div className="dev-subgroup-title" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span>IMAGE MASTER</span>
|
||||
{((recipe.adjustments.hslHue ?? 0) !== 0 || (recipe.adjustments.hslSat ?? 0) !== 0 || (recipe.adjustments.hslLum ?? 0) !== 0) ? (
|
||||
<button
|
||||
type="button"
|
||||
style={{ background: 'none', border: 'none', color: 'var(--accent)', cursor: 'pointer', fontSize: '10px' }}
|
||||
onClick={() => {
|
||||
remember();
|
||||
setAdjustment({ hslHue: 0, hslSat: 0, hslLum: 0 });
|
||||
}}
|
||||
>
|
||||
RESET
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<MiniSlider
|
||||
label="Hue"
|
||||
value={recipe.adjustments.hslHue ?? 0}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix="dev-hsl-master"
|
||||
dataKey="dev-hsl-master-hue"
|
||||
onChange={(v) => {
|
||||
remember();
|
||||
setAdjustment({ hslHue: v });
|
||||
}}
|
||||
onReset={() => {
|
||||
remember();
|
||||
setAdjustment({ hslHue: 0 });
|
||||
}}
|
||||
/>
|
||||
<MiniSlider
|
||||
label="Saturation"
|
||||
value={recipe.adjustments.hslSat ?? 0}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix="dev-hsl-master"
|
||||
dataKey="dev-hsl-master-sat"
|
||||
onChange={(v) => {
|
||||
remember();
|
||||
setAdjustment({ hslSat: v });
|
||||
}}
|
||||
onReset={() => {
|
||||
remember();
|
||||
setAdjustment({ hslSat: 0 });
|
||||
}}
|
||||
/>
|
||||
<MiniSlider
|
||||
label="Luminance"
|
||||
value={recipe.adjustments.hslLum ?? 0}
|
||||
min={-10}
|
||||
max={10}
|
||||
step={1}
|
||||
defaultValue={0}
|
||||
format={(v) => v.toFixed(1)}
|
||||
prefix="dev-hsl-master"
|
||||
dataKey="dev-hsl-master-lum"
|
||||
onChange={(v) => {
|
||||
remember();
|
||||
setAdjustment({ hslLum: v });
|
||||
}}
|
||||
onReset={() => {
|
||||
remember();
|
||||
setAdjustment({ hslLum: 0 });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -201,6 +201,30 @@
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-row.grid-4 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
.chip-row.grid-4 .chip {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 4px 2px;
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-row.grid-4 .chip .chip-label {
|
||||
font-size: 9.5px;
|
||||
}
|
||||
.chip-row.grid-4 .chip .chip-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
.chip-label {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
|
||||
@@ -72,19 +72,21 @@ export function ChipRow({
|
||||
chips,
|
||||
grid,
|
||||
grid3,
|
||||
grid4,
|
||||
list,
|
||||
glyph,
|
||||
}: {
|
||||
chips: ChipDef[];
|
||||
grid?: boolean;
|
||||
grid3?: boolean;
|
||||
grid4?: boolean;
|
||||
list?: boolean;
|
||||
// The mark drawn at the head of every row of a `list` (a frame, a look). It
|
||||
// is decoration and nothing else — the name beside it is the row.
|
||||
glyph?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={`chip-row${list ? ' list' : ''}${grid3 ? ' grid-3' : grid ? ' grid' : ''}`}>
|
||||
<div className={`chip-row${list ? ' list' : ''}${grid4 ? ' grid-4' : grid3 ? ' grid-3' : grid ? ' grid' : ''}`}>
|
||||
{chips.map((chip) =>
|
||||
chip.divider ? (
|
||||
<span key={chip.key} className="chip-rule" role="separator" aria-orientation="horizontal" />
|
||||
|
||||
Reference in New Issue
Block a user