@forgeax/app-shell 0.44.0 → 0.45.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
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactElement, HTMLAttributes, ReactNode } from 'react';
2
+ import { ReactElement, RefCallback, PointerEventHandler, HTMLAttributes, ReactNode } from 'react';
3
3
  import { DetachedWindowCapability } from './window.js';
4
4
  import { DockLayoutControlSnapshot, DockLayoutPanelControl, DockLayoutControlState, DockLayoutControlToggleSource } from './dock.js';
5
5
 
@@ -109,6 +109,27 @@ interface PointerReorderSession {
109
109
  /** Own one delegated pointer reorder lifecycle without owning item identity or mutation policy. */
110
110
  declare function installPointerReorderSession<Item>(options: PointerReorderSessionOptions<Item>): PointerReorderSession;
111
111
 
112
+ interface TabCloseInteractionsOptions<TabElement extends HTMLElement = HTMLElement> {
113
+ readonly close: () => void;
114
+ readonly disabled?: boolean;
115
+ readonly onPointerDown?: PointerEventHandler<TabElement>;
116
+ readonly onPointerUp?: PointerEventHandler<TabElement>;
117
+ readonly onPointerLeave?: PointerEventHandler<TabElement>;
118
+ }
119
+ interface TabCloseInteractions<TabElement extends HTMLElement = HTMLElement> {
120
+ readonly closeRef: RefCallback<HTMLElement>;
121
+ readonly onPointerDown: PointerEventHandler<TabElement>;
122
+ readonly onPointerUp: PointerEventHandler<TabElement>;
123
+ readonly onPointerLeave: PointerEventHandler<TabElement>;
124
+ }
125
+ /**
126
+ * Owns the browser-specific close gestures for a tab without owning tab identity,
127
+ * close policy, or presentation. Native pointerdown keeps an action surface from
128
+ * activating its parent tab; a middle-button close settles only the matching
129
+ * pointer that armed it.
130
+ */
131
+ declare function useTabCloseInteractions<TabElement extends HTMLElement = HTMLElement>(options: TabCloseInteractionsOptions<TabElement>): TabCloseInteractions<TabElement>;
132
+
112
133
  type PanelContentPadding = 'none' | 'sm' | 'md';
113
134
  type PanelContentScroll = 'none' | 'auto';
114
135
  type PanelContentTone = 'default' | 'surface' | 'tool';
@@ -250,4 +271,4 @@ interface ShellSlotProps extends HTMLAttributes<HTMLElement> {
250
271
  /** Structural shell marker that preserves the caller's semantic element. */
251
272
  declare function ShellSlot({ as, name, ...props }: ShellSlotProps): ReactElement;
252
273
 
253
- 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, type PointerReorderFinishReason, type PointerReorderSession, type PointerReorderSessionOptions, ResizeHandle, type ResizeHandleProps, ShellSlot, type ShellSlotElement, type ShellSlotProps, SlotDebugOverlay, SurfacePlaceholder, type SurfacePlaceholderProps, SurfaceRegion, type SurfaceRegionProps, type ThresholdPointerDragFinishReason, type ThresholdPointerDragSession, type ThresholdPointerDragSessionOptions, applyAnchoredResizeDelta, beginAnchoredResize, createPersistentSizeStore, hashSlotHue, installPointerDragSession, installPointerReorderSession, installThresholdPointerDragSession, isSlotDebugEnabled, useDockLayoutControlBinding, useLocalSize, usePersistentSizeStore };
274
+ 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, type PointerReorderFinishReason, type PointerReorderSession, type PointerReorderSessionOptions, ResizeHandle, type ResizeHandleProps, ShellSlot, type ShellSlotElement, type ShellSlotProps, SlotDebugOverlay, SurfacePlaceholder, type SurfacePlaceholderProps, SurfaceRegion, type SurfaceRegionProps, type TabCloseInteractions, type TabCloseInteractionsOptions, type ThresholdPointerDragFinishReason, type ThresholdPointerDragSession, type ThresholdPointerDragSessionOptions, applyAnchoredResizeDelta, beginAnchoredResize, createPersistentSizeStore, hashSlotHue, installPointerDragSession, installPointerReorderSession, installThresholdPointerDragSession, isSlotDebugEnabled, useDockLayoutControlBinding, useLocalSize, usePersistentSizeStore, useTabCloseInteractions };
package/dist/react.js CHANGED
@@ -1175,6 +1175,67 @@ function installPointerReorderSession(options) {
1175
1175
  };
1176
1176
  }
1177
1177
 
