@activecollab/components 2.0.372 → 2.0.373

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cjs/components/StackedCard/StackedCard.js +103 -19
  2. package/dist/cjs/components/StackedCard/StackedCard.js.map +1 -1
  3. package/dist/cjs/components/StackedCard/Styles.js +22 -17
  4. package/dist/cjs/components/StackedCard/Styles.js.map +1 -1
  5. package/dist/cjs/components/StackedCard/index.js +22 -0
  6. package/dist/cjs/components/StackedCard/index.js.map +1 -1
  7. package/dist/cjs/components/StackedCard/resizePolicy.js +238 -0
  8. package/dist/cjs/components/StackedCard/resizePolicy.js.map +1 -0
  9. package/dist/cjs/components/StackedCard/resizePolicy.test.js +474 -0
  10. package/dist/cjs/components/StackedCard/resizePolicy.test.js.map +1 -0
  11. package/dist/cjs/components/StackedCard/useStackedCardResize.js +145 -0
  12. package/dist/cjs/components/StackedCard/useStackedCardResize.js.map +1 -0
  13. package/dist/esm/components/StackedCard/StackedCard.d.ts +44 -3
  14. package/dist/esm/components/StackedCard/StackedCard.d.ts.map +1 -1
  15. package/dist/esm/components/StackedCard/StackedCard.js +102 -19
  16. package/dist/esm/components/StackedCard/StackedCard.js.map +1 -1
  17. package/dist/esm/components/StackedCard/Styles.d.ts +4 -1
  18. package/dist/esm/components/StackedCard/Styles.d.ts.map +1 -1
  19. package/dist/esm/components/StackedCard/Styles.js +22 -17
  20. package/dist/esm/components/StackedCard/Styles.js.map +1 -1
  21. package/dist/esm/components/StackedCard/index.d.ts +2 -0
  22. package/dist/esm/components/StackedCard/index.d.ts.map +1 -1
  23. package/dist/esm/components/StackedCard/index.js +2 -0
  24. package/dist/esm/components/StackedCard/index.js.map +1 -1
  25. package/dist/esm/components/StackedCard/resizePolicy.d.ts +111 -0
  26. package/dist/esm/components/StackedCard/resizePolicy.d.ts.map +1 -0
  27. package/dist/esm/{presentation/stackedCard → components/StackedCard}/resizePolicy.js +93 -48
  28. package/dist/esm/components/StackedCard/resizePolicy.js.map +1 -0
  29. package/dist/esm/components/StackedCard/resizePolicy.test.d.ts +2 -0
  30. package/dist/esm/components/StackedCard/resizePolicy.test.d.ts.map +1 -0
  31. package/dist/esm/components/StackedCard/resizePolicy.test.js +470 -0
  32. package/dist/esm/components/StackedCard/resizePolicy.test.js.map +1 -0
  33. package/dist/esm/components/StackedCard/useStackedCardResize.d.ts +62 -0
  34. package/dist/esm/components/StackedCard/useStackedCardResize.d.ts.map +1 -0
  35. package/dist/esm/components/StackedCard/useStackedCardResize.js +137 -0
  36. package/dist/esm/components/StackedCard/useStackedCardResize.js.map +1 -0
  37. package/dist/index.js +500 -36
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.min.js +1 -1
  40. package/dist/index.min.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/cjs/presentation/stackedCard/resizePolicy.js +0 -183
  43. package/dist/cjs/presentation/stackedCard/resizePolicy.js.map +0 -1
  44. package/dist/esm/presentation/stackedCard/resizePolicy.d.ts +0 -97
  45. package/dist/esm/presentation/stackedCard/resizePolicy.d.ts.map +0 -1
  46. package/dist/esm/presentation/stackedCard/resizePolicy.js.map +0 -1
package/dist/index.js CHANGED
@@ -26955,6 +26955,233 @@
26955
26955
  }));
26956
26956
  };
26957
26957
 
26958
+ /**
26959
+ * StackedCard resize policy — pure math.
26960
+ *
26961
+ * The card reports intent; the HOST applies a resize policy (spec §7). This
26962
+ * module is that policy, packaged as pure functions so the SAME code path
26963
+ * serves the pointer drag and the keyboard resize. Nothing here touches the
26964
+ * DOM, React or the card's content: it takes a start size, a delta and a
26965
+ * policy, and returns a clamped size. `useStackedCardResize` wires events to
26966
+ * it; the card's intrinsic content minimum is measured by the host and passed
26967
+ * in as `contentMin`, so this module knows nothing about heroes or footers.
26968
+ *
26969
+ * The policy has four parts:
26970
+ * - bounds — optional min/max per axis; unset means unbounded.
26971
+ * - proportional — lock the aspect ratio captured at the start of the gesture;
26972
+ * the scale is clamped by the BINDING axis so the ratio never
26973
+ * distorts at a bound.
26974
+ * - step — optional grid snap; proportion wins over the grid.
26975
+ * - content min — the effective min is max(policyMin, contentMin) per axis,
26976
+ * so a card can never be crushed below what its content needs.
26977
+ */
26978
+
26979
+ /** The card's intrinsic minimum, measured from its real content. */
26980
+
26981
+ /** The size captured at the start of a gesture (pointer down or a key press). */
26982
+
26983
+ /**
26984
+ * `width` locks the vertical axis: an auto-height card takes a width and lets
26985
+ * its rows set the height, so the gesture's vertical component is ignored.
26986
+ */
26987
+
26988
+ /** Which input drove a resize event. Hosts log undo entries per source. */
26989
+
26990
+ /** Keyboard resize nudges by the policy step, or this when no step is set. */
26991
+ var DEFAULT_KEYBOARD_STEP = 8;
26992
+
26993
+ /** Shift + arrow resizes by a larger increment (the familiar coarse nudge). */
26994
+ var SHIFT_STEP_MULTIPLIER = 4;
26995
+
26996
+ /** Keys the separator handles; everything else falls through to the browser. */
26997
+ var RESIZE_KEYS = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"];
26998
+ var isResizeKey = function isResizeKey(key) {
26999
+ return RESIZE_KEYS.includes(key);
27000
+ };
27001
+ var clamp = function clamp(v, lo, hi) {
27002
+ return Math.min(hi !== null && hi !== void 0 ? hi : Number.POSITIVE_INFINITY, Math.max(lo, v));
27003
+ };
27004
+ var snap = function snap(v, step) {
27005
+ return step && step > 0 ? Math.round(v / step) * step : v;
27006
+ };
27007
+ var makeStart = function makeStart(size, contentMin) {
27008
+ return {
27009
+ w: size.w,
27010
+ h: size.h,
27011
+ contentMin
27012
+ };
27013
+ };
27014
+
27015
+ /**
27016
+ * The effective bounds a size is clamped to: the policy min floored by the
27017
+ * content min, and the policy max (unbounded when unset). Independent of any
27018
+ * live gesture, so a host can also use it for the aria value and Home/End.
27019
+ */
27020
+ var effectiveBounds = function effectiveBounds(policy, contentMin) {
27021
+ var _policy$minW, _policy$minH, _policy$maxW, _policy$maxH;
27022
+ return {
27023
+ wMin: Math.max((_policy$minW = policy.minW) !== null && _policy$minW !== void 0 ? _policy$minW : 0, contentMin.w),
27024
+ hMin: Math.max((_policy$minH = policy.minH) !== null && _policy$minH !== void 0 ? _policy$minH : 0, contentMin.h),
27025
+ wMax: (_policy$maxW = policy.maxW) !== null && _policy$maxW !== void 0 ? _policy$maxW : Number.POSITIVE_INFINITY,
27026
+ hMax: (_policy$maxH = policy.maxH) !== null && _policy$maxH !== void 0 ? _policy$maxH : Number.POSITIVE_INFINITY
27027
+ };
27028
+ };
27029
+
27030
+ /**
27031
+ * Whether an axis has a floor anyone asked for. `effectiveBounds` reports 0 for
27032
+ * an unset minimum — correct as a clamp, but not something Home should jump to:
27033
+ * an axis nobody gave a minimum must not collapse to nothing.
27034
+ */
27035
+ var hasFloor = function hasFloor(policyMin, contentMin) {
27036
+ return policyMin !== undefined || contentMin > 0;
27037
+ };
27038
+
27039
+ /**
27040
+ * Apply a delta to the start size under the policy — the single path for both
27041
+ * inputs.
27042
+ *
27043
+ * `constrain` is the transient Shift-to-constrain lock: a free-form policy
27044
+ * behaves proportionally for that one gesture; a proportional policy is already
27045
+ * locked and ignores it. `axis: "width"` drops the gesture's vertical component;
27046
+ * the height then holds still in free-form mode, and still follows the ratio in
27047
+ * proportional mode (that is what proportional means).
27048
+ */
27049
+ var applyResize = function applyResize(start, delta, policy) {
27050
+ var opts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
27051
+ var _effectiveBounds = effectiveBounds(policy, start.contentMin),
27052
+ wMin = _effectiveBounds.wMin,
27053
+ hMin = _effectiveBounds.hMin,
27054
+ wMax = _effectiveBounds.wMax,
27055
+ hMax = _effectiveBounds.hMax;
27056
+ var widthOnly = opts.axis === "width";
27057
+ var dx = delta.dx;
27058
+ var dy = widthOnly ? 0 : delta.dy;
27059
+
27060
+ // A ratio needs two non-zero sides to be a ratio at all; a width-only card
27061
+ // parks a placeholder height, so fall back to free-form rather than dividing
27062
+ // by zero.
27063
+ var proportional = (policy.proportional || opts.constrain === true) && start.w > 0 && start.h > 0;
27064
+ var w;
27065
+ var h;
27066
+ if (proportional) {
27067
+ // one scale drives both axes; the corner's dominant direction wins, so a
27068
+ // vertical drag is as effective as a horizontal one
27069
+ var sW = (start.w + dx) / start.w;
27070
+ var sH = (start.h + dy) / start.h;
27071
+ var scale = Math.abs(sW - 1) >= Math.abs(sH - 1) ? sW : sH;
27072
+
27073
+ // step: proportion wins over the grid. Snap through the SCALE (driven off
27074
+ // the width edge) so both axes move together and the ratio survives; the
27075
+ // grid yields whenever honouring it would cost the ratio.
27076
+ if (policy.step) {
27077
+ scale = snap(start.w * scale, policy.step) / start.w;
27078
+ }
27079
+
27080
+ // Then clamp the scale so both axes stay in bounds (the binding constraint):
27081
+ // the ratio can never break at a bound — whichever edge is limiting stops
27082
+ // both. The clamp comes last on purpose: a bound is a hard stop, so the card
27083
+ // sits exactly on it rather than at the nearest grid line inside it.
27084
+ var scaleMin = Math.max(wMin / start.w, hMin / start.h);
27085
+ var scaleMax = Math.min(wMax / start.w, hMax / start.h);
27086
+ scale = clamp(scale, scaleMin, Math.max(scaleMin, scaleMax));
27087
+ w = start.w * scale;
27088
+ h = start.h * scale;
27089
+ } else {
27090
+ w = clamp(snap(start.w + dx, policy.step), wMin, wMax);
27091
+ h = widthOnly ? start.h : clamp(snap(start.h + dy, policy.step), hMin, hMax);
27092
+ }
27093
+ return {
27094
+ w: Math.round(w),
27095
+ h: Math.round(h)
27096
+ };
27097
+ };
27098
+
27099
+ /**
27100
+ * Translate a resize key into the next size, reusing `applyResize` so the
27101
+ * keyboard clamps exactly like the pointer. Arrows nudge by the step (Shift ×4);
27102
+ * Home/End jump to the min/max of the allowed range, leaving any axis without
27103
+ * that bound where it is. Returns null for a key the handle does not own, and
27104
+ * for a vertical key on a width-only card.
27105
+ */
27106
+ var keyboardResize = function keyboardResize(key, size, policy, contentMin) {
27107
+ var opts = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
27108
+ var widthOnly = opts.axis === "width";
27109
+ if (widthOnly && (key === "ArrowUp" || key === "ArrowDown")) return null;
27110
+ var base = policy.step && policy.step > 0 ? policy.step : DEFAULT_KEYBOARD_STEP;
27111
+ var step = base * (opts.shiftKey ? SHIFT_STEP_MULTIPLIER : 1);
27112
+ var start = makeStart(size, contentMin);
27113
+ var b = effectiveBounds(policy, contentMin);
27114
+ var apply = function apply(delta) {
27115
+ return applyResize(start, delta, policy, {
27116
+ axis: opts.axis
27117
+ });
27118
+ };
27119
+
27120
+ /**
27121
+ * Home and End are absolute jumps to a bound, not nudges, so they ignore the
27122
+ * grid: landing 4px off the minimum because that is where the nearest grid
27123
+ * line fell would mean the keyboard could never reach the bound the pointer
27124
+ * stops at.
27125
+ */
27126
+ var jump = function jump(delta) {
27127
+ return applyResize(start, delta, _objectSpread2(_objectSpread2({}, policy), {}, {
27128
+ step: 0
27129
+ }), {
27130
+ axis: opts.axis
27131
+ });
27132
+ };
27133
+ switch (key) {
27134
+ case "ArrowRight":
27135
+ return apply({
27136
+ dx: step,
27137
+ dy: 0
27138
+ });
27139
+ case "ArrowLeft":
27140
+ return apply({
27141
+ dx: -step,
27142
+ dy: 0
27143
+ });
27144
+ case "ArrowDown":
27145
+ return apply({
27146
+ dx: 0,
27147
+ dy: step
27148
+ });
27149
+ case "ArrowUp":
27150
+ return apply({
27151
+ dx: 0,
27152
+ dy: -step
27153
+ });
27154
+ case "Home":
27155
+ // jump to the min corner; an axis with no floor of its own stays put
27156
+ return jump({
27157
+ dx: hasFloor(policy.minW, contentMin.w) ? b.wMin - size.w : 0,
27158
+ dy: !widthOnly && hasFloor(policy.minH, contentMin.h) ? b.hMin - size.h : 0
27159
+ });
27160
+ case "End":
27161
+ // jump to the max corner; an unbounded axis simply doesn't move
27162
+ return jump({
27163
+ dx: Number.isFinite(b.wMax) ? b.wMax - size.w : 0,
27164
+ dy: !widthOnly && Number.isFinite(b.hMax) ? b.hMax - size.h : 0
27165
+ });
27166
+ default:
27167
+ return null;
27168
+ }
27169
+ };
27170
+
27171
+ /**
27172
+ * The handle's `aria-valuenow`: the current size as a percent (0–100) of its
27173
+ * allowed range. The corner resizes both axes, but the value must be a single
27174
+ * number, so it reports the WIDTH axis — the one axis the fixed-box and the
27175
+ * width-only cards share. Returns 0 when the range isn't finite (no max bound).
27176
+ */
27177
+ var measurePercent = function measurePercent(size, policy, contentMin) {
27178
+ var _effectiveBounds2 = effectiveBounds(policy, contentMin),
27179
+ wMin = _effectiveBounds2.wMin,
27180
+ wMax = _effectiveBounds2.wMax;
27181
+ if (!Number.isFinite(wMax) || wMax <= wMin) return 0;
27182
+ return Math.round(clamp((size.w - wMin) / (wMax - wMin) * 100, 0, 100));
27183
+ };
27184
+
26958
27185
  /**
26959
27186
  * Theming hooks.
26960
27187
  *
@@ -27096,10 +27323,15 @@
27096
27323
  /* Resize (opt-in): handle on hover, the card only reports deltas */
27097
27324
  /* ------------------------------------------------------------------ */
27098
27325
 
27326
+ /* The grip is a focusable separator, so it needs a focus ring of its own — a
27327
+ keyboard user has no pointer to tell them where they are. */
27099
27328
  var StyledStackedCardResizeHandle = styled__default["default"].span.withConfig({
27100
27329
  displayName: "Styles__StyledStackedCardResizeHandle",
27101
27330
  componentId: "sc-vnqm9x-10"
27102
- })(["position:absolute;right:2px;bottom:2px;width:14px;height:14px;cursor:nwse-resize;color:var(--color-theme-600);z-index:2;svg{display:block;}"]);
27331
+ })(["position:absolute;right:2px;bottom:2px;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:4px;cursor:", ";color:var(--color-theme-600);z-index:2;&:hover{color:var(--color-theme-900);}&:focus-visible{outline:2px solid ", ";outline-offset:1px;color:var(--color-theme-900);}svg{display:block;}"], function (_ref8) {
27332
+ var $axis = _ref8.$axis;
27333
+ return $axis === "width" ? "ew-resize" : "nwse-resize";
27334
+ }, SELECTION_RING);
27103
27335
  StyledStackedCardResizeHandle.displayName = "StyledStackedCardResizeHandle";
27104
27336
 
27105
27337
  /* ------------------------------------------------------------------ */
@@ -27110,34 +27342,34 @@
27110
27342
  var StyledStackedCard = styled__default["default"].div.withConfig({
27111
27343
  displayName: "Styles__StyledStackedCard",
27112
27344
  componentId: "sc-vnqm9x-11"
27113
- })(["", " position:relative;display:flex;flex-direction:column;width:", "px;border-radius:", "px;background-color:var(--page-paper-main);border:1px solid var(--border-primary);box-shadow:0 1px 3px rgba(0,0,0,0.08);overflow:hidden;transition:box-shadow 150ms ease,border-color 150ms ease;", " &:hover{", "}&:focus-visible{outline:none;", " box-shadow:0 0 0 2px ", ";}", " ", " ", " ", " ", " ", " ", " ", ""], BoxSizingStyle, STACKED_CARD_WIDTH, STACKED_CARD_RADIUS, revealControls, hoverStyle, hoverStyle, SELECTION_RING, function (_ref8) {
27114
- var $forceHover = _ref8.$forceHover;
27345
+ })(["", " position:relative;display:flex;flex-direction:column;width:", "px;border-radius:", "px;background-color:var(--page-paper-main);border:1px solid var(--border-primary);box-shadow:0 1px 3px rgba(0,0,0,0.08);overflow:hidden;transition:box-shadow 150ms ease,border-color 150ms ease;", " &:hover{", "}&:focus-visible{outline:none;", " box-shadow:0 0 0 2px ", ";}", " ", " ", " ", " ", " ", " ", " ", ""], BoxSizingStyle, STACKED_CARD_WIDTH, STACKED_CARD_RADIUS, revealControls, hoverStyle, hoverStyle, SELECTION_RING, function (_ref9) {
27346
+ var $forceHover = _ref9.$forceHover;
27115
27347
  return $forceHover && styled.css(["", " && ", "{opacity:1;}"], hoverStyle, CONTROL);
27116
- }, function (_ref9) {
27117
- var $priorityColor = _ref9.$priorityColor;
27118
- return $priorityColor && styled.css(["&::before{content:\"\";position:absolute;top:0;bottom:0;left:0;width:4px;background:", ";z-index:2;}"], $priorityColor);
27119
27348
  }, function (_ref0) {
27120
- var $selected = _ref0.$selected;
27121
- return $selected && styled.css(["&&{border-color:", ";box-shadow:0 0 0 2px ", ";}"], SELECTION_RING, SELECTION_RING);
27349
+ var $priorityColor = _ref0.$priorityColor;
27350
+ return $priorityColor && styled.css(["&::before{content:\"\";position:absolute;top:0;bottom:0;left:0;width:4px;background:", ";z-index:2;}"], $priorityColor);
27122
27351
  }, function (_ref1) {
27123
- var $disabled = _ref1.$disabled;
27124
- return $disabled && styled.css(["opacity:0.5;pointer-events:none;"]);
27352
+ var $selected = _ref1.$selected;
27353
+ return $selected && styled.css(["&&{border-color:", ";box-shadow:0 0 0 2px ", ";}"], SELECTION_RING, SELECTION_RING);
27125
27354
  }, function (_ref10) {
27126
- var $pending = _ref10.$pending;
27127
- return $pending && styled.css(["opacity:0.6;pointer-events:none;cursor:progress;"]);
27355
+ var $disabled = _ref10.$disabled;
27356
+ return $disabled && styled.css(["opacity:0.5;pointer-events:none;"]);
27128
27357
  }, function (_ref11) {
27129
- var $isDragSource = _ref11.$isDragSource;
27130
- return $isDragSource && styled.css(["background:transparent;border:1px dashed var(--color-theme-500);box-shadow:none;&:hover{box-shadow:none;border-color:var(--color-theme-500);}> *{visibility:hidden;}"]);
27358
+ var $pending = _ref11.$pending;
27359
+ return $pending && styled.css(["opacity:0.6;pointer-events:none;cursor:progress;"]);
27131
27360
  }, function (_ref12) {
27132
- var $isDragPreview = _ref12.$isDragPreview;
27133
- return $isDragPreview && styled.css(["box-shadow:0 12px 28px rgba(0,0,0,0.24);cursor:grabbing;"]);
27361
+ var $isDragSource = _ref12.$isDragSource;
27362
+ return $isDragSource && styled.css(["background:transparent;border:1px dashed var(--color-theme-500);box-shadow:none;&:hover{box-shadow:none;border-color:var(--color-theme-500);}> *{visibility:hidden;}"]);
27134
27363
  }, function (_ref13) {
27135
- var $entered = _ref13.$entered;
27364
+ var $isDragPreview = _ref13.$isDragPreview;
27365
+ return $isDragPreview && styled.css(["box-shadow:0 12px 28px rgba(0,0,0,0.24);cursor:grabbing;"]);
27366
+ }, function (_ref14) {
27367
+ var $entered = _ref14.$entered;
27136
27368
  return $entered && styled.css(["&&{border-color:", ";box-shadow:inset 0 0 0 2px ", ",0 0 0 2px ", ";}"], SELECTION_RING, SELECTION_RING, SELECTION_RING);
27137
27369
  });
27138
27370
  StyledStackedCard.displayName = "StyledStackedCard";
27139
27371
 
27140
- var _excluded$1 = ["selected", "disabled", "pending", "isDragSource", "isDragPreview", "entered", "forceHover", "resizable", "priorityColor", "onResizeDelta", "className", "children"];
27372
+ var _excluded$1 = ["selected", "disabled", "pending", "isDragSource", "isDragPreview", "entered", "forceHover", "resizable", "resizeAxis", "resizeLabel", "priorityColor", "onResizeStart", "onResizeDelta", "onResizeCommit", "resizeHandleProps", "className", "children"];
27141
27373
 
27142
27374
  /**
27143
27375
  * StackedCard — the hollow shell for cards on drag-and-drop surfaces.
@@ -27164,6 +27396,24 @@
27164
27396
  * `isDragPreview`. The shell forwards its ref and spreads DOM props, so a host
27165
27397
  * can attach `tabIndex`, ARIA, pointer and keyboard handlers to it.
27166
27398
  *
27399
+ * ── Resize ────────────────────────────────────────────────────────────────
27400
+ * The grip is opt-in (`resizable`) and reports intent as a gesture: one
27401
+ * `onResizeStart`, a stream of `onResizeDelta` previews measured FROM THE
27402
+ * GESTURE START, and one `onResizeCommit` at the end — and only if the gesture
27403
+ * moved anything. A host applies previews for live feedback and records an undo
27404
+ * entry on the commit alone (spec §7e).
27405
+ *
27406
+ * Because the deltas are gesture-relative rather than incremental, a host can
27407
+ * resolve every event against the size it snapshotted on start, and never has
27408
+ * to keep a running accumulator. That is what makes a slow, few-pixels-at-a-time
27409
+ * drag keep growing instead of stalling under a snap step.
27410
+ *
27411
+ * The grip itself is a focusable `role="separator"` and handles arrow keys
27412
+ * (Shift for a coarse nudge). For the full policy — bounds, proportional mode,
27413
+ * step snapping, content minimums, Home/End and the live `aria-valuenow` — use
27414
+ * `useStackedCardResize` and spread its result through `resizeHandleProps`;
27415
+ * those handlers then own the grip and the three callbacks above stay quiet.
27416
+ *
27167
27417
  * ── Theming ───────────────────────────────────────────────────────────────
27168
27418
  * Colours resolve through `--sc-*` custom properties with built-in fallbacks:
27169
27419
  * `--sc-selection-ring` (selection / focus / entered) and the cover scrim
@@ -27180,31 +27430,89 @@
27180
27430
  entered = _ref.entered,
27181
27431
  forceHover = _ref.forceHover,
27182
27432
  resizable = _ref.resizable,
27433
+ _ref$resizeAxis = _ref.resizeAxis,
27434
+ resizeAxis = _ref$resizeAxis === void 0 ? "both" : _ref$resizeAxis,
27435
+ _ref$resizeLabel = _ref.resizeLabel,
27436
+ resizeLabel = _ref$resizeLabel === void 0 ? "Resize card" : _ref$resizeLabel,
27183
27437
  priorityColor = _ref.priorityColor,
27438
+ onResizeStart = _ref.onResizeStart,
27184
27439
  onResizeDelta = _ref.onResizeDelta,
27440
+ onResizeCommit = _ref.onResizeCommit,
27441
+ resizeHandleProps = _ref.resizeHandleProps,
27185
27442
  className = _ref.className,
27186
27443
  children = _ref.children,
27187
27444
  rest = _objectWithoutProperties(_ref, _excluded$1);
27188
- var dragOrigin = React.useRef(null);
27445
+ /**
27446
+ * One live gesture. `x`/`y` anchor a pointer drag; `dx`/`dy` carry the
27447
+ * running total a keyboard gesture has nudged. `moved` gates the commit, so
27448
+ * a click that never travelled is not a change.
27449
+ */
27450
+ var gesture = React.useRef(null);
27451
+ var emitDelta = React.useCallback(function (dx, dy) {
27452
+ var g = gesture.current;
27453
+ if (!g) return;
27454
+ if (dx !== 0 || dy !== 0) g.moved = true;
27455
+ onResizeDelta === null || onResizeDelta === void 0 || onResizeDelta(dx, dy, g.source);
27456
+ }, [onResizeDelta]);
27457
+ var endGesture = React.useCallback(function () {
27458
+ var g = gesture.current;
27459
+ if (!g) return;
27460
+ gesture.current = null;
27461
+ if (g.moved) onResizeCommit === null || onResizeCommit === void 0 || onResizeCommit(g.source);
27462
+ }, [onResizeCommit]);
27189
27463
  var handlePointerDown = React.useCallback(function (e) {
27190
- dragOrigin.current = {
27464
+ var _setPointerCapture, _ref2;
27465
+ gesture.current = {
27466
+ source: "pointer",
27191
27467
  x: e.clientX,
27192
- y: e.clientY
27468
+ y: e.clientY,
27469
+ dx: 0,
27470
+ dy: 0,
27471
+ moved: false
27193
27472
  };
27194
- e.target.setPointerCapture(e.pointerId);
27473
+ (_setPointerCapture = (_ref2 = e.target).setPointerCapture) === null || _setPointerCapture === void 0 || _setPointerCapture.call(_ref2, e.pointerId);
27195
27474
  e.preventDefault();
27196
- }, []);
27475
+ onResizeStart === null || onResizeStart === void 0 || onResizeStart("pointer");
27476
+ }, [onResizeStart]);
27197
27477
  var handlePointerMove = React.useCallback(function (e) {
27198
- if (!dragOrigin.current || !onResizeDelta) return;
27199
- onResizeDelta(e.clientX - dragOrigin.current.x, e.clientY - dragOrigin.current.y);
27200
- dragOrigin.current = {
27201
- x: e.clientX,
27202
- y: e.clientY
27203
- };
27204
- }, [onResizeDelta]);
27205
- var handlePointerUp = React.useCallback(function () {
27206
- dragOrigin.current = null;
27207
- }, []);
27478
+ var g = gesture.current;
27479
+ if (!g || g.source !== "pointer") return;
27480
+ // Measured from the gesture start, so the host resolves every event
27481
+ // against one snapshot instead of accumulating.
27482
+ emitDelta(e.clientX - g.x, e.clientY - g.y);
27483
+ }, [emitDelta]);
27484
+
27485
+ /**
27486
+ * Arrows nudge the running total; Shift makes it coarse. Home and End need
27487
+ * bounds to resolve, which the bare shell does not have — they fall through
27488
+ * to `useStackedCardResize`, or to the browser.
27489
+ */
27490
+ var handleKeyDown = React.useCallback(function (e) {
27491
+ var step = DEFAULT_KEYBOARD_STEP * (e.shiftKey ? SHIFT_STEP_MULTIPLIER : 1);
27492
+ var dx = 0;
27493
+ var dy = 0;
27494
+ if (e.key === "ArrowRight") dx = step;else if (e.key === "ArrowLeft") dx = -step;else if (e.key === "ArrowDown") dy = step;else if (e.key === "ArrowUp") dy = -step;else return;
27495
+ e.preventDefault();
27496
+ if (!gesture.current || gesture.current.source !== "keyboard") {
27497
+ gesture.current = {
27498
+ source: "keyboard",
27499
+ x: 0,
27500
+ y: 0,
27501
+ dx: 0,
27502
+ dy: 0,
27503
+ moved: false
27504
+ };
27505
+ onResizeStart === null || onResizeStart === void 0 || onResizeStart("keyboard");
27506
+ }
27507
+ var g = gesture.current;
27508
+ g.dx += dx;
27509
+ g.dy += dy;
27510
+ emitDelta(g.dx, g.dy);
27511
+ }, [emitDelta, onResizeStart]);
27512
+ var handleKeyUp = React.useCallback(function (e) {
27513
+ // One commit per hold, mirroring pointer-up after a stream of moves.
27514
+ if (isResizeKey(e.key)) endGesture();
27515
+ }, [endGesture]);
27208
27516
  return /*#__PURE__*/React__default["default"].createElement(StyledStackedCard, _extends({
27209
27517
  ref: ref,
27210
27518
  $selected: selected,
@@ -27216,12 +27524,19 @@
27216
27524
  $forceHover: forceHover,
27217
27525
  $priorityColor: priorityColor,
27218
27526
  className: classNames__default["default"]("c-stacked-card", className)
27219
- }, rest), children, resizable ? /*#__PURE__*/React__default["default"].createElement(StyledStackedCardResizeHandle, {
27527
+ }, rest), children, resizable ? /*#__PURE__*/React__default["default"].createElement(StyledStackedCardResizeHandle, _extends({
27528
+ $axis: resizeAxis,
27220
27529
  className: classNames__default["default"]("c-stacked-card__resize-handle", STACKED_CARD_CONTROL_CLASS),
27530
+ role: "separator",
27531
+ tabIndex: 0,
27532
+ "aria-label": resizeLabel,
27221
27533
  onPointerDown: handlePointerDown,
27222
27534
  onPointerMove: handlePointerMove,
27223
- onPointerUp: handlePointerUp
27224
- }, /*#__PURE__*/React__default["default"].createElement(StackedCardResizeHandleGlyph, null)) : null);
27535
+ onPointerUp: endGesture,
27536
+ onLostPointerCapture: endGesture,
27537
+ onKeyDown: handleKeyDown,
27538
+ onKeyUp: handleKeyUp
27539
+ }, resizeHandleProps), /*#__PURE__*/React__default["default"].createElement(StackedCardResizeHandleGlyph, null)) : null);
27225
27540
  });
