refactor(sticky): fixed-width card with S/M/L/XL/XXL font presets

Stickies are now fixed-width cards — no resize handles shown.
Text size controlled via 5 presets (10/14/18/24/32px).
On creation, zoom-aware screenToWorld snaps to nearest preset.
Height auto-adjusts from content. Plain text resize unchanged.

Removes: sticky width bake, live reflow drag, min width constraint.
Backward compatible — existing stickies map to nearest preset.
This commit is contained in:
Hiren Kangad
2026-03-13 00:18:41 +05:30
parent 1631185e4f
commit b081ed7169
5 changed files with 37 additions and 60 deletions
+5 -27
View File
@@ -9,7 +9,6 @@
import { Container, Graphics, FederatedPointerEvent, Text, TextStyle } from 'pixi.js';
import type { Viewport } from 'pixi-viewport';
import { type SceneItem, getItemWorldBounds } from './SceneManager';
import { StickySprite } from './sprites/StickySprite';
import type { SnapGuides } from './SnapGuides';
// ---------------------------------------------------------------------------
@@ -21,7 +20,6 @@ const BORDER_WIDTH = 1.5;
const HANDLE_SIZE = 8;
const HANDLE_FILL = 0xffffff;
const HANDLE_STROKE = 0x4a90d9;
const MIN_STICKY_WIDTH = 80;
type HandleId = 'tl' | 'tc' | 'tr' | 'ml' | 'mr' | 'bl' | 'bc' | 'br';
@@ -199,11 +197,9 @@ export class TransformBox extends Container {
const s = HANDLE_SIZE / zoom;
const half = s / 2;
// Vertical-only handles hidden for sticky-only selections (width-only resize)
const hideVertical = this._stickyOnly;
for (const [id, handle] of this._handles) {
if (hideVertical && (id === 'tc' || id === 'bc')) {
// No resize handles for sticky-only selections (fixed width, auto height)
if (this._stickyOnly) {
handle.visible = false;
handle.eventMode = 'none';
continue;
@@ -329,11 +325,6 @@ export class TransformBox extends Container {
}
}
// Sticky-only: horizontal resize only — fy stays 1
if (this._stickyOnly) {
fy = 1;
}
// Compute the anchor point (fixed edge of bounding box)
let anchorX = ob.x; // default: top-left is fixed (br, mr, bc handles)
let anchorY = ob.y;
@@ -354,22 +345,9 @@ export class TransformBox extends Container {
item.data.x = anchorX + (orig.x - anchorX) * fx;
item.data.y = anchorY + (orig.y - anchorY) * fy;
if (this._stickyOnly && item.type === 'sticky') {
// Live reflow: bake width directly so text re-wraps every frame
item.data.w = Math.max(MIN_STICKY_WIDTH, Math.round(orig.w * fx));
item.data.sx = orig.sx > 0 ? 1 : -1;
item.data.sy = orig.sy > 0 ? 1 : -1;
if (item.displayObject instanceof StickySprite) {
item.displayObject.updateFromData(item.data as any);
item.data.h = item.displayObject.computedHeight;
}
item.displayObject.scale.set(item.data.sx, item.data.sy);
} else {
// Normal proportional scale
item.data.sx = orig.sx * fx;
item.data.sy = orig.sy * fy;
item.displayObject.scale.set(item.data.sx, item.data.sy);
}
item.data.sx = orig.sx * fx;
item.data.sy = orig.sy * fy;
item.displayObject.scale.set(item.data.sx, item.data.sy);
item.displayObject.position.set(item.data.x, item.data.y);
this._onItemTransform?.(item);
+4 -10
View File
@@ -1,23 +1,17 @@
/**
* textLimits — single source of truth for font-size constraints.
* textLimits — font-size constraints for plain text items.
*
* Used by creation defaults to keep initial sizes reasonable.
* Direct manipulation (resize-bake) intentionally bypasses these
* clamps so users can scale text to any size.
*
* Sticky notes use discrete presets (S/M/L/XL/XXL) defined in
* TextFormatToolbar.tsx — not continuous clamps.
*/
// Plain text: generous range for direct manipulation
const TEXT_FONT_MIN = 6;
const TEXT_FONT_MAX = 160;
// Sticky note text: narrower range (card layout breaks at extremes)
const STICKY_FONT_MIN = 8;
const STICKY_FONT_MAX = 96;
export function clampTextFontSize(size: number): number {
return Math.round(Math.min(TEXT_FONT_MAX, Math.max(TEXT_FONT_MIN, size)));
}
export function clampStickyFontSize(size: number): number {
return Math.round(Math.min(STICKY_FONT_MAX, Math.max(STICKY_FONT_MIN, size)));
}
+3 -2
View File
@@ -13,7 +13,8 @@ import type { SelectionManager } from './SelectionManager';
import { DrawingSprite } from './sprites/DrawingSprite';
import type { DrawingObject } from './scene-format';
import { TextEditor } from './TextEditor';
import { clampTextFontSize, clampStickyFontSize } from './textLimits';
import { clampTextFontSize } from './textLimits';
import { snapToStickyPreset } from '../components/TextFormatToolbar';
export enum ToolType {
SELECT = 'SELECT',
@@ -345,7 +346,7 @@ export function activateTool(
name: '',
visible: true,
text: '',
fontSize: clampStickyFontSize(screenToWorld(DEFAULT_STICKY_SCREEN_FONT, zoom, 8, 96)),
fontSize: snapToStickyPreset(screenToWorld(DEFAULT_STICKY_SCREEN_FONT, zoom, 8, 40)),
fontFamily: 'Inter, system-ui, sans-serif',
fill: '#ffd43b', // default yellow
textColor: '#1a1a1a',