UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

206 lines (205 loc) • 5.4 kB
import { AXES } from "./axes.mjs"; import { isRtl, lockBody } from "./dom.mjs"; import { timing } from "./transition.mjs"; import { clamp, emitter } from "./utils.mjs"; import { animate, motionValue } from "motion/react"; //#region src/base-ui/DraggablePanel/core/controller.ts const KEY_STEP = 10; const KEY_STEP_FAST = 50; const SCREEN_DELTA = { ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1, ArrowUp: -1 }; const createPanelController = (initial) => { const { emit: notify, subscribe } = emitter(); let element = null; let options = initial; let target = initial.expand ? initial.size : 0; let state = { collapsing: false, dragging: false, folding: false }; let unlock; let foldTo = target; const size = motionValue(target); const content = motionValue(initial.size); const session = { collapsing: false, max: 0, min: 0, sign: 1, start: 0 }; const patch = (next) => { if (Object.entries(next).some(([key, value]) => state[key] !== value)) { state = { ...state, ...next }; notify(); } }; const bounds = () => { const min = Math.max(0, options.min); return { max: options.max === void 0 ? Number.POSITIVE_INFINITY : Math.max(min, options.max), min }; }; const growSign = () => { const axis = AXES[options.placement]; return axis.grow * (axis.vertical || !isRtl(element) ? 1 : -1); }; const fold = (to) => { foldTo = to; patch({ folding: true }); if (size.get() === 0) content.jump(to || content.get()); else if (to > 0) animate(content, to, timing()); animate(size, to, timing()); }; const release = () => { unlock?.(); unlock = void 0; }; const stopDragging = () => { release(); patch({ collapsing: false, dragging: false }); }; const drag = { cancel: () => { if (!state.dragging) return; size.jump(session.start); content.jump(session.start); stopDragging(); }, end: () => { if (!state.dragging) return; const { collapsing, start } = session; const committed = collapsing ? start : size.get(); stopDragging(); if (collapsing) { content.jump(start); options.onExpandChange?.(false); } options.onSizeChange?.(committed, committed - start); }, move: (offset) => { if (!state.dragging) return; const axis = AXES[options.placement]; const pixels = session.start + offset[axis.point] * session.sign; const collapsing = options.collapseThreshold !== void 0 && pixels < options.collapseThreshold; const next = collapsing ? 0 : Math.round(clamp(pixels, session.min, session.max)); size.jump(next); if (!collapsing) content.jump(next); if (collapsing !== session.collapsing) { session.collapsing = collapsing; patch({ collapsing }); } const reported = collapsing ? session.start : next; options.onSizeDragging?.(reported, reported - session.start); }, start: () => { Object.assign(session, { ...bounds(), collapsing: false, sign: growSign(), start: size.get() }); release(); unlock = lockBody(AXES[options.placement].cursor, drag.cancel); patch({ dragging: true, folding: false }); } }; const resizeByKey = (event) => { if (event.key === "Enter" || event.key === " ") { if (!options.onExpandChange) return; event.preventDefault(); options.onExpandChange(!options.expand); return; } const { max, min } = bounds(); const step = (event.shiftKey || event.key === "PageUp" || event.key === "PageDown" ? KEY_STEP_FAST : KEY_STEP) * growSign(); const screen = (AXES[options.placement].vertical ? event.key === "ArrowUp" || event.key === "ArrowDown" : event.key === "ArrowLeft" || event.key === "ArrowRight") ? SCREEN_DELTA[event.key] : void 0; const moves = { End: Number.isFinite(max) ? max : target, Home: min, PageDown: target + step, PageUp: target - step }; const next = screen === void 0 ? moves[event.key] : target + screen * step; if (next === void 0) return; event.preventDefault(); const value = clamp(next, min, max); if (!options.expand && value > 0) options.onExpandChange?.(true); options.onSizeChange?.(value, value - target); }; const sync = (next) => { options = next; const value = next.expand ? next.size : 0; if (size.get() === 0 && value > 0) content.jump(next.size); if (value !== target) { target = value; notify(); } if (state.dragging) return; if (size.get() === target) { size.stop(); content.stop(); if (state.folding) { patch({ folding: false }); content.jump(target || content.get()); } foldTo = target; } else if (!state.folding || foldTo !== target) fold(target); }; return { attach: (node) => { element = node; const stopSettle = size.on("animationComplete", () => { if (size.get() === target) patch({ folding: false }); }); return () => { stopSettle(); release(); element = null; }; }, get axis() { return AXES[options.placement]; }, bounds, drag, motion: { content, size }, get options() { return options; }, reset: () => { if (!options.expand) options.onExpandChange?.(true); options.onSizeChange?.(options.defaultSize, options.defaultSize - target); }, resizeByKey, get state() { return state; }, subscribe, sync, get target() { return target; } }; }; //#endregion export { createPanelController }; //# sourceMappingURL=controller.mjs.map