@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.
@@ -223,12 +223,14 @@ function useIsMobile() {
223
223
 
224
224
  // src/dialog.tsx
225
225
  import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
226
+ var DialogWindowedByDefaultContext = React5.createContext(false);
226
227
  var Dialog = ({
227
228
  children,
228
229
  ...props
229
230
  }) => {
230
- const modal = props.modal ?? true;
231
- return /* @__PURE__ */ jsx5(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ jsx5(DialogPrimitive.Root, { ...props, modal, children }) });
231
+ const isMobile = useIsMobile();
232
+ const modal = props.modal ?? isMobile;
233
+ return /* @__PURE__ */ jsx5(DialogWindowedByDefaultContext.Provider, { value: props.modal === void 0, children: /* @__PURE__ */ jsx5(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ jsx5(DialogPrimitive.Root, { ...props, modal, children }) }) });
232
234
  };
233
235
  Dialog.displayName = "Dialog";
234
236
  var DialogPortal = ({
@@ -273,6 +275,11 @@ DialogContentPrimitive.displayName = "DialogContentPrimitive";
273
275
  var DIALOG_DESKTOP_CLASSES = "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";
274
276
  var DIALOG_MOBILE_SHEET_CLASSES = "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";
275
277
  var DIALOG_MOBILE_SHEET_OVERRIDE = "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";
278
+ var DialogWindowHandleContext = React5.createContext(null);
279
+ var WINDOW_KEEP_VISIBLE = 80;
280
+ function isInteractiveTarget(target) {
281
+ return target instanceof Element && Boolean(target.closest("button, a, input, textarea, select, [role='button'], [contenteditable='true']"));
282
+ }
276
283
  var DialogContent = React5.forwardRef(
277
284
  ({
278
285
  className,
@@ -281,15 +288,51 @@ var DialogContent = React5.forwardRef(
281
288
  animated = true,
282
289
  showCloseButton = true,
283
290
  container,
291
+ dismissOnOutsideClick = false,
292
+ onInteractOutside,
293
+ onEscapeKeyDown,
294
+ style,
284
295
  ...props
285
296
  }, ref) => {
286
297
  const isMobile = useIsMobile();
287
298
  const isModal = useRadixDialogModal();
288
299
  const injectedContainer = usePortalContainer(container);
289
300
  const asSheet = mobileSheet && isMobile;
301
+ const windowed = !isModal && !asSheet;
302
+ const windowedByDefault = React5.useContext(DialogWindowedByDefaultContext);
290
303
  const [containerEl, setContainerEl] = React5.useState(
291
304
  null
292
305
  );
306
+ const [offset, setOffset] = React5.useState({ x: 0, y: 0 });
307
+ const dragRef = React5.useRef(null);
308
+ const handle = React5.useMemo(() => {
309
+ if (!windowed) return null;
310
+ const clamp = (value, limit) => Math.max(-limit, Math.min(limit, value));
311
+ return {
312
+ onPointerDown: (event) => {
313
+ if (event.button !== 0 || isInteractiveTarget(event.target)) return;
314
+ dragRef.current = { x: event.clientX, y: event.clientY, baseX: offset.x, baseY: offset.y };
315
+ event.currentTarget.setPointerCapture?.(event.pointerId);
316
+ },
317
+ onPointerMove: (event) => {
318
+ const start = dragRef.current;
319
+ if (!start) return;
320
+ setOffset({
321
+ x: clamp(start.baseX + event.clientX - start.x, window.innerWidth / 2 - WINDOW_KEEP_VISIBLE / 2),
322
+ y: clamp(start.baseY + event.clientY - start.y, window.innerHeight / 2 - WINDOW_KEEP_VISIBLE / 2)
323
+ });
324
+ },
325
+ onPointerUp: (event) => {
326
+ if (!dragRef.current) return;
327
+ dragRef.current = null;
328
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
329
+ },
330
+ onPointerCancel: () => {
331
+ dragRef.current = null;
332
+ },
333
+ onDoubleClick: () => setOffset({ x: 0, y: 0 })
334
+ };
335
+ }, [windowed, offset.x, offset.y]);
293
336
  const mergedRef = React5.useCallback(
294
337
  (node) => {
295
338
  setContainerEl(node);
@@ -317,19 +360,33 @@ var DialogContent = React5.forwardRef(
317
360
  // can sit beside a host's floating window manager (which starts at
318
361
  // z=1000); staying below that boundary makes a newly focused window
319
362
  // usable without coupling the two systems.
320
- !isModal && "z-[900]"
363
+ !isModal && !windowedByDefault && "z-[900]",
364
+ // Without an overlay the card needs its own separation from the page.
365
+ windowed && "shadow-2xl ring-1 ring-border/60"
321
366
  ),
367
+ style: windowed && (offset.x !== 0 || offset.y !== 0) ? { ...style, translate: `${offset.x}px ${offset.y}px` } : style,
368
+ onInteractOutside: (event) => {
369
+ onInteractOutside?.(event);
370
+ if (windowed && !dismissOnOutsideClick) event.preventDefault();
371
+ },
372
+ onEscapeKeyDown: (event) => {
373
+ onEscapeKeyDown?.(event);
374
+ const pressedIn = event.target instanceof Node && event.target !== document.body ? event.target : document.activeElement;
375
+ if (windowed && containerEl && !(pressedIn && containerEl.contains(pressedIn))) {
376
+ event.preventDefault();
377
+ }
378
+ },
322
379
  ...hasDescription ? {} : { "aria-describedby": void 0 },
323
380
  ...props,
324
381
  children: [
325
382
  hasTitle ? null : /* @__PURE__ */ jsx5(VisuallyHidden.Root, { children: /* @__PURE__ */ jsx5(DialogPrimitive.Title, { children: "Dialog" }) }),
326
- /* @__PURE__ */ jsx5(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ jsx5(
383
+ /* @__PURE__ */ jsx5(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ jsx5(DialogWindowHandleContext.Provider, { value: handle, children: /* @__PURE__ */ jsx5(
327
384
  PortalContainerProvider,
328
385
  {
329
386
  container: containerEl ?? injectedContainer ?? null,
330
387
  children
331
388
  }
332
- ) }),
389
+ ) }) }),
333
390
  showCloseButton ? /* @__PURE__ */ jsxs2(
334
391
  DialogPrimitive.Close,
335
392
  {
@@ -352,19 +409,28 @@ DialogContent.displayName = DialogPrimitive.Content.displayName;
352
409
  var DialogHeader = ({
353
410
  className,
354
411
  ...props
355
- }) => /* @__PURE__ */ jsx5(
356
- "div",
357
- {
358
- "data-slot": "dialog-header",
359
- className: cn(
360
- // DialogContent owns an absolute close control. Reserve its hit area in
361
- // every header so trailing actions never render underneath it.
362
- "flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
363
- className
364
- ),
365
- ...props
366
- }
367
- );
412
+ }) => {
413
+ const handle = React5.useContext(DialogWindowHandleContext);
414
+ return /* @__PURE__ */ jsx5(
415
+ "div",
416
+ {
417
+ "data-slot": "dialog-header",
418
+ ...handle ? {
419
+ ...handle,
420
+ "data-window-handle": "",
421
+ title: "Drag to move \xB7 double-click to recenter"
422
+ } : {},
423
+ className: cn(
424
+ // DialogContent owns an absolute close control. Reserve its hit area in
425
+ // every header so trailing actions never render underneath it.
426
+ "flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
427
+ handle && "cursor-grab select-none active:cursor-grabbing",
428
+ className
429
+ ),
430
+ ...props
431
+ }
432
+ );
433
+ };
368
434
  DialogHeader.displayName = "DialogHeader";
369
435
  var DialogFooter = ({
370
436
  className,
@@ -2991,7 +3057,7 @@ function CopyButtons(props) {
2991
3057
  // src/data-table/MatrxUuidCell.tsx
2992
3058
  import {
2993
3059
  useEffect as useEffect4,
2994
- useRef as useRef4,
3060
+ useRef as useRef5,
2995
3061
  useState as useState7
2996
3062
  } from "react";
2997
3063
 
@@ -3079,7 +3145,7 @@ function MatrxUuidCell({
3079
3145
  const [copied, setCopied] = useState7(false);
3080
3146
  const [opening, setOpening] = useState7(false);
3081
3147
  const [peekOpen, setPeekOpen] = useState7(false);
3082
- const timer = useRef4(null);
3148
+ const timer = useRef5(null);
3083
3149
  useEffect4(
3084
3150
  () => () => {
3085
3151
  if (timer.current) clearTimeout(timer.current);