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:
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user