@bearmenu/ui 0.8.37 → 0.8.39

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.
@@ -4,20 +4,30 @@ import * as React from 'react';
4
4
  import { Drawer as Drawer$1 } from 'vaul';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
 
7
- var DrawerDirectionContext = React.createContext("bottom");
7
+ var DrawerContext = React.createContext({
8
+ direction: "bottom",
9
+ modal: true
10
+ });
8
11
  var Drawer = (_a) => {
9
12
  var _b = _a, {
10
13
  shouldScaleBackground = false,
11
- direction = "bottom"
14
+ direction = "bottom",
15
+ modal = true,
16
+ autoFocus = true
12
17
  } = _b, props = __objRest(_b, [
13
18
  "shouldScaleBackground",
14
- "direction"
19
+ "direction",
20
+ "modal",
21
+ "autoFocus"
15
22
  ]);
16
- return /* @__PURE__ */ jsx(DrawerDirectionContext.Provider, { value: direction, children: /* @__PURE__ */ jsx(
23
+ const context = React.useMemo(() => ({ direction, modal }), [direction, modal]);
24
+ return /* @__PURE__ */ jsx(DrawerContext.Provider, { value: context, children: /* @__PURE__ */ jsx(
17
25
  Drawer$1.Root,
18
26
  __spreadValues({
19
27
  shouldScaleBackground,
20
- direction
28
+ direction,
29
+ modal,
30
+ autoFocus
21
31
  }, props)
22
32
  ) });
23
33
  };
@@ -39,7 +49,7 @@ var DrawerOverlay = React.forwardRef((_a, ref) => {
39
49
  DrawerOverlay.displayName = Drawer$1.Overlay.displayName;
40
50
  var DrawerContent = React.forwardRef((_a, ref) => {
41
51
  var _b = _a, { className, variant = "flush", showHandle, overlayClassName, children } = _b, props = __objRest(_b, ["className", "variant", "showHandle", "overlayClassName", "children"]);
42
- const direction = React.useContext(DrawerDirectionContext);
52
+ const { direction, modal } = React.useContext(DrawerContext);
43
53
  const isVertical = direction === "top" || direction === "bottom";
44
54
  const handleEnabled = showHandle != null ? showHandle : direction === "bottom";
45
55
  const positionClasses = direction === "bottom" ? "inset-x-0 bottom-0 flex-col rounded-t-2xl border-t [padding-bottom:max(env(safe-area-inset-bottom),0px)]" : direction === "top" ? "inset-x-0 top-0 flex-col rounded-b-2xl border-b [padding-top:max(env(safe-area-inset-top),0px)]" : direction === "left" ? "inset-y-0 left-0 h-full w-3/4 max-w-sm flex-col rounded-r-2xl border-r [padding-left:max(env(safe-area-inset-left),0px)]" : "inset-y-0 right-0 h-full w-3/4 max-w-sm flex-col rounded-l-2xl border-l [padding-right:max(env(safe-area-inset-right),0px)]";
@@ -50,6 +60,7 @@ var DrawerContent = React.forwardRef((_a, ref) => {
50
60
  __spreadProps(__spreadValues({
51
61
  ref,
52
62
  "data-slot": "drawer-content",
63
+ "aria-modal": modal ? true : void 0,
53
64
  className: cn(
54
65
  "fixed z-[1000] flex border-border bg-card shadow-[var(--shadow-overlay)]",
55
66
  positionClasses,
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "secondary" | "default" | "success" | "warning" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
7
+ variant?: "default" | "secondary" | "success" | "warning" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
8
8
  size?: "status" | "chip" | null | undefined;
9
9
  live?: boolean | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -3,8 +3,8 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "secondary" | "default" | "link" | "warning" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
- size?: "default" | "icon" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ variant?: "default" | "link" | "secondary" | "warning" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
+ size?: "icon" | "default" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -28,7 +28,7 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
28
28
  ref?: React.Ref<HTMLInputElement>;
29
29
  } & {
30
30
  asChild?: boolean;
31
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "value" | "type"> & {
31
+ }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "value" | "onChange" | "type"> & {
32
32
  value?: string;
33
33
  onValueChange?: (search: string) => void;
34
34
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -64,7 +64,7 @@ declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
64
64
  ref?: React.Ref<HTMLDivElement>;
65
65
  } & {
66
66
  asChild?: boolean;
67
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "heading" | "value"> & {
67
+ }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "heading"> & {
68
68
  heading?: React.ReactNode;
69
69
  value?: string;
70
70
  forceMount?: boolean;
@@ -82,7 +82,7 @@ declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
82
82
  ref?: React.Ref<HTMLDivElement>;
83
83
  } & {
84
84
  asChild?: boolean;
85
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "value" | "disabled"> & {
85
+ }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "onSelect" | "disabled"> & {
86
86
  disabled?: boolean;
87
87
  onSelect?: (value: string) => void;
88
88
  value?: string;
@@ -4,8 +4,15 @@ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as React from 'react';
6
6
 
7
+ /**
8
+ * `autoFocus` defaults to `true` here, where vaul defaults it to `false`.
9
+ * vaul's default cancels Radix's open auto-focus, so focus never enters the
10
+ * sheet, Radix's FocusScope never engages, and Tab walks the page behind a
11
+ * modal drawer. A caller that must not move focus on open passes
12
+ * `autoFocus={false}`.
13
+ */
7
14
  declare const Drawer: {
8
- ({ shouldScaleBackground, direction, ...props }: React.ComponentProps<typeof Drawer$1.Root>): react_jsx_runtime.JSX.Element;
15
+ ({ shouldScaleBackground, direction, modal, autoFocus, ...props }: React.ComponentProps<typeof Drawer$1.Root>): react_jsx_runtime.JSX.Element;
9
16
  displayName: string;
10
17
  };
11
18
  declare const DrawerTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
@@ -1,3 +1,3 @@
1
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from '../chunk-XLHMF6F5.js';
1
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from '../chunk-NF5UMLSL.js';
2
2
  import '../chunk-FEK5XGZW.js';
3
3
  import '../chunk-MMUBH76A.js';
@@ -4,6 +4,7 @@ type EmptyStateAction = {
4
4
  label: string;
5
5
  onClick: () => void;
6
6
  };
7
+ type EmptyStateVariant = "default" | "search" | "error";
7
8
  interface EmptyStateProps {
8
9
  title: string;
9
10
  description: string;
@@ -11,10 +12,34 @@ interface EmptyStateProps {
11
12
  illustration?: React.ReactNode;
12
13
  action?: EmptyStateAction;
13
14
  secondaryAction?: EmptyStateAction;
14
- variant?: "default" | "search" | "error";
15
+ /**
16
+ * The primary action's style. Defaults by variant: `outline` for `error`
17
+ * (try again) and `search` (clear filters) — a recovery step, not the page's
18
+ * main call to action — and a filled `default` for the plain empty state,
19
+ * where the action is usually "create the first one".
20
+ */
21
+ actionVariant?: "default" | "outline";
22
+ variant?: EmptyStateVariant;
15
23
  size?: "sm" | "md" | "lg";
24
+ /**
25
+ * `card` draws the booking pages' whole-body state card (reports/02-p4,
26
+ * calendar/02-c2): a 16px-radius card surface with the resting card shadow.
27
+ * `none` (the default) leaves the surface to the container it sits in.
28
+ */
29
+ surface?: "none" | "card";
16
30
  className?: string;
17
31
  }
32
+ /**
33
+ * EmptyState — an icon tile, a title, one line of body and an optional action,
34
+ * spaced as the booking frames draw them (reports/02-p4, calendar/02-c2): a
35
+ * 64px tile (72px from `sm`) with a 28px glyph, 16px to the title, 12px from
36
+ * title to body, 16px from body to the action. The action is full-width on a
37
+ * phone and hugs its label from `sm`.
38
+ *
39
+ * The error variant says "error" with its glyph and its tile's wash, not by
40
+ * painting the title and body red: red body copy is both harder to read and
41
+ * louder than a load failure the user can retry.
42
+ */
18
43
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
19
44
  declare const SearchEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
20
45
  declare const ErrorEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
@@ -5,7 +5,7 @@ import { Text } from '../chunk-ZLMRVBD7.js';
5
5
  import { cn } from '../chunk-FEK5XGZW.js';
6
6
  import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
7
7
  import * as React from 'react';
8
- import { FileX, AlertCircle, Search } from 'lucide-react';
8
+ import { AlertCircle, Search, FileX } from 'lucide-react';
9
9
  import { jsxs, jsx } from 'react/jsx-runtime';
10
10
 
11
11
  var sizeStyles = {
@@ -13,6 +13,16 @@ var sizeStyles = {
13
13
  md: "py-12 px-6",
14
14
  lg: "py-16 px-8"
15
15
  };
16
+ var DEFAULT_ACTION_VARIANT = {
17
+ default: "default",
18
+ search: "outline",
19
+ error: "outline"
20
+ };
21
+ var DEFAULT_ICON = {
22
+ default: /* @__PURE__ */ jsx(FileX, { className: "text-muted-foreground", "aria-hidden": true }),
23
+ search: /* @__PURE__ */ jsx(Search, { className: "text-muted-foreground", "aria-hidden": true }),
24
+ error: /* @__PURE__ */ jsx(AlertCircle, { className: "text-destructive", "aria-hidden": true })
25
+ };
16
26
  var EmptyState = React.forwardRef(
17
27
  (_a, ref) => {
18
28
  var _b = _a, {
@@ -22,8 +32,10 @@ var EmptyState = React.forwardRef(
22
32
  illustration,
23
33
  action,
24
34
  secondaryAction,
35
+ actionVariant,
25
36
  variant = "default",
26
37
  size = "md",
38
+ surface = "none",
27
39
  className
28
40
  } = _b, props = __objRest(_b, [
29
41
  "title",
@@ -32,21 +44,12 @@ var EmptyState = React.forwardRef(
32
44
  "illustration",
33
45
  "action",
34
46
  "secondaryAction",
47
+ "actionVariant",
35
48
  "variant",
36
49
  "size",
50
+ "surface",
37
51
  "className"
38
52
  ]);
39
- const getDefaultIcon = () => {
40
- switch (variant) {
41
- case "search":
42
- return /* @__PURE__ */ jsx(Search, { className: "h-12 w-12 text-muted-foreground/50" });
43
- case "error":
44
- return /* @__PURE__ */ jsx(AlertCircle, { className: "h-12 w-12 text-destructive/50" });
45
- default:
46
- return /* @__PURE__ */ jsx(FileX, { className: "h-12 w-12 text-muted-foreground/50" });
47
- }
48
- };
49
- const textColor = variant === "error" ? "destructive" : "muted";
50
53
  return /* @__PURE__ */ jsxs(
51
54
  "div",
52
55
  __spreadProps(__spreadValues({
@@ -55,15 +58,36 @@ var EmptyState = React.forwardRef(
55
58
  className: cn(
56
59
  "flex flex-col items-center justify-center text-center",
57
60
  sizeStyles[size],
61
+ surface === "card" && "rounded-2xl border border-border bg-card shadow-[var(--shadow-card)]",
58
62
  className
59
63
  )
60
64
  }, props), {
61
65
  children: [
62
- illustration ? /* @__PURE__ */ jsx("div", { className: "mb-6", children: illustration }) : /* @__PURE__ */ jsx("div", { className: "mb-4", children: icon || getDefaultIcon() }),
63
- /* @__PURE__ */ jsx(Text, { as: "h3", weight: "semibold", color: textColor, className: "text-lg mb-2", children: title }),
64
- /* @__PURE__ */ jsx(Text, { as: "p", color: textColor, className: "text-sm max-w-md mb-6", children: description }),
65
- (action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
66
- action && /* @__PURE__ */ jsx(Button, { onClick: action.onClick, variant: "default", className: "glass-hover", children: action.label }),
66
+ illustration ? /* @__PURE__ */ jsx("div", { className: "mb-6", children: illustration }) : /* @__PURE__ */ jsx(
67
+ "span",
68
+ {
69
+ "data-slot": "empty-state-icon",
70
+ className: cn(
71
+ // The glyph size is the tile's, whatever size the caller's icon
72
+ // node asked for — a 40px icon in a 64px tile has no margin.
73
+ "mb-4 flex size-16 shrink-0 items-center justify-center rounded-2xl border border-border shadow-sm sm:size-18 [&_svg]:size-7",
74
+ variant === "error" ? "bg-destructive/5" : "bg-card"
75
+ ),
76
+ children: icon != null ? icon : DEFAULT_ICON[variant]
77
+ }
78
+ ),
79
+ /* @__PURE__ */ jsx(Text, { as: "h3", weight: "semibold", className: "text-lg", children: title }),
80
+ description ? /* @__PURE__ */ jsx(Text, { as: "p", color: "muted", className: "mt-3 max-w-md text-sm", children: description }) : null,
81
+ (action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:items-center", children: [
82
+ action && /* @__PURE__ */ jsx(
83
+ Button,
84
+ {
85
+ onClick: action.onClick,
86
+ variant: actionVariant != null ? actionVariant : DEFAULT_ACTION_VARIANT[variant],
87
+ className: "glass-hover",
88
+ children: action.label
89
+ }
90
+ ),
67
91
  secondaryAction && /* @__PURE__ */ jsx(Button, { onClick: secondaryAction.onClick, variant: "outline", className: "glass-hover", children: secondaryAction.label })
68
92
  ] })
69
93
  ]
@@ -23,7 +23,9 @@ import * as React from 'react';
23
23
  * - Keyboard, with the viewport focused: arrows pan (Shift for four times the
24
24
  * step); `+` / `-` zoom and `0` fits, from anywhere inside it. Tabbing onto
25
25
  * a table that sits off screen pans it into view.
26
- * - The + / − / Fit controls, bottom right.
26
+ * - The + / − / Fit controls: a raised card 12px in from the bottom-right
27
+ * corner, 44 × 44 per button. Fit and the pan bounds keep the content clear
28
+ * of them.
27
29
  *
28
30
  * The min-scale floor: `Fit` never shrinks below `minScale`. On a phone the
29
31
  * plan would otherwise fit at a size no finger can hit, so below the floor it
@@ -96,8 +98,18 @@ type PanZoomViewportProps = Omit<React.HTMLAttributes<HTMLDivElement>, "children
96
98
  zoomStep?: number;
97
99
  /** One arrow press, in viewport px. Default 48. */
98
100
  panStep?: number;
99
- /** The air Fit leaves around the content, and how far past it a pan may go. Default 24. */
101
+ /**
102
+ * The air Fit leaves around the content, and how far past it a pan may go.
103
+ * Default 24. With `controls` on, the side the control stack would cover
104
+ * (right or bottom, whichever costs the fit less) is widened to clear it.
105
+ */
100
106
  fitPadding?: number;
107
+ /**
108
+ * Where fitted content sits vertically when it is shorter than the
109
+ * viewport: `center` (default), or `top` — pinned under the top padding,
110
+ * as a phone shows a plan.
111
+ */
112
+ fitAlign?: "center" | "top";
101
113
  /** Render the + / − / Fit controls. Default true. */
102
114
  controls?: boolean;
103
115
  /** Called with every transform — gestures included, so keep it cheap. */
@@ -122,8 +134,18 @@ declare const PanZoomViewport: React.ForwardRefExoticComponent<Omit<React.HTMLAt
122
134
  zoomStep?: number;
123
135
  /** One arrow press, in viewport px. Default 48. */
124
136
  panStep?: number;
125
- /** The air Fit leaves around the content, and how far past it a pan may go. Default 24. */
137
+ /**
138
+ * The air Fit leaves around the content, and how far past it a pan may go.
139
+ * Default 24. With `controls` on, the side the control stack would cover
140
+ * (right or bottom, whichever costs the fit less) is widened to clear it.
141
+ */
126
142
  fitPadding?: number;
143
+ /**
144
+ * Where fitted content sits vertically when it is shorter than the
145
+ * viewport: `center` (default), or `top` — pinned under the top padding,
146
+ * as a phone shows a plan.
147
+ */
148
+ fitAlign?: "center" | "top";
127
149
  /** Render the + / − / Fit controls. Default true. */
128
150
  controls?: boolean;
129
151
  /** Called with every transform — gestures included, so keep it cheap. */
@@ -12,12 +12,21 @@ var DRAG_THRESHOLD = 4;
12
12
  var MAX_WHEEL_ZOOM_DELTA = 25;
13
13
  var WHEEL_ZOOM_SPEED = 0.01;
14
14
  var EPSILON = 1e-3;
15
+ var CONTROLS_RESERVE_X = 12 + 44 + 8;
16
+ var CONTROLS_RESERVE_Y = 12 + 130 + 8;
15
17
  function clamp(value, min, max) {
16
18
  return Math.min(max, Math.max(min, value));
17
19
  }
18
- function clampAxis(offset, content, viewport, pad) {
19
- if (content + pad * 2 <= viewport) return (viewport - content) / 2;
20
- return clamp(offset, viewport - content - pad, pad);
20
+ function clampAxis(offset, content, viewport, start, end, align) {
21
+ const room = viewport - start - end;
22
+ if (content <= room) return align === "start" ? start : start + (room - content) / 2;
23
+ return clamp(offset, viewport - content - end, start);
24
+ }
25
+ function fitScale(viewport, insets, contentWidth, contentHeight) {
26
+ return Math.min(
27
+ (viewport.width - insets.left - insets.right) / contentWidth,
28
+ (viewport.height - insets.top - insets.bottom) / contentHeight
29
+ );
21
30
  }
22
31
  function distance(a, b) {
23
32
  return Math.hypot(a.x - b.x, a.y - b.y);
@@ -40,6 +49,7 @@ var PanZoomViewport = React.forwardRef(
40
49
  zoomStep = 1.25,
41
50
  panStep = 48,
42
51
  fitPadding = 24,
52
+ fitAlign = "center",
43
53
  controls = true,
44
54
  onTransformChange,
45
55
  apiRef,
@@ -55,6 +65,7 @@ var PanZoomViewport = React.forwardRef(
55
65
  "zoomStep",
56
66
  "panStep",
57
67
  "fitPadding",
68
+ "fitAlign",
58
69
  "controls",
59
70
  "onTransformChange",
60
71
  "apiRef",
@@ -79,26 +90,37 @@ var PanZoomViewport = React.forwardRef(
79
90
  onTransformChangeRef.current = onTransformChange;
80
91
  zoomLevelRef.current = labels.zoomLevel;
81
92
  });
93
+ const insets = React.useMemo(() => {
94
+ const base = { top: fitPadding, right: fitPadding, bottom: fitPadding, left: fitPadding };
95
+ if (!controls || !viewport) return base;
96
+ const right = __spreadProps(__spreadValues({}, base), { right: Math.max(fitPadding, CONTROLS_RESERVE_X) });
97
+ const bottom = __spreadProps(__spreadValues({}, base), { bottom: Math.max(fitPadding, CONTROLS_RESERVE_Y) });
98
+ return fitScale(viewport, bottom, contentWidth, contentHeight) > fitScale(viewport, right, contentWidth, contentHeight) ? bottom : right;
99
+ }, [controls, viewport, fitPadding, contentWidth, contentHeight]);
82
100
  const clampTransform = React.useCallback(
83
101
  (next) => {
84
102
  if (!viewport || viewport.width <= 0 || viewport.height <= 0) return next;
85
103
  const scale = clamp(next.scale, minScale, maxScale);
86
104
  return {
87
105
  scale,
88
- x: clampAxis(next.x, contentWidth * scale, viewport.width, fitPadding),
89
- y: clampAxis(next.y, contentHeight * scale, viewport.height, fitPadding)
106
+ x: clampAxis(next.x, contentWidth * scale, viewport.width, insets.left, insets.right, "center"),
107
+ y: clampAxis(
108
+ next.y,
109
+ contentHeight * scale,
110
+ viewport.height,
111
+ insets.top,
112
+ insets.bottom,
113
+ fitAlign === "top" ? "start" : "center"
114
+ )
90
115
  };
91
116
  },
92
- [viewport, minScale, maxScale, contentWidth, contentHeight, fitPadding]
117
+ [viewport, minScale, maxScale, contentWidth, contentHeight, insets, fitAlign]
93
118
  );
94
119
  const fitted = React.useCallback(() => {
95
120
  if (!viewport || viewport.width <= 0 || viewport.height <= 0) return null;
96
- const scale = Math.min(
97
- (viewport.width - fitPadding * 2) / contentWidth,
98
- (viewport.height - fitPadding * 2) / contentHeight
99
- );
100
- return clampTransform({ scale, x: fitPadding, y: fitPadding });
101
- }, [viewport, fitPadding, contentWidth, contentHeight, clampTransform]);
121
+ const scale = fitScale(viewport, insets, contentWidth, contentHeight);
122
+ return clampTransform({ scale, x: insets.left, y: insets.top });
123
+ }, [viewport, insets, contentWidth, contentHeight, clampTransform]);
102
124
  const commit = React.useCallback(
103
125
  (next, options) => {
104
126
  var _a2;
@@ -382,13 +404,13 @@ var PanZoomViewport = React.forwardRef(
382
404
  if (pressing.current || event.target === event.currentTarget) return;
383
405
  const bounds = event.currentTarget.getBoundingClientRect();
384
406
  const rect = event.target.getBoundingClientRect();
385
- const margin = fitPadding;
386
407
  let dx = 0;
387
408
  let dy = 0;
388
- if (rect.left < bounds.left + margin) dx = bounds.left + margin - rect.left;
389
- else if (rect.right > bounds.right - margin) dx = bounds.right - margin - rect.right;
390
- if (rect.top < bounds.top + margin) dy = bounds.top + margin - rect.top;
391
- else if (rect.bottom > bounds.bottom - margin) dy = bounds.bottom - margin - rect.bottom;
409
+ if (rect.left < bounds.left + insets.left) dx = bounds.left + insets.left - rect.left;
410
+ else if (rect.right > bounds.right - insets.right) dx = bounds.right - insets.right - rect.right;
411
+ if (rect.top < bounds.top + insets.top) dy = bounds.top + insets.top - rect.top;
412
+ else if (rect.bottom > bounds.bottom - insets.bottom)
413
+ dy = bounds.bottom - insets.bottom - rect.bottom;
392
414
  if (dx !== 0 || dy !== 0) panBy(dx, dy);
393
415
  };
394
416
  const scaledWidth = contentWidth * transform.scale;
@@ -479,14 +501,14 @@ var PanZoomViewport = React.forwardRef(
479
501
  orientation: "vertical",
480
502
  "aria-label": labels.controls,
481
503
  "data-slot": "pan-zoom-viewport-controls",
482
- className: "absolute bottom-3 right-3 rounded-xl bg-card shadow-sm",
504
+ className: "absolute bottom-3 right-3 rounded-xl bg-card shadow-[var(--shadow-card)]",
483
505
  children: [
484
506
  /* @__PURE__ */ jsx(
485
507
  Button,
486
508
  {
487
509
  type: "button",
488
510
  variant: "outline",
489
- size: "icon-sm",
511
+ size: "icon",
490
512
  "aria-label": labels.zoomIn,
491
513
  disabled: atMax,
492
514
  onClick: zoomIn,
@@ -498,7 +520,7 @@ var PanZoomViewport = React.forwardRef(
498
520
  {
499
521
  type: "button",
500
522
  variant: "outline",
501
- size: "icon-sm",
523
+ size: "icon",
502
524
  "aria-label": labels.zoomOut,
503
525
  disabled: atMin,
504
526
  onClick: zoomOut,
@@ -510,7 +532,7 @@ var PanZoomViewport = React.forwardRef(
510
532
  {
511
533
  type: "button",
512
534
  variant: "outline",
513
- size: "icon-sm",
535
+ size: "icon",
514
536
  className: "text-xs font-semibold",
515
537
  onClick: fit,
516
538
  children: labels.fit
@@ -1,6 +1,6 @@
1
1
  import { useMediaQuery, DESKTOP_MEDIA_QUERY } from '../chunk-OPVKL4VS.js';
2
2
  import { Sheet, SheetContent, SheetTitle, SheetDescription } from '../chunk-LAEVMNJN.js';
3
- import { Drawer, DrawerContent, DrawerTitle, DrawerDescription } from '../chunk-XLHMF6F5.js';
3
+ import { Drawer, DrawerContent, DrawerTitle, DrawerDescription } from '../chunk-NF5UMLSL.js';
4
4
  import { Dialog, DialogContent, DialogTitle, DialogDescription } from '../chunk-IQIJCMG2.js';
5
5
  import { cn } from '../chunk-FEK5XGZW.js';
6
6
  import { __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
@@ -59,15 +59,14 @@ var ResponsiveOverlay = React.forwardRef(
59
59
  }, ref) => {
60
60
  const isWide = useMediaQuery(wideFrom === "lg" ? DESKTOP_MEDIA_QUERY : MD_MEDIA_QUERY);
61
61
  const container = isWide ? desktop : "drawer";
62
- const titleId = React.useId();
63
- const subtitleId = React.useId();
64
62
  useFocusReturn(open);
63
+ const describedBy = subtitle ? {} : { "aria-describedby": void 0 };
65
64
  const Title = CONTAINER_TITLE[container];
66
65
  const Description = CONTAINER_DESCRIPTION[container];
67
66
  const header = /* @__PURE__ */ jsxs("div", { "data-slot": "responsive-overlay-header", className: "flex items-start gap-3 px-5 pt-5", children: [
68
67
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
69
- /* @__PURE__ */ jsx(Title, { id: titleId, className: "text-[19px] font-bold leading-tight", children: title }),
70
- subtitle ? /* @__PURE__ */ jsx(Description, { id: subtitleId, className: "mt-1 text-[13px] text-muted-foreground", children: subtitle }) : null
68
+ /* @__PURE__ */ jsx(Title, { className: "text-[19px] font-bold leading-tight", children: title }),
69
+ subtitle ? /* @__PURE__ */ jsx(Description, { className: "mt-1 text-[13px] text-muted-foreground", children: subtitle }) : null
71
70
  ] }),
72
71
  /* @__PURE__ */ jsxs(
73
72
  "button",
@@ -102,17 +101,16 @@ var ResponsiveOverlay = React.forwardRef(
102
101
  if (container === "sheet") {
103
102
  return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, modal, children: /* @__PURE__ */ jsxs(
104
103
  SheetContent,
105
- __spreadProps(__spreadValues({
104
+ __spreadProps(__spreadValues(__spreadProps(__spreadValues({
106
105
  ref,
107
106
  side: "right",
108
107
  attachment: "inset",
109
108
  hideClose: true,
110
- overlayClassName: "bg-scrim-overlay backdrop-blur-none",
111
- "aria-labelledby": titleId,
112
- "aria-describedby": subtitle ? subtitleId : void 0,
109
+ overlayClassName: "bg-scrim-overlay backdrop-blur-none"
110
+ }, describedBy), {
113
111
  "data-slot": "responsive-overlay-sheet",
114
112
  "data-modal": modal ? "true" : "false"
115
- }, modal ? {} : { onInteractOutside: (event) => event.preventDefault() }), {
113
+ }), modal ? {} : { onInteractOutside: (event) => event.preventDefault() }), {
116
114
  className: cn(
117
115
  "flex w-[420px] max-w-[calc(100vw-1.5rem)] flex-col gap-0 bg-card p-0",
118
116
  className
@@ -128,13 +126,12 @@ var ResponsiveOverlay = React.forwardRef(
128
126
  if (container === "dialog") {
129
127
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
130
128
  DialogContent,
131
- {
129
+ __spreadProps(__spreadValues({
132
130
  ref,
133
131
  size,
134
132
  showCloseButton: false,
135
- overlayClassName: "bg-scrim-overlay backdrop-blur-none",
136
- "aria-labelledby": titleId,
137
- "aria-describedby": subtitle ? subtitleId : void 0,
133
+ overlayClassName: "bg-scrim-overlay backdrop-blur-none"
134
+ }, describedBy), {
138
135
  className: cn(
139
136
  // The dialog's own `grid gap-4 p-6` would put air between the
140
137
  // header, the body and the band the frames draw flush.
@@ -146,23 +143,22 @@ var ResponsiveOverlay = React.forwardRef(
146
143
  body,
147
144
  foot
148
145
  ]
149
- }
146
+ })
150
147
  ) });
151
148
  }
152
149
  return /* @__PURE__ */ jsx(Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
153
150
  DrawerContent,
154
- {
151
+ __spreadProps(__spreadValues({
155
152
  ref,
156
- overlayClassName: "bg-scrim-overlay",
157
- "aria-labelledby": titleId,
158
- "aria-describedby": subtitle ? subtitleId : void 0,
153
+ overlayClassName: "bg-scrim-overlay"
154
+ }, describedBy), {
159
155
  className: cn("flex max-h-[85vh] flex-col p-0", className),
160
156
  children: [
161
157
  header,
162
158
  body,
163
159
  foot
164
160
  ]
165
- }
161
+ })
166
162
  ) });
167
163
  }
168
164
  );
@@ -4,11 +4,11 @@ import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const spinnerVariants: (props?: ({
6
6
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
7
- tone?: "primary" | "default" | "inherit" | "destructive" | null | undefined;
7
+ tone?: "default" | "inherit" | "primary" | "destructive" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Spinner: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
10
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
11
- tone?: "primary" | "default" | "inherit" | "destructive" | null | undefined;
11
+ tone?: "default" | "inherit" | "primary" | "destructive" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  /** Visually-hidden label for screen readers. Defaults to "Loading". */
14
14
  label?: string;
@@ -3,11 +3,11 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const tagVariants: (props?: ({
6
- variant?: "primary" | "default" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
6
+ variant?: "default" | "primary" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Tag: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
- variant?: "primary" | "default" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
10
+ variant?: "default" | "primary" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
11
11
  size?: "default" | "sm" | "lg" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  onRemove?: () => void;
@@ -22,8 +22,8 @@ import { VariantProps } from 'class-variance-authority';
22
22
  * Weights are 400 / 600 / 700. 500 is not in the system.
23
23
  */
24
24
  declare const textVariants: (props?: ({
25
- variant?: "small" | "body" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
- color?: "primary" | "default" | "inherit" | "destructive" | "muted" | "primary-foreground" | null | undefined;
25
+ variant?: "body" | "small" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
+ color?: "default" | "inherit" | "primary" | "destructive" | "muted" | "primary-foreground" | null | undefined;
27
27
  weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
28
28
  leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
29
29
  tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bearmenu/ui",
3
- "version": "0.8.37",
3
+ "version": "0.8.39",
4
4
  "description": "BearMenu design system — shared UI tokens, primitives, and components",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {