@forgeax/app-shell 0.41.0 → 0.42.0

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.
package/dist/react.d.ts CHANGED
@@ -41,6 +41,18 @@ interface ResizeHandleProps {
41
41
  ariaLabel?: string;
42
42
  title?: string;
43
43
  }
44
+ type PointerDragFinishReason = 'pointerup' | 'pointercancel' | 'lostpointercapture' | 'dispose' | 'error';
45
+ interface PointerDragSessionOptions<Session> {
46
+ readonly element: HTMLElement;
47
+ readonly begin: (event: globalThis.PointerEvent) => Session | null;
48
+ readonly move: (session: Session, event: globalThis.PointerEvent) => void;
49
+ readonly end?: (session: Session, reason: PointerDragFinishReason) => void;
50
+ readonly preventDefault?: boolean;
51
+ readonly stopPropagation?: boolean;
52
+ readonly draggingClassName?: string;
53
+ }
54
+ /** Own one imperative pointer-capture drag session and its balanced teardown. */
55
+ declare function installPointerDragSession<Session>(options: PointerDragSessionOptions<Session>): () => void;
44
56
  type AnchoredResizeDirection = 'subtract' | 'add';
45
57
  interface AnchoredResizeSession {
46
58
  readonly startSize: number;
@@ -202,4 +214,4 @@ interface ShellSlotProps extends HTMLAttributes<HTMLElement> {
202
214
  /** Structural shell marker that preserves the caller's semantic element. */
203
215
  declare function ShellSlot({ as, name, ...props }: ShellSlotProps): ReactElement;
204
216
 
205
- export { type AnchoredResizeDirection, AnchoredResizeHandle, type AnchoredResizeHandleProps, type AnchoredResizeSession, DetachedPanelBoundary, type DetachedPanelBoundaryProps, DetachedSurfaceFrame, type DetachedSurfaceFrameProps, DetachedSurfaceStatus, type DetachedSurfaceStatusProps, type DetachedSurfaceStatusTone, type DockLayoutControlBinding, type DockLayoutControlBindingOptions, DockLayoutMenu, type DockLayoutMenuPanel, type DockLayoutMenuProps, FloatingMenu, type FloatingMenuAnchor, type FloatingMenuPoint, type FloatingMenuProps, type PanelContentPadding, type PanelContentPolicy, type PanelContentScroll, type PanelContentTone, PanelEmptyState, type PanelEmptyStateProps, PanelSurface, type PanelSurfaceProps, type PersistentSizeStorage, type PersistentSizeStore, type PersistentSizeStoreOptions, type PersistentSizeUpdate, ResizeHandle, type ResizeHandleProps, ShellSlot, type ShellSlotElement, type ShellSlotProps, SlotDebugOverlay, SurfacePlaceholder, type SurfacePlaceholderProps, SurfaceRegion, type SurfaceRegionProps, applyAnchoredResizeDelta, beginAnchoredResize, createPersistentSizeStore, hashSlotHue, isSlotDebugEnabled, useDockLayoutControlBinding, useLocalSize, usePersistentSizeStore };
217
+ export { type AnchoredResizeDirection, AnchoredResizeHandle, type AnchoredResizeHandleProps, type AnchoredResizeSession, DetachedPanelBoundary, type DetachedPanelBoundaryProps, DetachedSurfaceFrame, type DetachedSurfaceFrameProps, DetachedSurfaceStatus, type DetachedSurfaceStatusProps, type DetachedSurfaceStatusTone, type DockLayoutControlBinding, type DockLayoutControlBindingOptions, DockLayoutMenu, type DockLayoutMenuPanel, type DockLayoutMenuProps, FloatingMenu, type FloatingMenuAnchor, type FloatingMenuPoint, type FloatingMenuProps, type PanelContentPadding, type PanelContentPolicy, type PanelContentScroll, type PanelContentTone, PanelEmptyState, type PanelEmptyStateProps, PanelSurface, type PanelSurfaceProps, type PersistentSizeStorage, type PersistentSizeStore, type PersistentSizeStoreOptions, type PersistentSizeUpdate, type PointerDragFinishReason, type PointerDragSessionOptions, ResizeHandle, type ResizeHandleProps, ShellSlot, type ShellSlotElement, type ShellSlotProps, SlotDebugOverlay, SurfacePlaceholder, type SurfacePlaceholderProps, SurfaceRegion, type SurfaceRegionProps, applyAnchoredResizeDelta, beginAnchoredResize, createPersistentSizeStore, hashSlotHue, installPointerDragSession, isSlotDebugEnabled, useDockLayoutControlBinding, useLocalSize, usePersistentSizeStore };
package/dist/react.js CHANGED
@@ -690,13 +690,13 @@ function useLocalSize(key, initial, min, max) {
690
690
  };
691
691
  return [value, setValue];
692
692
  }
693
- var bodyClassLeases = /* @__PURE__ */ new WeakMap();
694
- function acquireBodyClassLease(body, tokens) {
693
+ var elementClassLeases = /* @__PURE__ */ new WeakMap();
694
+ function acquireElementClassLease(element, tokens) {
695
695
  const uniqueTokens = [...new Set(tokens)];
696
- let records = bodyClassLeases.get(body);
696
+ let records = elementClassLeases.get(element);
697
697
  if (!records) {
698
698
  records = /* @__PURE__ */ new Map();
699
- bodyClassLeases.set(body, records);
699
+ elementClassLeases.set(element, records);
700
700
  }
701
701
  for (const token of uniqueTokens) {
702
702
  const existing = records.get(token);
@@ -704,8 +704,8 @@ function acquireBodyClassLease(body, tokens) {
704
704
  existing.count += 1;
705
705
  continue;
706
706
  }
707
- const owned = !body.classList.contains(token);
708
- if (owned) body.classList.add(token);
707
+ const owned = !element.classList.contains(token);
708
+ if (owned) element.classList.add(token);
709
709
  records.set(token, { count: 1, owned });
710
710
  }
711
711
  let active = true;
@@ -718,9 +718,97 @@ function acquireBodyClassLease(body, tokens) {
718
718
  record.count -= 1;
719
719
  if (record.count > 0) continue;
720
720
  records?.delete(token);
721
- if (record.owned) body.classList.remove(token);
721
+ if (record.owned) element.classList.remove(token);
722
722
  }
723
- if (records?.size === 0) bodyClassLeases.delete(body);
723
+ if (records?.size === 0) elementClassLeases.delete(element);
724
+ };
725
+ }
726
+ function installPointerDragSession(options) {
727
+ const { element } = options;
728
+ let disposed = false;
729
+ let starting = false;
730
+ let active = null;
731
+ const removeSessionListeners = () => {
732
+ element.removeEventListener("pointermove", onPointerMove);
733
+ element.removeEventListener("pointerup", onPointerFinish);
734
+ element.removeEventListener("pointercancel", onPointerFinish);
735
+ element.removeEventListener("lostpointercapture", onPointerFinish);
736
+ };
737
+ const finish = (reason) => {
738
+ const current = active;
739
+ if (!current) return;
740
+ active = null;
741
+ removeSessionListeners();
742
+ try {
743
+ element.releasePointerCapture(current.pointerId);
744
+ } catch {
745
+ }
746
+ try {
747
+ current.releaseClass();
748
+ } catch {
749
+ }
750
+ try {
751
+ options.end?.(current.session, reason);
752
+ } catch {
753
+ }
754
+ };
755
+ function onPointerMove(event) {
756
+ const current = active;
757
+ if (!current || current.pointerId !== event.pointerId) return;
758
+ try {
759
+ options.move(current.session, event);
760
+ } catch {
761
+ finish("error");
762
+ }
763
+ }
764
+ function onPointerFinish(event) {
765
+ if (!active || active.pointerId !== event.pointerId) return;
766
+ const reason = event.type === "pointercancel" ? "pointercancel" : event.type === "lostpointercapture" ? "lostpointercapture" : "pointerup";
767
+ finish(reason);
768
+ }
769
+ const onPointerDown = (event) => {
770
+ if (disposed || active || starting) return;
771
+ starting = true;
772
+ let session;
773
+ try {
774
+ session = options.begin(event);
775
+ } catch {
776
+ starting = false;
777
+ return;
778
+ }
779
+ starting = false;
780
+ if (session === null) return;
781
+ if (disposed) {
782
+ try {
783
+ options.end?.(session, "dispose");
784
+ } catch {
785
+ }
786
+ return;
787
+ }
788
+ if (options.preventDefault) event.preventDefault();
789
+ if (options.stopPropagation) event.stopPropagation();
790
+ const classTokens = options.draggingClassName?.trim().split(/\s+/).filter(Boolean) ?? [];
791
+ active = {
792
+ pointerId: event.pointerId,
793
+ session,
794
+ releaseClass: acquireElementClassLease(element, classTokens)
795
+ };
796
+ element.addEventListener("pointermove", onPointerMove);
797
+ element.addEventListener("pointerup", onPointerFinish);
798
+ element.addEventListener("pointercancel", onPointerFinish);
799
+ element.addEventListener("lostpointercapture", onPointerFinish);
800
+ try {
801
+ element.setPointerCapture(event.pointerId);
802
+ } catch {
803
+ finish("error");
804
+ }
805
+ };
806
+ element.addEventListener("pointerdown", onPointerDown);
807
+ return () => {
808
+ if (disposed) return;
809
+ disposed = true;
810
+ element.removeEventListener("pointerdown", onPointerDown);
811
+ finish("dispose");
724
812
  };
725
813
  }
726
814
  function beginAnchoredResize(startSize, direction = "subtract") {
@@ -845,7 +933,7 @@ function AnchoredResizeHandle({
845
933
  finishSession();
846
934
  const bodyClassTokens = bodyClassNameRef.current?.trim().split(/\s+/).filter(Boolean) ?? [];
847
935
  const resize = beginAnchoredResize(readSizeRef.current(), directionRef.current);
848
- const releaseBodyClass = typeof document !== "undefined" && bodyClassTokens.length > 0 ? acquireBodyClassLease(document.body, bodyClassTokens) : () => {
936
+ const releaseBodyClass = typeof document !== "undefined" && bodyClassTokens.length > 0 ? acquireElementClassLease(document.body, bodyClassTokens) : () => {
849
937
  };
850
938
  sessionRef.current = {
851
939
  resize,
@@ -1235,6 +1323,7 @@ export {
1235
1323
  beginAnchoredResize,
1236
1324
  createPersistentSizeStore,
1237
1325
  hashSlotHue,
1326
+ installPointerDragSession,
1238
1327
  isSlotDebugEnabled,
1239
1328
  useDockLayoutControlBinding,
1240
1329
  useLocalSize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/app-shell",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "Generic Dock, Panel, Window, and Slot composition primitives",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",