@activecollab/components
Version:
ActiveCollab Components
145 lines (141 loc) • 5.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useStackedCardResize = void 0;
var _react = require("react");
var _resizePolicy = require("./resizePolicy");
/**
* The one place pointer and keyboard input meet the resize policy.
*
* `StackedCard`'s built-in grip reports raw deltas and knows no policy — enough
* for a host that just tracks a width. This hook is the full contract: it
* captures the gesture start, funnels both inputs through the pure policy in
* `resizePolicy.ts`, streams preview events and fires exactly ONE commit per
* gesture. It returns the props to spread on the grip, including the live
* separator ARIA.
*
* Preview versus commit (spec §7e): the continuous stream is live feedback
* only. A host creates an undo entry on the COMMIT, never on a preview — and a
* gesture that never changed the size commits nothing at all, so a bare click
* on the grip is not a change.
*
* The hook never touches the card's content: the intrinsic minimum is read
* through `contentMin`, which may be a getter so a card that measures its own
* footer can answer at the moment it is asked.
*/
/** The props to spread on the grip — `StackedCard`'s `resizeHandleProps`. */
var sameSize = function sameSize(a, b) {
return a.w === b.w && a.h === b.h;
};
var useStackedCardResize = exports.useStackedCardResize = function useStackedCardResize(_ref) {
var size = _ref.size,
policy = _ref.policy,
cardId = _ref.cardId,
contentMin = _ref.contentMin,
_ref$axis = _ref.axis,
axis = _ref$axis === void 0 ? "both" : _ref$axis,
_ref$scale = _ref.scale,
scale = _ref$scale === void 0 ? 1 : _ref$scale,
_ref$label = _ref.label,
label = _ref$label === void 0 ? "Resize card" : _ref$label,
onResize = _ref.onResize,
onResizeCommit = _ref.onResizeCommit;
// The pointer gesture's anchor: the size + content-min captured on pointer
// down, plus the pointer origin the delta is measured from.
var pointerStart = (0, _react.useRef)(null);
// Mirrors the latest emitted size so pointer-up / key-up can commit exactly
// what preview last showed, without a stale closure.
var latest = (0, _react.useRef)(size);
latest.current = size;
// Whether this gesture actually moved the card. A bare click, or a Home on an
// axis with no floor, leaves it false and commits nothing.
var dirty = (0, _react.useRef)(false);
// Read through refs so the handlers below can stay stable across renders
// while still seeing the current policy, axis and zoom.
var policyRef = (0, _react.useRef)(policy);
policyRef.current = policy;
var axisRef = (0, _react.useRef)(axis);
axisRef.current = axis;
var scaleRef = (0, _react.useRef)(scale);
scaleRef.current = scale;
var contentMinRef = (0, _react.useRef)(contentMin);
contentMinRef.current = contentMin;
var readContentMin = (0, _react.useCallback)(function () {
var source = contentMinRef.current;
return typeof source === "function" ? source() : source;
}, []);
var emitPreview = (0, _react.useCallback)(function (next, source) {
// Nothing changed — not a preview, and not something to commit later.
if (sameSize(next, latest.current)) return;
latest.current = next;
dirty.current = true;
onResize(next, source);
}, [onResize]);
var handlePointerDown = (0, _react.useCallback)(function (e) {
var _setPointerCapture, _ref2;
pointerStart.current = {
start: (0, _resizePolicy.makeStart)(latest.current, readContentMin()),
x: e.clientX,
y: e.clientY
};
dirty.current = false;
(_setPointerCapture = (_ref2 = e.target).setPointerCapture) === null || _setPointerCapture === void 0 || _setPointerCapture.call(_ref2, e.pointerId);
e.preventDefault();
}, [readContentMin]);
var handlePointerMove = (0, _react.useCallback)(function (e) {
var ps = pointerStart.current;
if (!ps) return;
var z = scaleRef.current || 1;
var next = (0, _resizePolicy.applyResize)(ps.start, {
dx: (e.clientX - ps.x) / z,
dy: (e.clientY - ps.y) / z
}, policyRef.current,
// Shift held during a free-form drag = temporary proportional lock.
{
constrain: e.shiftKey,
axis: axisRef.current
});
emitPreview(next, "pointer");
}, [emitPreview]);
var endPointer = (0, _react.useCallback)(function () {
if (!pointerStart.current) return;
pointerStart.current = null;
if (dirty.current) onResizeCommit(latest.current, "pointer");
dirty.current = false;
}, [onResizeCommit]);
var handleKeyDown = (0, _react.useCallback)(function (e) {
var next = (0, _resizePolicy.keyboardResize)(e.key, latest.current, policyRef.current, readContentMin(), {
shiftKey: e.shiftKey,
axis: axisRef.current
});
if (!next) return;
// The key is ours even when it resolves to no movement (already pinned at
// a bound), so the browser must not also scroll the surface.
e.preventDefault();
emitPreview(next, "keyboard");
}, [emitPreview, readContentMin]);
var handleKeyUp = (0, _react.useCallback)(function (e) {
if (!(0, _resizePolicy.isResizeKey)(e.key)) return;
// The gesture end for the keyboard is the key release: one commit per
// hold, mirroring pointer-up after a stream of moves.
if (dirty.current) onResizeCommit(latest.current, "keyboard");
dirty.current = false;
}, [onResizeCommit]);
return {
role: "separator",
tabIndex: 0,
"aria-label": label,
"aria-controls": cardId,
"aria-valuemin": 0,
"aria-valuemax": 100,
"aria-valuenow": (0, _resizePolicy.measurePercent)(size, policy, readContentMin()),
onPointerDown: handlePointerDown,
onPointerMove: handlePointerMove,
onPointerUp: endPointer,
onLostPointerCapture: endPointer,
onKeyDown: handleKeyDown,
onKeyUp: handleKeyUp
};
};
//# sourceMappingURL=useStackedCardResize.js.map