@classic-homes/data-panel-core
v0.0.4
Published
Core utilities and types for DataPanel components
Readme
@classic-homes/data-panel-core
Core utilities and types for DataPanel components. This package provides shared functionality for building flexible, draggable, resizable panels across React and Svelte implementations.
Installation
npm install @classic-homes/data-panel-coreQuick Start
import {
// Types
type PanelMode,
type PanelEdge,
type DataPanelState,
// Default values
DEFAULT_PANEL_STATE,
DEFAULT_CONSTRAINTS,
// Utilities
constrainSize,
constrainPosition,
loadPanelState,
savePanelState,
} from '@classic-homes/data-panel-core';
// Load persisted state
const persisted = loadPanelState('my-panel');
const state = mergeWithDefaults(persisted);
// Constrain panel size
const size = constrainSize({ width: 500, height: 600 }, { minWidth: 300, maxWidth: 800 });API Reference
Types
Panel Configuration Types
type PanelMode = 'pinned' | 'detached';
type PanelEdge = 'left' | 'right' | 'top' | 'bottom';
type PanelVariant = 'full' | 'card';
type ResizeHandle =
| 'top'
| 'right'
| 'bottom'
| 'left'
| 'top-left'
| 'top-right'
| 'bottom-left'
| 'bottom-right';State Types
interface Position {
x: number;
y: number;
}
interface Size {
width: number;
height: number;
}
interface PanelConstraints {
minWidth?: number;
maxWidth?: number;
minHeight?: number;
maxHeight?: number;
}
interface DataPanelState {
mode: PanelMode;
variant: PanelVariant;
edge: PanelEdge;
isExpanded: boolean;
detachedPosition: Position;
detachedSize: Size;
pinnedSize: number;
cardSnapIndex: number;
}Interaction State Types
interface DragState {
isDragging: boolean;
startPosition: Position;
startPanelPosition: Position;
currentPosition: Position;
}
interface ResizeState {
isResizing: boolean;
handle: ResizeHandle | null;
startPosition: Position;
startSize: Size;
startPanelPosition: Position;
}
interface PinnedDragState {
isDragging: boolean;
hasDetached: boolean;
startPosition: Position;
currentPosition: Position;
edge: PanelEdge;
pinnedSize: number;
}Constants
Default Values
import {
DEFAULT_PANEL_STATE, // Default DataPanelState
DEFAULT_CONSTRAINTS, // Default PanelConstraints
DEFAULT_CARD_CONFIG, // Default CardConfig
DEFAULT_SNAP_POINTS, // Default snap points { peek: 0.25, half: 0.5, full: 1.0 }
} from '@classic-homes/data-panel-core';Card Mode Constants
import {
CARD_HEADER_HEIGHT, // 72 - Height of collapsed card header
CARD_EXPANDED_RATIO, // 0.85 - Max height as ratio of viewport
CARD_MIN_WIDTH, // 280 - Minimum card width
} from '@classic-homes/data-panel-core';Drag Utilities
State Initialization
// Create initial drag state
const dragState = createDragState();
// Create initial resize state
const resizeState = createResizeState();
// Create pinned drag-to-detach state
const pinnedDragState = createPinnedDragState();Pointer Handling
// Get position from any pointer event type
const position = getPointerPosition(event); // MouseEvent | PointerEvent | TouchEvent
// Calculate new panel position during drag
const newPosition = calculateDragPosition(dragState, currentPointer);
// Calculate new size and position during resize
const { position, size } = calculateResize(resizeState, currentPointer);Resize Handles
// Get cursor style for a resize handle
const cursor = getResizeCursor('top-left'); // 'nwse-resize'
// Get the resize handle for a pinned panel edge
const handle = getPinnedResizeHandle('right'); // 'left'
// Get all handles for detached mode
const handles = getDetachedResizeHandles(); // ['top', 'right', 'bottom', 'left', ...]
// Check if pointer is near a resize handle
const nearHandle = isNearResizeHandle(mousePos, panelRect, threshold);Pinned Drag-to-Detach
// Calculate distance dragged away from edge
const distance = calculateDetachDistance(startPos, currentPos, 'right');
// Calculate rubber-band pull offset
const offset = calculatePullOffset(distance, threshold, maxPull);
// Get new position when detaching
const newPos = calculateDetachedPositionFromPinned(
pointerPos,
pinnedSize,
detachedSize,
edge,
viewportWidth,
viewportHeight
);
// Get CSS transform for pull effect
const transform = getPinnedPullTransform(pullOffset, 'right'); // 'translateX(-10px)'Constraint Utilities
Value Clamping
// Clamp a value between min and max
const clamped = clamp(value, min, max);
// Constrain size to min/max bounds
const size = constrainSize({ width: 1000, height: 1000 }, constraints);
// Constrain position within viewport
const pos = constrainPosition(position, size, viewportWidth, viewportHeight, padding);
// Combined size and position constraint during resize
const { position, size } = constrainResize(newPos, newSize, constraints, vw, vh);Pinned Panel Constraints
// Constrain width for horizontal edges (left/right)
const width = constrainPinnedWidth(width, constraints, viewportWidth, maxRatio);
// Constrain height for vertical edges (top/bottom)
const height = constrainPinnedHeight(height, constraints, viewportHeight, maxRatio);Edge Detection
// Check if position should snap to an edge
const { shouldSnap, edge, distance } = detectEdgeSnap(
position,
size,
viewportWidth,
viewportHeight,
threshold
);
// Edge type helpers
const isHoriz = isHorizontalEdge('left'); // true
const isVert = isVerticalEdge('top'); // truePosition Styles
// Get CSS styles for pinned panel
const styles = getPinnedPositionStyles('right', 400);
// { top: '0', right: '0', bottom: '0', width: '400px', height: '100%' }
// Get CSS styles for detached panel
const styles = getDetachedPositionStyles({ x: 100, y: 200 }, { width: 400, height: 500 });
// { top: '200px', left: '100px', width: '400px', height: '500px' }
// Get centered initial position
const pos = getInitialDetachedPosition(size, viewportWidth, viewportHeight);Card Mode Utilities
// Calculate snap point height in pixels
const height = getSnapPointHeight(0.5, viewportHeight, headerHeight);
// Get CSS styles for card mode
const styles = getCardPositionStyles(0.5, viewportWidth, viewportHeight, cardConfig);
// Find nearest snap point to current height
const index = findNearestSnapIndex(currentHeight, viewportHeight, snapPoints);
// Determine snap based on swipe velocity
const index = getSnapIndexFromVelocity(currentIndex, velocity, snapPoints, threshold);
// Calculate drag velocity
const velocity = calculateDragVelocity(startY, endY, startTime, endTime);
// Check if card should be dismissed
const dismiss = shouldDismissCard(currentHeight, viewportHeight, threshold);
// Get labels for snap points
const labels = getSnapPointLabels([0.25, 0.5, 1.0]); // ['Peek', 'Half', 'Full']Persistence Utilities
// Load state from localStorage
const state = loadPanelState('my-panel-key');
// Save state to localStorage
savePanelState('my-panel-key', panelState);
// Clear persisted state
clearPanelState('my-panel-key');
// Merge persisted state with defaults
const fullState = mergeWithDefaults(persisted, customDefaults);
// Create debounced save function
const debouncedSave = createDebouncedSave('my-panel-key', 300);
debouncedSave(state); // Saves after 300ms of inactivityUsage with React/Svelte
This package is framework-agnostic and provides the core logic for DataPanel components. Use with:
@classic-homes/react- React DataPanel component@classic-homes/svelte- Svelte DataPanel component
Both implementations use this package internally for consistent behavior.
License
MIT
