@ai-matrx/design-system 0.37.1 → 0.38.1

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/index.cjs CHANGED
@@ -2132,12 +2132,14 @@ var VisuallyHidden2 = __toESM(require("@radix-ui/react-visually-hidden"), 1);
2132
2132
  var import_react_tree3 = require("@ai-matrx/kit/react-tree");
2133
2133
  var React17 = __toESM(require("react"), 1);
2134
2134
  var import_jsx_runtime21 = require("react/jsx-runtime");
2135
+ var DialogWindowedByDefaultContext = React17.createContext(false);
2135
2136
  var Dialog = ({
2136
2137
  children,
2137
2138
  ...props
2138
2139
  }) => {
2139
- const modal = props.modal ?? true;
2140
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogPrimitive2.Root, { ...props, modal, children }) });
2140
+ const isMobile = useIsMobile();
2141
+ const modal = props.modal ?? isMobile;
2142
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogWindowedByDefaultContext.Provider, { value: props.modal === void 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogPrimitive2.Root, { ...props, modal, children }) }) });
2141
2143
  };
2142
2144
  Dialog.displayName = "Dialog";
2143
2145
  var DialogTrigger = DialogPrimitive2.Trigger;
@@ -2185,6 +2187,11 @@ DialogContentPrimitive.displayName = "DialogContentPrimitive";
2185
2187
  var DIALOG_DESKTOP_CLASSES2 = "matrx-dialog-card fixed inset-0 z-[10000] m-auto grid h-fit min-w-0 w-full max-w-lg gap-4 max-h-[85dvh] overflow-y-auto overscroll-contain [--dialog-pad:1.5rem] overflow-x-clip border bg-background p-6 shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full sm:rounded-lg";
2186
2188
  var DIALOG_MOBILE_SHEET_CLASSES2 = "matrx-mobile-sheet matrx-dialog-card fixed inset-x-0 bottom-0 left-0 right-0 top-auto z-[10000] flex min-w-0 flex-col w-full max-w-full max-h-[90dvh] gap-4 [--dialog-pad:1rem] overflow-x-clip border-t bg-background p-4 pb-safe shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full rounded-t-2xl rounded-b-none overflow-y-auto overscroll-contain";
2187
2189
  var DIALOG_MOBILE_SHEET_OVERRIDE2 = "inset-x-0 bottom-0 left-0 right-0 top-auto translate-none w-full max-w-full max-h-[90dvh] rounded-b-none rounded-t-2xl overflow-y-auto";
