feat(sticky): S/M/L text size toggle, live reflow resize, min width

Sticky UX overhaul — clear separation between card resize and text size:

- S/M/L toggle (12/14/18px) in contextual toolbar for sticky text size
- TransformBox: horizontal-only resize for stickies with live text
  reflow during drag (no more stretch-then-snap)
- Hide vertical handles (tc/bc) for sticky-only selections
- Minimum sticky width of 80px enforced in both drag and bake
- TextEditor: sticky background auto-resizes as user types
- textLimits.ts comment corrected to match actual usage

Plain text unchanged: resize gesture scales fontSize via bake.
Fully backward compatible — existing stickies map to nearest preset.
This commit is contained in:
Hiren Kangad
2026-03-13 00:12:23 +05:30
parent a20cab3fcd
commit 1631185e4f
6 changed files with 115 additions and 14 deletions
+19 -1
View File
@@ -123,7 +123,7 @@ export default function Editor({ isPublicView }: EditorProps) {
const [selToolbar, setSelToolbar] = useState<{ x: number; y: number; count: number } | null>(null);
const [textToolbar, setTextToolbar] = useState<
| { kind: 'text'; x: number; y: number; fontFamily: string; fill: string; items: SceneItem[] }
| { kind: 'sticky'; x: number; y: number; fontFamily: string; textColor: string; fill: string; items: SceneItem[] }
| { kind: 'sticky'; x: number; y: number; fontFamily: string; textColor: string; fill: string; stickyFontSize: number; items: SceneItem[] }
| null
>(null);
const [videoCtrl, setVideoCtrl] = useState<{ videoSprite: VideoSprite; screenRect: { x: number; y: number; w: number; h: number } } | null>(null);
@@ -628,6 +628,7 @@ export default function Editor({ isPublicView }: EditorProps) {
kind: 'sticky', x: posX, y: posY,
fontFamily: sd.fontFamily || 'Inter, system-ui, sans-serif',
textColor: sd.textColor || '#1a1a1a', fill: sd.fill || '#ffd43b',
stickyFontSize: sd.fontSize || 14,
items: stickyItems,
});
}
@@ -915,6 +916,7 @@ export default function Editor({ isPublicView }: EditorProps) {
fontFamily={textToolbar.fontFamily}
fill={textToolbar.kind === 'sticky' ? textToolbar.textColor : textToolbar.fill}
noteFill={textToolbar.kind === 'sticky' ? textToolbar.fill : undefined}
stickyFontSize={textToolbar.kind === 'sticky' ? textToolbar.stickyFontSize : undefined}
position={textToolbar.items.length >= 2 ? 'below' : 'above'}
onFontFamilyChange={(family) => {
const scene = canvasRef.current?.getScene();
@@ -971,6 +973,22 @@ export default function Editor({ isPublicView }: EditorProps) {
onCanvasChange(textToolbar.items.map(i => i.id));
updateOverlays();
} : undefined}
onStickySizeChange={textToolbar.kind === 'sticky' ? (fontSize) => {
const scene = canvasRef.current?.getScene();
for (const item of textToolbar.items) {
const d = item.data as StickyObject;
d.fontSize = fontSize;
if (item.displayObject instanceof StickySprite) {
item.displayObject.updateFromData(d);
d.h = item.displayObject.computedHeight;
}
if (scene) scene.updateSpatialEntry(item);
}
setTextToolbar((prev) => prev && prev.kind === 'sticky' ? { ...prev, stickyFontSize: fontSize } : prev);
selectionRef.current?.transformBox.update(textToolbar.items);
onCanvasChange(textToolbar.items.map(i => i.id));
updateOverlays();
} : undefined}
/>
)}