Add D.RANGE toggle chip to LIGHT tab; letterbox camera polaroid to 3:4 card
This commit is contained in:
@@ -13,6 +13,9 @@ import { FRAMES } from '../utils/frameUtils';
|
||||
// Darkroom-flavoured: amber = active/edited, red = destructive, mono labels.
|
||||
|
||||
type TabId = 'recipes' | 'iq' | 'wb' | 'filters' | 'frame';
|
||||
// Choice-group keys that open a strip above the chip row: color chrome groups
|
||||
// (wb tab) and the D.RANGE selector (iq tab).
|
||||
type GroupKey = 'cx' | 'cxb' | 'dr';
|
||||
|
||||
interface AdjustmentPanelProps {
|
||||
activeTab: TabId;
|
||||
@@ -72,9 +75,9 @@ export default function AdjustmentPanel({
|
||||
// The single parameter whose slider row is open — a new selection replaces
|
||||
// the previous row (one row at a time, above the chips).
|
||||
const [openParam, setOpenParam] = useState<string | null>(null);
|
||||
// The single color-chrome group whose OFF/WEAK/STRONG strip is open above the
|
||||
// chips (same one-row-at-a-time pattern as openParam, but for choice chips).
|
||||
const [openGroup, setOpenGroup] = useState<'cx' | 'cxb' | null>(null);
|
||||
// The single choice-group whose strip (OFF/WEAK/STRONG, or the DR ladder) is
|
||||
// open above the chips (same one-row-at-a-time pattern as openParam).
|
||||
const [openGroup, setOpenGroup] = useState<GroupKey | null>(null);
|
||||
useEffect(() => {
|
||||
setOpenParam(null);
|
||||
setOpenGroup(null);
|
||||
@@ -85,7 +88,7 @@ export default function AdjustmentPanel({
|
||||
setOpenParam((prev) => (prev === key ? null : key));
|
||||
};
|
||||
|
||||
const toggleGroup = (g: 'cx' | 'cxb') => {
|
||||
const toggleGroup = (g: GroupKey) => {
|
||||
haptic();
|
||||
setOpenGroup((prev) => (prev === g ? null : g));
|
||||
};
|
||||
@@ -210,22 +213,46 @@ export default function AdjustmentPanel({
|
||||
}
|
||||
};
|
||||
|
||||
// Color-chrome groups: one toggle chip among the WB chips (COLOR CHROME /
|
||||
// CHROME BLUE); tapping it reveals the OFF/WEAK/STRONG strip above the chip
|
||||
// row. Amber text while an effect is active, amber border while its strip is
|
||||
// open — same affordances as the parameter chips.
|
||||
const chromeGroup: Record<
|
||||
'cx' | 'cxb',
|
||||
{ label: string; value: ColorAdjustments['colorChrome']; onChange: (v: ColorAdjustments['colorChrome']) => void }
|
||||
> = {
|
||||
cx: { label: 'COLOR CHROME', value: a.colorChrome || 'none', onChange: (v) => onUpdateAdjustments({ colorChrome: v }) },
|
||||
cxb: { label: 'CHROME BLUE', value: a.colorChromeBlue || 'none', onChange: (v) => onUpdateAdjustments({ colorChromeBlue: v }) },
|
||||
};
|
||||
// Choice-groups: one toggle chip sits in the chip row (COLOR CHROME /
|
||||
// CHROME BLUE among the WB chips, D.RANGE after the LIGHT params); tapping it
|
||||
// reveals a strip above the chip row with that group's options. Amber text
|
||||
// while a value differs from the group's off value, amber border while its
|
||||
// strip is open — same affordances as the parameter chips.
|
||||
const chromeOptions = [
|
||||
{ v: 'none', d: 'OFF' },
|
||||
{ v: 'weak', d: 'WEAK' },
|
||||
{ v: 'strong', d: 'STRONG' },
|
||||
];
|
||||
const drOptions = [
|
||||
{ v: 'auto', d: 'AUTO' },
|
||||
{ v: '100', d: 'DR100' },
|
||||
{ v: '200', d: 'DR200' },
|
||||
{ v: '400', d: 'DR400' },
|
||||
];
|
||||
const groupDefs: Record<GroupKey, { label: string; off: string; value: string; options: { v: string; d: string }[]; onPick: (v: string) => void }> = {
|
||||
cx: {
|
||||
label: 'COLOR CHROME', off: 'none', value: a.colorChrome || 'none', options: chromeOptions,
|
||||
onPick: (v) => onUpdateAdjustments({ colorChrome: v as ColorAdjustments['colorChrome'] }),
|
||||
},
|
||||
cxb: {
|
||||
label: 'CHROME BLUE', off: 'none', value: a.colorChromeBlue || 'none', options: chromeOptions,
|
||||
onPick: (v) => onUpdateAdjustments({ colorChromeBlue: v as ColorAdjustments['colorChromeBlue'] }),
|
||||
},
|
||||
dr: {
|
||||
label: 'D.RANGE', off: 'auto', value: String(a.dynamicRange ?? 'auto'), options: drOptions,
|
||||
onPick: (v) =>
|
||||
onUpdateAdjustments({
|
||||
dynamicRange: (v === 'auto' ? 'auto' : parseInt(v, 10)) as ColorAdjustments['dynamicRange'],
|
||||
}),
|
||||
},
|
||||
};
|
||||
const groupChip = (g: GroupKey): ChipDef => ({
|
||||
key: `grp-${g}`,
|
||||
label: groupDefs[g].label,
|
||||
active: openGroup === g,
|
||||
amberValue: openGroup !== g && groupDefs[g].value !== groupDefs[g].off,
|
||||
onPress: () => toggleGroup(g),
|
||||
});
|
||||
|
||||
// ---- per-tab chip rows (bottom, above the tool rail) -----------------------
|
||||
const chipsRow = (() => {
|
||||
@@ -258,36 +285,18 @@ export default function AdjustmentPanel({
|
||||
}
|
||||
|
||||
case 'iq':
|
||||
// LIGHT: continuous params on one row; D.RANGE is a toggle chip that
|
||||
// opens its AUTO/DR100/DR200/DR400 strip above (same as the chrome
|
||||
// groups on WB).
|
||||
return (
|
||||
<View>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{choiceChips(
|
||||
'dr',
|
||||
[
|
||||
{ v: 'auto', d: 'AUTO' },
|
||||
{ v: '100', d: 'DR100' },
|
||||
{ v: '200', d: 'DR200' },
|
||||
{ v: '400', d: 'DR400' },
|
||||
],
|
||||
String(a.dynamicRange ?? 'auto'),
|
||||
(val) =>
|
||||
onUpdateAdjustments({
|
||||
dynamicRange: (val === 'auto' ? 'auto' : parseInt(val, 10)) as ColorAdjustments['dynamicRange'],
|
||||
})
|
||||
)}
|
||||
</ScrollView>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{paramChips(paramDefs.iq)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{paramChips(paramDefs.iq)}
|
||||
{renderChip(groupChip('dr'))}
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
case 'wb':
|
||||
@@ -313,15 +322,7 @@ export default function AdjustmentPanel({
|
||||
wbApply
|
||||
)}
|
||||
{paramChips(paramDefs.wb)}
|
||||
{(['cx', 'cxb'] as const).map((g) =>
|
||||
renderChip({
|
||||
key: `grp-${g}`,
|
||||
label: chromeGroup[g].label,
|
||||
active: openGroup === g,
|
||||
amberValue: openGroup !== g && chromeGroup[g].value !== 'none',
|
||||
onPress: () => toggleGroup(g),
|
||||
})
|
||||
)}
|
||||
{(['cx', 'cxb'] as const).map((g) => renderChip(groupChip(g)))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
@@ -417,14 +418,12 @@ export default function AdjustmentPanel({
|
||||
>
|
||||
{renderChip({
|
||||
key: `grp-hint-${openGroup}`,
|
||||
label: chromeGroup[openGroup].label,
|
||||
label: groupDefs[openGroup].label,
|
||||
disabled: true,
|
||||
active: false,
|
||||
onPress: () => {},
|
||||
})}
|
||||
{choiceChips(openGroup, chromeOptions, chromeGroup[openGroup].value, (v) =>
|
||||
chromeGroup[openGroup].onChange(v as ColorAdjustments['colorChrome'])
|
||||
)}
|
||||
{choiceChips(openGroup, groupDefs[openGroup].options, groupDefs[openGroup].value, groupDefs[openGroup].onPick)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -395,14 +395,18 @@ export default function Viewfinder({
|
||||
return { dx: (vw - dw) / 2, dy: (vh - dh) / 2, dw, dh };
|
||||
}, [skiaImage, vw, vh]);
|
||||
// Polaroid card geometry (JS/overlay space) for the CURRENT mode. Library
|
||||
// stills keep the photo aspect; camera previews use the screen aspect (the
|
||||
// feed fills the screen, so the card overlays it in view coordinates).
|
||||
// stills keep the photo aspect; the camera assumes the upright capture
|
||||
// aspect of the photo output (4:3 sensor → 3:4 portrait), NOT the screen
|
||||
// aspect: a screen-aspect window made the card nearly fullscreen and
|
||||
// pole-like, whereas the real print is a shorter 3:4 card letterboxed on
|
||||
// screen. ponytail: verified on Xiaomi quality-mode captures (4:3); if the
|
||||
// session negotiates 16:9 photos, change to 9/16.
|
||||
const polaroidGeo = useMemo(() => {
|
||||
if (selectedFrame !== 'polaroid') return null;
|
||||
const aspect =
|
||||
mode === 'library' && skiaImage && skiaImage.width() > 0 && skiaImage.height() > 0
|
||||
? skiaImage.width() / skiaImage.height()
|
||||
: vw / vh;
|
||||
: 3 / 4;
|
||||
return polaroidLayout(vw, vh, aspect, 'contain');
|
||||
}, [selectedFrame, mode, skiaImage, vw, vh]);
|
||||
const onLibTouchStart = (e: any) => {
|
||||
|
||||
Reference in New Issue
Block a user