2190
+ var DialogWindowHandleContext = React17.createContext(null);
2191
+ var WINDOW_KEEP_VISIBLE = 80;
2192
+ function isInteractiveTarget(target) {
2193
+ return target instanceof Element && Boolean(target.closest("button, a, input, textarea, select, [role='button'], [contenteditable='true']"));
2194
+ }
2188
2195
  var DialogContent = React17.forwardRef(
2189
2196
  ({
2190
2197
  className,
@@ -2193,15 +2200,51 @@ var DialogContent = React17.forwardRef(
2193
2200
  animated = true,
2194
2201
  showCloseButton = true,
2195
2202
  container,
2203
+ dismissOnOutsideClick = false,
2204
+ onInteractOutside,
2205
+ onEscapeKeyDown,
2206
+ style,
2196
2207
  ...props
2197
2208
  }, ref) => {
2198
2209
  const isMobile = useIsMobile();
2199
2210
  const isModal = useRadixDialogModal();
2200
2211
  const injectedContainer = usePortalContainer(container);
2201
2212
  const asSheet = mobileSheet && isMobile;
2213
+ const windowed = !isModal && !asSheet;
2214
+ const windowedByDefault = React17.useContext(DialogWindowedByDefaultContext);
2202
2215
  const [containerEl, setContainerEl] = React17.useState(
2203
2216
  null
2204
2217
  );
2218
+ const [offset, setOffset] = React17.useState({ x: 0, y: 0 });
2219
+ const dragRef = React17.useRef(null);
2220
+ const handle = React17.useMemo(() => {
2221
+ if (!windowed) return null;
2222
+ const clamp = (value, limit) => Math.max(-limit, Math.min(limit, value));
2223
+ return {
2224
+ onPointerDown: (event) => {
2225
+ if (event.button !== 0 || isInteractiveTarget(event.target)) return;
2226
+ dragRef.current = { x: event.clientX, y: event.clientY, baseX: offset.x, baseY: offset.y };
2227
+ event.currentTarget.setPointerCapture?.(event.pointerId);
2228
+ },
2229
+ onPointerMove: (event) => {
2230
+ const start = dragRef.current;
2231
+ if (!start) return;
2232
+ setOffset({
2233
+ x: clamp(start.baseX + event.clientX - start.x, window.innerWidth / 2 - WINDOW_KEEP_VISIBLE / 2),
2234
+ y: clamp(start.baseY + event.clientY - start.y, window.innerHeight / 2 - WINDOW_KEEP_VISIBLE / 2)
2235
+ });
2236
+ },
2237
+ onPointerUp: (event) => {
2238
+ if (!dragRef.current) return;
2239
+ dragRef.current = null;
2240
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
2241
+ },
2242
+ onPointerCancel: () => {
2243
+ dragRef.current = null;
2244
+ },
2245
+ onDoubleClick: () => setOffset({ x: 0, y: 0 })
2246
+ };
2247
+ }, [windowed, offset.x, offset.y]);
2205
2248
  const mergedRef = React17.useCallback(
2206
2249
  (node) => {
2207
2250
  setContainerEl(node);
@@ -2229,19 +2272,33 @@ var DialogContent = React17.forwardRef(
2229
2272
  // can sit beside a host's floating window manager (which starts at
2230
2273
  // z=1000); staying below that boundary makes a newly focused window
2231
2274
  // usable without coupling the two systems.
2232
- !isModal && "z-[900]"
2275
+ !isModal && !windowedByDefault && "z-[900]",
2276
+ // Without an overlay the card needs its own separation from the page.
2277
+ windowed && "shadow-2xl ring-1 ring-border/60"
2233
2278
  ),
2279
+ style: windowed && (offset.x !== 0 || offset.y !== 0) ? { ...style, translate: `${offset.x}px ${offset.y}px` } : style,
2280
+ onInteractOutside: (event) => {
2281
+ onInteractOutside?.(event);
2282
+ if (windowed && !dismissOnOutsideClick) event.preventDefault();
2283
+ },
2284
+ onEscapeKeyDown: (event) => {
2285
+ onEscapeKeyDown?.(event);
2286
+ const pressedIn = event.target instanceof Node && event.target !== document.body ? event.target : document.activeElement;
2287
+ if (windowed && containerEl && !(pressedIn && containerEl.contains(pressedIn))) {
2288
+ event.preventDefault();
2289
+ }
2290
+ },
2234
2291
  ...hasDescription ? {} : { "aria-describedby": void 0 },
2235
2292
  ...props,
2236
2293
  children: [
2237
2294
  hasTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(VisuallyHidden2.Root, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogPrimitive2.Title, { children: "Dialog" }) }),
2238
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2295
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogWindowHandleContext.Provider, { value: handle, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2239
2296
  PortalContainerProvider,
2240
2297
  {
2241
2298
  container: containerEl ?? injectedContainer ?? null,
2242
2299
  children
2243
2300
  }
2244
- ) }),
2301
+ ) }) }),
2245
2302
  showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2246
2303
  DialogPrimitive2.Close,
2247
2304
  {
@@ -2264,19 +2321,28 @@ DialogContent.displayName = DialogPrimitive2.Content.displayName;
2264
2321
  var DialogHeader = ({
2265
2322
  className,
2266
2323
  ...props
2267
- }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2268
- "div",
2269
- {
2270
- "data-slot": "dialog-header",
2271
- className: cn(
2272
- // DialogContent owns an absolute close control. Reserve its hit area in
2273
- // every header so trailing actions never render underneath it.
2274
- "flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
2275
- className
2276
- ),
2277
- ...props
2278
- }
2279
- );
2324
+ }) => {
2325
+ const handle = React17.useContext(DialogWindowHandleContext);
2326
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2327
+ "div",
2328
+ {
2329
+ "data-slot": "dialog-header",
2330
+ ...handle ? {
2331
+ ...handle,
2332
+ "data-window-handle": "",
2333
+ title: "Drag to move \xB7 double-click to recenter"
2334
+ } : {},
2335
+ className: cn(
2336
+ // DialogContent owns an absolute close control. Reserve its hit area in
2337
+ // every header so trailing actions never render underneath it.
2338
+ "flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
2339
+ handle && "cursor-grab select-none active:cursor-grabbing",
2340
+ className
2341
+ ),
2342
+ ...props
2343
+ }
2344
+ );
2345
+ };
2280
2346
  DialogHeader.displayName = "DialogHeader";
2281
2347
  var DialogFooter = ({
2282
2348
  className,