1178
+ // src/tab-close.tsx
1179
+ import {
1180
+ useCallback,
1181
+ useEffect as useEffect3,
1182
+ useRef as useRef2
1183
+ } from "react";
1184
+ function useTabCloseInteractions(options) {
1185
+ const optionsRef = useRef2(options);
1186
+ const closeElementRef = useRef2(null);
1187
+ const middlePointerRef = useRef2(null);
1188
+ optionsRef.current = options;
1189
+ const closeCurrent = useCallback(() => {
1190
+ const current = optionsRef.current;
1191
+ if (current.disabled) return;
1192
+ try {
1193
+ current.close();
1194
+ } catch {
1195
+ }
1196
+ }, []);
1197
+ const onClosePointerDown = useCallback((event) => {
1198
+ event.preventDefault();
1199
+ event.stopPropagation();
1200
+ closeCurrent();
1201
+ }, [closeCurrent]);
1202
+ const closeRef = useCallback((element) => {
1203
+ const previous = closeElementRef.current;
1204
+ if (previous === element) return;
1205
+ previous?.removeEventListener("pointerdown", onClosePointerDown);
1206
+ closeElementRef.current = element;
1207
+ element?.addEventListener("pointerdown", onClosePointerDown);
1208
+ }, [onClosePointerDown]);
1209
+ const forward = useCallback((callback, event) => {
1210
+ try {
1211
+ callback?.(event);
1212
+ } catch {
1213
+ }
1214
+ }, []);
1215
+ const onPointerDown = useCallback((event) => {
1216
+ middlePointerRef.current = event.button === 1 ? event.pointerId : null;
1217
+ forward(optionsRef.current.onPointerDown, event);
1218
+ }, [forward]);
1219
+ const onPointerUp = useCallback((event) => {
1220
+ const matches = middlePointerRef.current === event.pointerId;
1221
+ const shouldClose = event.button === 1 && matches;
1222
+ if (matches) middlePointerRef.current = null;
1223
+ if (shouldClose) closeCurrent();
1224
+ forward(optionsRef.current.onPointerUp, event);
1225
+ }, [closeCurrent, forward]);
1226
+ const onPointerLeave = useCallback((event) => {
1227
+ if (middlePointerRef.current === event.pointerId) middlePointerRef.current = null;
1228
+ forward(optionsRef.current.onPointerLeave, event);
1229
+ }, [forward]);
1230
+ useEffect3(() => () => {
1231
+ middlePointerRef.current = null;
1232
+ const closeElement = closeElementRef.current;
1233
+ closeElementRef.current = null;
1234
+ closeElement?.removeEventListener("pointerdown", onClosePointerDown);
1235
+ }, [onClosePointerDown]);
1236
+ return { closeRef, onPointerDown, onPointerUp, onPointerLeave };
1237
+ }
1238
+
1178
1239
  // src/panel.tsx
1179
1240
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
1180
1241
  function PanelEmptyState({
@@ -1317,7 +1378,7 @@ var SurfaceRegion = forwardRef(
1317
1378
  );
1318
1379
 
1319
1380
  // src/dock-layout-control.ts
1320
- import { useEffect as useEffect3, useMemo, useSyncExternalStore as useSyncExternalStore2 } from "react";
1381
+ import { useEffect as useEffect4, useMemo, useSyncExternalStore as useSyncExternalStore2 } from "react";
1321
1382
  function useDockLayoutControlBinding({
1322
1383
  state,
1323
1384
  onToggle,
@@ -1330,7 +1391,7 @@ function useDockLayoutControlBinding({
1330
1391
  state.getSnapshot,
1331
1392
  state.getSnapshot
1332
1393
  );
1333
- useEffect3(() => installDockLayoutControlToggle({ onToggle }, state), [onToggle, state]);
1394
+ useEffect4(() => installDockLayoutControlToggle({ onToggle }, state), [onToggle, state]);
1334
1395
  const panelControl = useMemo(() => createDockLayoutPanelControl({
1335
1396
  isOpen: isPanelOpen,
1336
1397
  close: closePanel,
@@ -1341,7 +1402,7 @@ function useDockLayoutControlBinding({
1341
1402
 
1342
1403
  // src/floating-menu.tsx
1343
1404
  var import_react_dom = __toESM(require_react_dom(), 1);
1344
- import { useEffect as useEffect4, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
1405
+ import { useEffect as useEffect5, useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
1345
1406
  import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1346
1407
  var VIEWPORT_PADDING = 8;
1347
1408
  function FloatingMenu({
@@ -1354,7 +1415,7 @@ function FloatingMenu({
1354
1415
  className,
1355
1416
  children
1356
1417
  }) {
1357
- const menuRef = useRef2(null);
1418
+ const menuRef = useRef3(null);
1358
1419
  const [position, setPosition] = useState3({ top: 0 });
1359
1420
  useLayoutEffect(() => {
1360
1421
  if (!open) return;
@@ -1391,7 +1452,7 @@ function FloatingMenu({
1391
1452
  }
1392
1453
  }
1393
1454
  }, [align, anchor?.bottom, anchor?.left, anchor?.right, anchor?.top, offset, open, point?.x, point?.y]);
1394
- useEffect4(() => {
1455
+ useEffect5(() => {
1395
1456
  if (!open) return;
1396
1457
  const closeOnEscape = (event) => {
1397
1458
  if (event.key === "Escape") onClose();
@@ -1548,7 +1609,8 @@ export {
1548
1609
  isSlotDebugEnabled,
1549
1610
  useDockLayoutControlBinding,
1550
1611
  useLocalSize,
1551
- usePersistentSizeStore
1612
+ usePersistentSizeStore,
1613
+ useTabCloseInteractions
1552
1614
  };
1553
1615
  /*! Bundled license information:
1554
1616
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/app-shell",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "description": "Generic Dock, Panel, Window, and Slot composition primitives",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",