feat(studio): MY PHOTOS, FAVORITED, PHOTO STYLE and RECIPES read as a list of names, and their column is the visitor's

This commit is contained in:
locpham
2026-10-10 21:47:34 +07:00
parent 3e7a826f59
commit 22753ef50b
3 changed files with 93 additions and 7 deletions
+49 -3
View File
@@ -1,4 +1,5 @@
import { useEffect, useRef } from 'react';
import { keep, recall } from '../remember';
// One strip at a time — the same shape as the phone's AdjustmentPanel: a ruler
// row for the open slider, an options strip for the open group, then the tab's
@@ -62,10 +63,13 @@ export interface SliderDef {
// `grid` lays the row out as the table a set of same-kind values is read as
// (WB's presets, LIGHT's two Color Chromes) instead of the wrapping strip the
// option rows use.
export function ChipRow({ chips, grid, grid3 }: { chips: ChipDef[]; grid?: boolean; grid3?: boolean }) {
// option rows use. `list` is the other end of it: the rows that are a LIST OF
// NAMES — MY PHOTOS' frames, FAVORITED and RECIPES' looks, PHOTO STYLE's sims —
// drop the button's chrome and read as plain text, with only the accent marking
// the one in force (see .chip-row.list).
export function ChipRow({ chips, grid, grid3, list }: { chips: ChipDef[]; grid?: boolean; grid3?: boolean; list?: boolean }) {
return (
<div className={`chip-row${grid3 ? ' grid-3' : grid ? ' grid' : ''}`}>
<div className={`chip-row${list ? ' list' : ''}${grid3 ? ' grid-3' : grid ? ' grid' : ''}`}>
{chips.map((chip) =>
chip.divider ? (
<span key={chip.key} className="chip-rule" role="separator" aria-orientation="horizontal" />
@@ -114,6 +118,48 @@ export function ChipRow({ chips, grid, grid3 }: { chips: ChipDef[]; grid?: boole
);
}
// The edge of a column you can drag: the width is written on the column itself
// and remembered (remember.ts), so the next visit opens at the size it was left
// at. Drag, not a panel: the grip appears where the column's edge already is.
// ponytail: the DOM takes the width and React never learns it — a column width
// is presentation, not state, and a re-render carrying it would fight the drag.
export function ColGrip({ col, min = 110, max = 560 }: { col: string; min?: number; max?: number }) {
const ref = useRef<HTMLDivElement>(null);
const key = `studio-col-${col}`;
useEffect(() => {
const box = ref.current?.parentElement;
const kept = recall(key);
if (box && kept) box.style.width = kept;
}, [key]);
return (
<div
ref={ref}
className="col-grip"
data-key={`col-grip-${col}`}
role="separator"
aria-orientation="vertical"
aria-label={`resize ${col}`}
onPointerDown={(e) => {
const box = ref.current?.parentElement;
if (!box) return;
e.preventDefault();
const from = e.clientX;
const start = box.getBoundingClientRect().width;
const move = (m: PointerEvent) => {
box.style.width = `${Math.round(Math.min(max, Math.max(min, start + m.clientX - from)))}px`;
};
const up = () => {
window.removeEventListener('pointermove', move);
window.removeEventListener('pointerup', up);
keep(key, box.style.width);
};
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', up);
}}
/>
);
}
export function SliderRow({ slider }: { slider: SliderDef }) {
// The wheel belongs to the open ruler: one notch is one step, held inside the
// parameter's own range. The listener is native because React's is passive —