27226
27541
  StackedCard.displayName = "StackedCard";
27227
27542
 
@@ -27340,6 +27655,143 @@
27340
27655
  /** Top-right cover-management button (collapse, remove …). */
27341
27656
  var StackedCardCoverButton = StyledStackedCardCoverButton;
27342
27657
 
27658
+ /**
27659
+ * The one place pointer and keyboard input meet the resize policy.
27660
+ *
27661
+ * `StackedCard`'s built-in grip reports raw deltas and knows no policy — enough
27662
+ * for a host that just tracks a width. This hook is the full contract: it
27663
+ * captures the gesture start, funnels both inputs through the pure policy in
27664
+ * `resizePolicy.ts`, streams preview events and fires exactly ONE commit per
27665
+ * gesture. It returns the props to spread on the grip, including the live
27666
+ * separator ARIA.
27667
+ *
27668
+ * Preview versus commit (spec §7e): the continuous stream is live feedback
27669
+ * only. A host creates an undo entry on the COMMIT, never on a preview — and a
27670
+ * gesture that never changed the size commits nothing at all, so a bare click
27671
+ * on the grip is not a change.
27672
+ *
27673
+ * The hook never touches the card's content: the intrinsic minimum is read
27674
+ * through `contentMin`, which may be a getter so a card that measures its own
27675
+ * footer can answer at the moment it is asked.
27676
+ */
27677
+
27678
+ /** The props to spread on the grip — `StackedCard`'s `resizeHandleProps`. */
27679
+
27680
+ var sameSize = function sameSize(a, b) {
27681
+ return a.w === b.w && a.h === b.h;
27682
+ };
27683
+ var useStackedCardResize = function useStackedCardResize(_ref) {
27684
+ var size = _ref.size,
27685
+ policy = _ref.policy,
27686
+ cardId = _ref.cardId,
27687
+ contentMin = _ref.contentMin,
27688
+ _ref$axis = _ref.axis,
27689
+ axis = _ref$axis === void 0 ? "both" : _ref$axis,
27690
+ _ref$scale = _ref.scale,
27691
+ scale = _ref$scale === void 0 ? 1 : _ref$scale,
27692
+ _ref$label = _ref.label,
27693
+ label = _ref$label === void 0 ? "Resize card" : _ref$label,
27694
+ onResize = _ref.onResize,
27695
+ onResizeCommit = _ref.onResizeCommit;
27696
+ // The pointer gesture's anchor: the size + content-min captured on pointer
27697
+ // down, plus the pointer origin the delta is measured from.
27698
+ var pointerStart = React.useRef(null);
27699
+ // Mirrors the latest emitted size so pointer-up / key-up can commit exactly
27700
+ // what preview last showed, without a stale closure.
27701
+ var latest = React.useRef(size);
27702
+ latest.current = size;
27703
+ // Whether this gesture actually moved the card. A bare click, or a Home on an
27704
+ // axis with no floor, leaves it false and commits nothing.
27705
+ var dirty = React.useRef(false);
27706
+
27707
+ // Read through refs so the handlers below can stay stable across renders
27708
+ // while still seeing the current policy, axis and zoom.
27709
+ var policyRef = React.useRef(policy);
27710
+ policyRef.current = policy;
27711
+ var axisRef = React.useRef(axis);
27712
+ axisRef.current = axis;
27713
+ var scaleRef = React.useRef(scale);
27714
+ scaleRef.current = scale;
27715
+ var contentMinRef = React.useRef(contentMin);
27716
+ contentMinRef.current = contentMin;
27717
+ var readContentMin = React.useCallback(function () {
27718
+ var source = contentMinRef.current;
27719
+ return typeof source === "function" ? source() : source;
27720
+ }, []);
27721
+ var emitPreview = React.useCallback(function (next, source) {
27722
+ // Nothing changed — not a preview, and not something to commit later.
27723
+ if (sameSize(next, latest.current)) return;
27724
+ latest.current = next;
27725
+ dirty.current = true;
27726
+ onResize(next, source);
27727
+ }, [onResize]);
27728
+ var handlePointerDown = React.useCallback(function (e) {
27729
+ var _setPointerCapture, _ref2;
27730
+ pointerStart.current = {
27731
+ start: makeStart(latest.current, readContentMin()),
27732
+ x: e.clientX,
27733
+ y: e.clientY
27734
+ };
27735
+ dirty.current = false;
27736
+ (_setPointerCapture = (_ref2 = e.target).setPointerCapture) === null || _setPointerCapture === void 0 || _setPointerCapture.call(_ref2, e.pointerId);
27737
+ e.preventDefault();
27738
+ }, [readContentMin]);
27739
+ var handlePointerMove = React.useCallback(function (e) {
27740
+ var ps = pointerStart.current;
27741
+ if (!ps) return;
27742
+ var z = scaleRef.current || 1;
27743
+ var next = applyResize(ps.start, {
27744
+ dx: (e.clientX - ps.x) / z,
27745
+ dy: (e.clientY - ps.y) / z
27746
+ }, policyRef.current,
27747
+ // Shift held during a free-form drag = temporary proportional lock.
27748
+ {
27749
+ constrain: e.shiftKey,
27750
+ axis: axisRef.current
27751
+ });
27752
+ emitPreview(next, "pointer");
27753
+ }, [emitPreview]);
27754
+ var endPointer = React.useCallback(function () {
27755
+ if (!pointerStart.current) return;
27756
+ pointerStart.current = null;
27757
+ if (dirty.current) onResizeCommit(latest.current, "pointer");
27758
+ dirty.current = false;
27759
+ }, [onResizeCommit]);
27760
+ var handleKeyDown = React.useCallback(function (e) {
27761
+ var next = keyboardResize(e.key, latest.current, policyRef.current, readContentMin(), {
27762
+ shiftKey: e.shiftKey,
27763
+ axis: axisRef.current
27764
+ });
27765
+ if (!next) return;
27766
+ // The key is ours even when it resolves to no movement (already pinned at
27767
+ // a bound), so the browser must not also scroll the surface.
27768
+ e.preventDefault();
27769
+ emitPreview(next, "keyboard");
27770
+ }, [emitPreview, readContentMin]);
27771
+ var handleKeyUp = React.useCallback(function (e) {
27772
+ if (!isResizeKey(e.key)) return;
27773
+ // The gesture end for the keyboard is the key release: one commit per
27774
+ // hold, mirroring pointer-up after a stream of moves.
27775
+ if (dirty.current) onResizeCommit(latest.current, "keyboard");
27776
+ dirty.current = false;
27777
+ }, [onResizeCommit]);
27778
+ return {
27779
+ role: "separator",
27780
+ tabIndex: 0,
27781
+ "aria-label": label,
27782
+ "aria-controls": cardId,
27783
+ "aria-valuemin": 0,
27784
+ "aria-valuemax": 100,
27785
+ "aria-valuenow": measurePercent(size, policy, readContentMin()),
27786
+ onPointerDown: handlePointerDown,
27787
+ onPointerMove: handlePointerMove,
27788
+ onPointerUp: endPointer,
27789
+ onLostPointerCapture: endPointer,
27790
+ onKeyDown: handleKeyDown,
27791
+ onKeyUp: handleKeyUp
27792
+ };
27793
+ };
27794
+
27343
27795
  exports.AccessLogIcon = AccessLogIcon$1;
