@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
1 lines • 14.2 kB
Source Map (JSON)
{"version":3,"file":"useResizableCalculations-BDITle3Q.cjs","names":[],"sources":["../components/resizable/composables/constraintResolver.ts","../components/resizable/composables/useResizableCalculations.ts"],"sourcesContent":["/**\n * Constraint Resolver\n *\n * Pure functions for resolving the constraint hierarchy for resizable panels.\n * No DOM access, no Vue reactivity, no side effects.\n *\n * @see computeLayout.ts\n * @see ../resizable_constants.ts\n */\n\nimport type { ResizablePanelConfig } from '../resizable_constants';\nimport { parseSizeToPixels } from '../resizable_utils';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * Result of constraint hierarchy calculation.\n * User limits are absolute boundaries; system limits are the operating range within user limits.\n */\nexport interface ConstraintHierarchy {\n // User drag limits (absolute boundaries)\n userMinSizePixels?: number;\n userMaxSizePixels?: number;\n // System scaling limits (operating range within user limits)\n systemMinSizePixels?: number;\n systemMaxSizePixels?: number;\n // Auto-collapse threshold (container width, not panel width)\n collapseSizePixels?: number;\n}\n\n// ============================================================================\n// CLAMPING PRIMITIVE\n// ============================================================================\n\n/**\n * Clamp a value to optional min/max bounds.\n * Single source of truth for all constraint clamping in the resizable system.\n */\nexport function clampSize(value: number, min?: number, max?: number): number {\n let result = value;\n if (min !== undefined) result = Math.max(result, min);\n if (max !== undefined) result = Math.min(result, max);\n return result;\n}\n\n/**\n * Resolve effective min/max for a given constraint tier, then clamp.\n * - 'user' tier: uses userMin/userMax only\n * - 'system' tier: uses systemMin/systemMax, falling back to userMin/userMax\n */\nexport function clampToTier(\n value: number,\n constraints: ConstraintHierarchy,\n tier: 'user' | 'system' = 'system'\n): number {\n const min = tier === 'user'\n ? constraints.userMinSizePixels\n : (constraints.systemMinSizePixels ?? constraints.userMinSizePixels);\n const max = tier === 'user'\n ? constraints.userMaxSizePixels\n : (constraints.systemMaxSizePixels ?? constraints.userMaxSizePixels);\n return clampSize(value, min, max);\n}\n\n// ============================================================================\n// INTERNAL HELPERS\n// ============================================================================\n\n/**\n * Resolve user constraints (drag limits) from panel config.\n */\nexport function resolveUserConstraints(\n panelConfig: ResizablePanelConfig,\n containerSize: number\n): { userMinSizePixels?: number; userMaxSizePixels?: number } {\n return {\n userMinSizePixels: panelConfig.userMinSize ? parseSizeToPixels(panelConfig.userMinSize, containerSize) : undefined,\n userMaxSizePixels: panelConfig.userMaxSize ? parseSizeToPixels(panelConfig.userMaxSize, containerSize) : undefined,\n };\n}\n\n/**\n * Clamp system min to be at least user min.\n * System constraints that exceed user boundaries are silently clamped\n * since this is expected behavior during constraint resolution.\n */\nexport function clampSystemMin(systemMin: number | undefined, userMin: number | undefined): number | undefined {\n if (systemMin === undefined || userMin === undefined) return systemMin;\n return Math.max(systemMin, userMin);\n}\n\n/**\n * Clamp system max to be at most user max.\n * System constraints that exceed user boundaries are silently clamped\n * since this is expected behavior during constraint resolution.\n */\nexport function clampSystemMax(systemMax: number | undefined, userMax: number | undefined): number | undefined {\n if (systemMax === undefined || userMax === undefined) return systemMax;\n return Math.min(systemMax, userMax);\n}\n\n/**\n * Resolve system constraints with fallback to user constraints.\n * System limits are silently clamped to stay within user limits.\n */\nexport function resolveSystemConstraints(\n panelConfig: ResizablePanelConfig,\n containerSize: number,\n userMinSizePixels?: number,\n userMaxSizePixels?: number\n): { systemMinSizePixels?: number; systemMaxSizePixels?: number } {\n // Calculate system limits from props, or fall back to user limits\n const rawSystemMin = panelConfig.systemMinSize\n ? parseSizeToPixels(panelConfig.systemMinSize, containerSize)\n : userMinSizePixels;\n const rawSystemMax = panelConfig.systemMaxSize\n ? parseSizeToPixels(panelConfig.systemMaxSize, containerSize)\n : userMaxSizePixels;\n\n // Validate and clamp system limits within user limits\n const systemMinSizePixels = clampSystemMin(rawSystemMin, userMinSizePixels);\n const systemMaxSizePixels = clampSystemMax(rawSystemMax, userMaxSizePixels);\n\n return { systemMinSizePixels, systemMaxSizePixels };\n}\n\n// ============================================================================\n// PUBLIC API\n// ============================================================================\n\n/**\n * Calculate the complete constraint hierarchy for a panel.\n *\n * This separates user drag limits from system scaling limits:\n * - User limits (userMinSize/userMaxSize) are absolute boundaries for user dragging\n * - System limits (systemMinSize/systemMaxSize) are the operating range for viewport resize\n * - System limits must be within user limits (clamped silently if not)\n *\n * @param panelConfig - Panel configuration\n * @param containerSize - Current container size in pixels\n * @returns Complete constraint hierarchy with all pixel values\n */\nexport function calculateConstraintHierarchy(\n panelConfig: ResizablePanelConfig,\n containerSize: number\n): ConstraintHierarchy {\n // Resolve user constraints (drag limits)\n const { userMinSizePixels, userMaxSizePixels } = resolveUserConstraints(panelConfig, containerSize);\n\n // Resolve system constraints (viewport resize limits) with fallback to user limits\n const { systemMinSizePixels, systemMaxSizePixels } = resolveSystemConstraints(\n panelConfig,\n containerSize,\n userMinSizePixels,\n userMaxSizePixels\n );\n\n // Calculate collapse threshold (container width trigger, NOT a panel size)\n // Don't clamp to container - this threshold is compared AGAINST the container width\n const collapseSizePixels = panelConfig.collapseSize\n ? parseSizeToPixels(panelConfig.collapseSize, containerSize, { clampToContainer: false })\n : undefined;\n\n return {\n userMinSizePixels,\n userMaxSizePixels,\n systemMinSizePixels,\n systemMaxSizePixels,\n collapseSizePixels,\n };\n}\n","import { MIN_PANEL_SIZE_PX } from '../resizable_constants';\nimport type { ResizablePanelState } from '../resizable_constants';\nimport { clampSize } from './constraintResolver';\n\n// ============================================================================\n// RESIZE HANDLING\n// ============================================================================\n\nexport interface ResizeHandler {\n processResizeMove: (\n rawCursorPosition: number,\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState,\n containerSizeValue: number,\n handleId?: string,\n allPanels?: ResizablePanelState[],\n beforePanelLeft?: number\n ) => ResizeMoveResult;\n}\n\nexport interface ResizeMoveResult {\n constrainedCursorPosition: number;\n beforePanelSize: number;\n afterPanelSize: number;\n isValidResize: boolean;\n}\n\nexport function useResizeHandling(containerSize?: () => number) {\n function checkPanelsResizable(\n rawCursorPosition: number,\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState\n ): ResizeMoveResult | null {\n if (beforePanel.resizable === false || afterPanel.resizable === false) {\n return {\n constrainedCursorPosition: rawCursorPosition,\n beforePanelSize: beforePanel.pixelSize || 0,\n afterPanelSize: afterPanel.pixelSize || 0,\n isValidResize: false,\n };\n }\n return null;\n }\n\n function applyPanelConstraints(proposedSize: number, panel: ResizablePanelState): number {\n return clampSize(proposedSize, panel.userMinSizePixels, panel.userMaxSizePixels);\n }\n\n function calculateMaxCursorForAfterPanel(\n afterPanel: ResizablePanelState,\n containerSizeValue: number,\n allPanels?: ResizablePanelState[]\n ): number {\n const minPixels = afterPanel.userMinSizePixels;\n\n if (minPixels === undefined || !allPanels) {\n return containerSizeValue;\n }\n\n const afterPanelIndex = allPanels.findIndex(p => p.id === afterPanel.id);\n const panelsAfterThis = allPanels.slice(afterPanelIndex + 1);\n const totalFixedSpaceAfter = panelsAfterThis.reduce((sum, panel) => sum + (panel.pixelSize || 0), 0);\n\n return containerSizeValue - minPixels - totalFixedSpaceAfter;\n }\n\n function calculateAfterPanelSize(\n cursorPosition: number,\n containerSizeValue: number,\n afterPanel: ResizablePanelState,\n allPanels?: ResizablePanelState[]\n ): number {\n if (!allPanels) {\n return containerSizeValue - cursorPosition;\n }\n\n const afterPanelIndex = allPanels.findIndex(p => p.id === afterPanel.id);\n const panelsAfterThis = allPanels.slice(afterPanelIndex + 1);\n const totalFixedSpaceAfter = panelsAfterThis.reduce((sum, panel) => sum + (panel.pixelSize || 0), 0);\n\n return containerSizeValue - cursorPosition - totalFixedSpaceAfter;\n }\n\n function processResizeMove(\n rawCursorPosition: number,\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState,\n containerSizeValue: number,\n handleId?: string,\n allPanels?: ResizablePanelState[],\n beforePanelLeft?: number\n ): ResizeMoveResult {\n const nonResizableResult = checkPanelsResizable(rawCursorPosition, beforePanel, afterPanel);\n if (nonResizableResult) return nonResizableResult;\n\n const beforeLeft = beforePanelLeft || 0;\n const proposedBeforeSize = rawCursorPosition - beforeLeft;\n const proposedAfterSize = containerSizeValue - rawCursorPosition;\n\n let constrainedBeforeSize = applyPanelConstraints(proposedBeforeSize, beforePanel);\n let constrainedAfterSize = applyPanelConstraints(proposedAfterSize, afterPanel);\n\n const maxCursorForAfterPanel = calculateMaxCursorForAfterPanel(afterPanel, containerSizeValue, allPanels);\n const beforePanelCursor = beforeLeft + constrainedBeforeSize;\n const effectiveCursor = Math.min(beforePanelCursor, maxCursorForAfterPanel);\n\n let constrainedCursorPosition;\n\n if (effectiveCursor < beforePanelCursor) {\n constrainedCursorPosition = effectiveCursor;\n constrainedBeforeSize = constrainedCursorPosition - beforeLeft;\n constrainedAfterSize = calculateAfterPanelSize(\n constrainedCursorPosition,\n containerSizeValue,\n afterPanel,\n allPanels\n );\n } else {\n constrainedCursorPosition = beforePanelCursor;\n constrainedAfterSize = calculateAfterPanelSize(\n constrainedCursorPosition,\n containerSizeValue,\n afterPanel,\n allPanels\n );\n }\n\n return {\n constrainedCursorPosition,\n beforePanelSize: constrainedBeforeSize,\n afterPanelSize: constrainedAfterSize,\n isValidResize: constrainedBeforeSize >= MIN_PANEL_SIZE_PX && constrainedAfterSize >= MIN_PANEL_SIZE_PX,\n };\n }\n\n return {\n processResizeMove,\n };\n}\n"],"mappings":"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"}