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