27344
27796
  exports.Accordion = Accordion;
27345
27797
  exports.AccordionContext = AccordionContext;
@@ -27440,6 +27892,7 @@
27440
27892
  exports.CounterButton = CounterButton;
27441
27893
  exports.CrownBlankIcon = CrownBlankIcon$1;
27442
27894
  exports.CrownSelectedIcon = CrownSelectedIcon$1;
27895
+ exports.DEFAULT_KEYBOARD_STEP = DEFAULT_KEYBOARD_STEP;
27443
27896
  exports.DataTable = DataTable;
27444
27897
  exports.DatePicker = DatePicker;
27445
27898
  exports.DateStepper = DateStepper;
@@ -27625,6 +28078,7 @@
27625
28078
  exports.ProjectTemplateIcon = ProjectTemplateIcon$1;
27626
28079
  exports.ProjectsIcon = ProjectsIcon$1;
27627
28080
  exports.PuzzleIcon = PuzzleIcon$1;
28081
+ exports.RESIZE_KEYS = RESIZE_KEYS;
27628
28082
  exports.RadioBlankIcon = RadioBlankIcon$1;
27629
28083
  exports.RadioButton = RadioButton;
27630
28084
  exports.RadioButtonIcon = RadioButtonIcon$1;
@@ -27645,6 +28099,7 @@
27645
28099
  exports.RocketIcon = RocketIcon$1;
27646
28100
  exports.RocketSmallIcon = RocketSmallIcon$1;
27647
28101
  exports.RoundedRectangleIcon = RoundedRectangleIcon$1;
28102
+ exports.SHIFT_STEP_MULTIPLIER = SHIFT_STEP_MULTIPLIER;
27648
28103
  exports.STACKED_CARD_CONTROL_CLASS = STACKED_CARD_CONTROL_CLASS;
27649
28104
  exports.STACKED_CARD_COVER_RATIO = STACKED_CARD_COVER_RATIO;
27650
28105
  exports.STACKED_CARD_FIXED_ATTR = STACKED_CARD_FIXED_ATTR;
@@ -27747,16 +28202,24 @@
27747
28202
  exports._Dialog = _Dialog;
27748
28203
  exports._List = _List;
27749
28204
  exports.aiColors = aiColors;
28205
+ exports.applyResize = applyResize;
28206
+ exports.clamp = clamp;
27750
28207
  exports.colors = colors$1;
28208
+ exports.effectiveBounds = effectiveBounds;
27751
28209
  exports.formatCurrency = formatCurrency;
27752
28210
  exports.formatHours = formatHours;
27753
28211
  exports.formatNumber = formatNumber;
27754
28212
  exports.getNumberFromString = getNumberFromString;
27755
28213
  exports.isOptionGroup = isOptionGroup;
28214
+ exports.isResizeKey = isResizeKey;
27756
28215
  exports.isValidUrl = isValidUrl;
28216
+ exports.keyboardResize = keyboardResize;
27757
28217
  exports.layers = layers;
28218
+ exports.makeStart = makeStart;
28219
+ exports.measurePercent = measurePercent;
27758
28220
  exports.numberWithSeparator = numberWithSeparator;
27759
28221
  exports.signifierTypes = signifierTypes;
28222
+ exports.snap = snap;
27760
28223
  exports.toMoment = toMoment;
27761
28224
  exports.useContentStepperContext = useContentStepperContext;
27762
28225
  exports.useEntityGroupContext = useEntityGroupContext;
@@ -27767,6 +28230,7 @@
27767
28230
  exports.useLayerContext = useLayerContext;
27768
28231
  exports.useMenuContext = useMenuContext;
27769
28232
  exports.useResizeObserver = useResizeObserver$1;
28233
+ exports.useStackedCardResize = useStackedCardResize;
27770
28234
  exports.useWizardContext = useWizardContext;
27771
28235
  exports.validateNumberInput = validateNumberInput;
27772
28236
  exports.validateStopwatchTime = validateStopwatchTime;