@medalsocial/meda 3.13.0 → 3.15.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/README.md CHANGED
@@ -66,7 +66,8 @@ per-feature stylesheets never reach an optional peer (see
66
66
 
67
67
  Unbranded, presentational screens for a customer booking flow: the wizard steps
68
68
  (`WhoScreen`, `ServiceScreen`, `StylistScreen`, `TimeScreen`, `DetailsScreen`,
69
- `SummaryBar`, `Confirmation`, `BookingSkeleton`, `AddChildSheet`), `ManageScreen`,
69
+ `SummaryBar`, `Confirmation`, `BookingSkeleton`, `AddChildSheet`), the desktop's
70
+ sticky side panel (`BookingPanel`), `ManageScreen`,
70
71
  login (`LoginSheet`, `LoginPanel`, `OtpSlots`, `VippsButton`) and the customer
71
72
  portal (`PortalShell`, `UpcomingBookings`, `VisitHistory`, `ChildCards`,
72
73
  `RebookCards`, `FamilyEditor`, `ProfileForm`, `DataControls`, `AccountCard`,
@@ -30,9 +30,10 @@ export declare function PersonTabs({ people, active, done, onSelect, labels, lab
30
30
  * missing, and «Next». The same bar as `SummaryBar`, so the step reads as one
31
31
  * flow; a caller that also renders `SummaryBar` hides it on this step.
32
32
  *
33
- * «Next» is never disabled. A disabled button explains nothing and is skipped
34
- * by a screen reader's tab order; this one stays, the line says what is
35
- * missing, and the caller decides what pressing it does.
33
+ * «Next» is not disabled by default. A disabled button explains nothing and
34
+ * is skipped by a screen reader's tab order; this one stays, the line says
35
+ * what is missing, and the caller decides what pressing it does. A site that
36
+ * wants it held back anyway opts in with `selection.disableUntilReady`.
36
37
  */
37
38
  export declare function TotalBar({ selection, format, classNames, }: {
38
39
  selection: ServiceSelection;
@@ -53,16 +53,20 @@ export function PersonTabs({ people, active, done, onSelect, labels, labelledBy,
53
53
  * missing, and «Next». The same bar as `SummaryBar`, so the step reads as one
54
54
  * flow; a caller that also renders `SummaryBar` hides it on this step.
55
55
  *
56
- * «Next» is never disabled. A disabled button explains nothing and is skipped
57
- * by a screen reader's tab order; this one stays, the line says what is
58
- * missing, and the caller decides what pressing it does.
56
+ * «Next» is not disabled by default. A disabled button explains nothing and
57
+ * is skipped by a screen reader's tab order; this one stays, the line says
58
+ * what is missing, and the caller decides what pressing it does. A site that
59
+ * wants it held back anyway opts in with `selection.disableUntilReady`.
59
60
  */
60
61
  export function TotalBar({ selection, format, classNames, }) {
61
62
  const { total, canContinue, labels } = selection;
63
+ const held = selection.disableUntilReady === true && !canContinue;
62
64
  return (_jsxs("div", { className: slotClass(classNames, 'bar', 'sticky bottom-0 z-40 border-t border-border bg-background/95 backdrop-blur-md'), children: [_jsx(LiveStatus, { text: selection.notice ?? null, className: "px-5 text-sm font-medium text-foreground [&:not(:empty)]:pt-3" }), _jsxs("div", { className: "flex h-16 items-center justify-between gap-4 px-5", children: [_jsxs("div", { "aria-live": "polite", "aria-atomic": "true", className: "min-w-0 text-sm tabular-nums", children: [total !== null && (_jsx("p", { className: "truncate font-semibold", children: renderLabel(labels['service.total'], {
63
65
  minutes: total.minutes,
64
66
  price: format.price(total.priceOre),
65
- }) })), !canContinue && (_jsx("p", { className: "truncate text-muted-foreground", children: renderLabel(labels['service.chooseFirst']) }))] }), _jsx(BookingButton, { size: "lg", onClick: selection.onContinue, children: renderLabel(labels['service.continue']) })] })] }));
67
+ }) })), !canContinue && (_jsx("p", { className: "truncate text-muted-foreground", children: renderLabel(labels['service.chooseFirst']) }))] }), _jsx(BookingButton, { size: "lg", onClick: selection.onContinue, disabled: held, "aria-disabled": held || undefined, "data-disabled": held ? '' : undefined, className: cn(
68
+ // Material 3's disabled button: 38 % content on a 12 % container.
69
+ held && 'disabled:bg-foreground/12 disabled:text-foreground/38 disabled:opacity-100', classNames?.next), children: renderLabel(labels['service.continue']) })] })] }));
66
70
  }
67
71
  /**
68
72
  * A service card on the multi-select step: a real checkbox inside its label.
@@ -11,18 +11,26 @@ import type { ComponentProps } from 'react';
11
11
  */
12
12
  export type BookingButtonVariant = 'default' | 'outline' | 'secondary' | 'ghost' | 'destructive' | 'link';
13
13
  export type BookingButtonSize = 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm';
14
+ /**
15
+ * The corners: `default` is booking's near-square look (each size's own);
16
+ * `full` a pill, for a site whose buttons are round.
17
+ */
18
+ export type BookingButtonRadius = 'default' | 'full';
14
19
  /** Classes for a booking button — also for a link that should look like one. */
15
- export declare function bookingButtonClass({ variant, size, className, }?: {
20
+ export declare function bookingButtonClass({ variant, size, radius, className, }?: {
16
21
  variant?: BookingButtonVariant;
17
22
  size?: BookingButtonSize;
23
+ radius?: BookingButtonRadius;
18
24
  className?: string;
19
25
  }): string;
20
26
  export interface BookingButtonProps extends ComponentProps<'button'> {
21
27
  variant?: BookingButtonVariant;
22
28
  size?: BookingButtonSize;
29
+ /** `full` for pill-shaped corners. Default: booking's near-square ones. */
30
+ radius?: BookingButtonRadius;
23
31
  }
24
32
  /** The booking screens' button. A native `<button type="button">` unless told otherwise. */
25
- export declare function BookingButton({ variant, size, className, type, ...props }: BookingButtonProps): import("react").JSX.Element;
33
+ export declare function BookingButton({ variant, size, radius, className, type, ...props }: BookingButtonProps): import("react").JSX.Element;
26
34
  /** Override for meda `Input` in booking forms. */
27
35
  export declare const BOOKING_INPUT_CLASS = "h-12 rounded-[4px] border-[1.5px] border-input bg-background px-4 py-3 text-base transition-[color,border-color] focus-visible:border-primary focus-visible:ring-0 aria-invalid:border-destructive sm:text-base md:text-base dark:aria-invalid:border-destructive/50";
28
36
  /** Override for meda `Textarea` in booking forms. */
@@ -18,12 +18,12 @@ const BUTTON_SIZES = {
18
18
  'icon-sm': 'size-8 rounded-[min(var(--radius-md),10px)]',
19
19
  };
20
20
  /** Classes for a booking button — also for a link that should look like one. */
21
- export function bookingButtonClass({ variant = 'default', size = 'default', className, } = {}) {
22
- return cn(BUTTON_BASE, BUTTON_VARIANTS[variant], BUTTON_SIZES[size], className);
21
+ export function bookingButtonClass({ variant = 'default', size = 'default', radius = 'default', className, } = {}) {
22
+ return cn(BUTTON_BASE, BUTTON_VARIANTS[variant], BUTTON_SIZES[size], radius === 'full' && 'rounded-full', className);
23
23
  }
24
24
  /** The booking screens' button. A native `<button type="button">` unless told otherwise. */
25
- export function BookingButton({ variant, size, className, type = 'button', ...props }) {
26
- return (_jsx("button", { type: type, "data-slot": "button", className: bookingButtonClass({ variant, size, className }), ...props }));
25
+ export function BookingButton({ variant, size, radius, className, type = 'button', ...props }) {
26
+ return (_jsx("button", { type: type, "data-slot": "button", className: bookingButtonClass({ variant, size, radius, className }), ...props }));
27
27
  }
28
28
  /** Override for meda `Input` in booking forms. */
29
29
  export const BOOKING_INPUT_CLASS = 'h-12 rounded-[4px] border-[1.5px] border-input bg-background px-4 py-3 text-base transition-[color,border-color] focus-visible:border-primary focus-visible:ring-0 aria-invalid:border-destructive sm:text-base md:text-base dark:aria-invalid:border-destructive/50';
@@ -51,8 +51,10 @@ export type ServiceScreenLabels = Record<(typeof SERVICE_SCREEN_LABEL_KEYS)[numb
51
51
  * - `price` — a card's price (passed to `ServiceCard`, and the «same as last time» card's)
52
52
  * - `tab` / `tabSelected` — a person's tab in a multi-select family / the open one
53
53
  * - `bar` — the multi-select step's sticky total bar
54
+ * - `next` — the bar's «Next» button (`data-disabled` while
55
+ * `selection.disableUntilReady` holds it back, for a site's own disabled look)
54
56
  */
55
- export type ServiceScreenSlot = 'tab' | 'tabSelected' | 'bar' | 'root' | 'heading' | 'pill' | 'pillSelected' | 'groupHeading' | 'card' | 'cardSelected' | 'price';
57
+ export type ServiceScreenSlot = 'tab' | 'tabSelected' | 'bar' | 'next' | 'root' | 'heading' | 'pill' | 'pillSelected' | 'groupHeading' | 'card' | 'cardSelected' | 'price';
56
58
  /** One service group, in display order. */
57
59
  export interface ServiceScreenCategory {
58
60
  key: string;
@@ -127,10 +129,18 @@ export interface ServiceSelection {
127
129
  } | null;
128
130
  /**
129
131
  * Whether every person has at least one service. «Next» stays enabled
130
- * either way (a disabled button explains nothing); while `false` the bar
131
- * shows `service.chooseFirst` instead of the total.
132
+ * either way (a disabled button explains nothing) unless
133
+ * `disableUntilReady`; while `false` the bar shows `service.chooseFirst`
134
+ * under the total.
132
135
  */
133
136
  canContinue: boolean;
137
+ /**
138
+ * Opt-in: hold «Next» back — `disabled`, `aria-disabled` and
139
+ * `data-disabled` — while `canContinue` is `false`, drawn in the Material 3
140
+ * disabled look (38 % content on a 12 % container). The line under the
141
+ * total still says what is missing. Default: off, «Next» always enabled.
142
+ */
143
+ disableUntilReady?: boolean;
134
144
  /** A refusal to say politely (e.g. too many services), announced as a live region. */
135
145
  notice?: string | null;
136
146
  /**
@@ -106,13 +106,11 @@ export function ContextRail({ appId, module, hidden = false, collapsible = true,
106
106
  setDisplayWidth(null);
107
107
  ctx.contextRail.setWidth(w);
108
108
  };
109
- // Only animate width during collapse/expand toggles, not during manual
110
- // pointer drag on the ResizeHandle. displayWidth is non-null only while
111
- // the user is mid-drag — using it as the drag signal suppresses the
112
- // transition then so the rail snaps to the cursor instead of lagging
113
- // behind it.
114
- const isDragging = displayWidth !== null;
115
- return (_jsxs("aside", { id: railId, "data-testid": "context-rail", "aria-label": module.label, className: cn('group/rail relative h-full shrink-0 bg-shell-context', !isDragging && 'transition-[width] duration-200 ease-in-out motion-reduce:transition-none', collapsed && 'w-0', className), style: { width: collapsed ? 0 : width }, children: [collapsible && (_jsx(ContextRailSeam, { railId: railId, currentWidth: width, onResize: handleResize, onCommit: handleCommit })), _jsxs("div", { className: "flex h-full min-w-0 flex-col overflow-hidden", "aria-hidden": collapsed, inert: collapsed || undefined, children: [showHeader && (_jsxs("div", { className: "shrink-0 border-b border-shell-border px-4 py-3", children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: module.label }), module.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: module.description }))] })), _jsxs("div", { "data-meda-context-rail-scroll-area": "", className: cn('min-h-0 flex-1', scroll === 'auto' ? 'overflow-y-auto overflow-x-hidden' : 'overflow-hidden'), children: [items.length > 0 && (_jsx("nav", { "aria-label": `${module.label} navigation`, className: "flex flex-col gap-0.5 p-2", children: items.map((item) => {
109
+ // The rail never animates its width. Collapse/expand is mostly ⌘B, a
110
+ // keyboard action used many times a day, and a width transition re-wraps
111
+ // the whole main column on every frame. It snaps on the keyboard, on a
112
+ // click and while dragging.
113
+ return (_jsxs("aside", { id: railId, "data-testid": "context-rail", "aria-label": module.label, className: cn('group/rail relative h-full shrink-0 bg-shell-context', collapsed && 'w-0', className), style: { width: collapsed ? 0 : width }, children: [collapsible && (_jsx(ContextRailSeam, { railId: railId, currentWidth: width, onResize: handleResize, onCommit: handleCommit })), _jsxs("div", { className: "flex h-full min-w-0 flex-col overflow-hidden", "aria-hidden": collapsed, inert: collapsed || undefined, children: [showHeader && (_jsxs("div", { className: "shrink-0 border-b border-shell-border px-4 py-3", children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: module.label }), module.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: module.description }))] })), _jsxs("div", { "data-meda-context-rail-scroll-area": "", className: cn('min-h-0 flex-1', scroll === 'auto' ? 'overflow-y-auto overflow-x-hidden' : 'overflow-hidden'), children: [items.length > 0 && (_jsx("nav", { "aria-label": `${module.label} navigation`, className: "flex flex-col gap-0.5 p-2", children: items.map((item) => {
116
114
  const isActive = item.id === activeItemId;
117
115
  const klass = cn('flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors', isActive
118
116
  ? 'bg-primary/10 text-primary'
@@ -25,7 +25,8 @@ export { AppTabs, PanelToggle, ShellHeader, WorkspaceSwitcher } from './shell-he
25
25
  export { ShellMain } from './shell-main.js';
26
26
  export type { MedaShellProviderProps } from './shell-provider.js';
27
27
  export { MedaShellProvider, useMedaShell, useShellSelection } from './shell-provider.js';
28
+ export { ShellViewportHintProvider, type ShellViewportHintProviderProps, } from './shell-viewport-hint.js';
28
29
  export { DefaultThemeProvider, ThemeToggle, useTheme } from './theme.js';
29
30
  export { NextThemesAdapter } from './theme-next-themes.js';
30
31
  export type { AppDefinition, AppShellAppTabsConfig, AppShellAuthBranding, AppShellAuthConfig, AppShellContextRailConfig, AppShellIconRailConfig, AppShellMobileNavConfig, AppShellRightPanelConfig, AppShellVariant, AppShellWorkspaceConfig, AppTabRenderLinkArgs, CommandDefinition, ContextItem, ContextModule, ContextRailHeader, ContextRailScroll, MobileBottomNavItem, MobileDockItem, MobileNavGroup, MobileNavItem, MobileNavLinkArgs, MobileNavTree, MobileNavView, PanelMode, PanelView, ShellHeaderLayout, ShellLinkRenderArgs, ShellMainLayout, ShellRenderContext, ShellViewport, ThemeAdapter, WorkspaceDefinition, WorkspaceMenuItem, } from './types.js';
31
- export { useShellViewport } from './use-shell-viewport.js';
32
+ export { ShellViewportHintContext, useShellViewport } from './use-shell-viewport.js';
@@ -28,7 +28,8 @@ export { AppTabs, PanelToggle, ShellHeader, WorkspaceSwitcher } from './shell-he
28
28
  export { ShellMain } from './shell-main.js';
29
29
  // Provider + hooks
30
30
  export { MedaShellProvider, useMedaShell, useShellSelection } from './shell-provider.js';
31
+ export { ShellViewportHintProvider, } from './shell-viewport-hint.js';
31
32
  // Theme
32
33
  export { DefaultThemeProvider, ThemeToggle, useTheme } from './theme.js';
33
34
  export { NextThemesAdapter } from './theme-next-themes.js';
34
- export { useShellViewport } from './use-shell-viewport.js';
35
+ export { ShellViewportHintContext, useShellViewport } from './use-shell-viewport.js';
@@ -138,7 +138,11 @@ export function RightPanel({ panelViews = EMPTY_PANEL_VIEWS, defaultView, modes
138
138
  setDisplayWidth(null);
139
139
  setWidth(w);
140
140
  };
141
- return (_jsx("aside", { "data-meda-panel-mode": mode, "aria-hidden": mode === 'closed' ? 'true' : undefined, className: cn('relative h-full shrink-0 overflow-hidden border-l border-shell-border bg-shell-panel', 'transition-[width] ease-[var(--motion-ease)] duration-[var(--motion-panel)]', mode === 'fullscreen' && 'fixed inset-0 h-screen w-screen border-none', zIndexClass, className), style: widthStyle, children: mode !== 'closed' && (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-shell-border px-3 py-2", children: [_jsx("div", { className: "flex items-center gap-1", children: resolvedPanelViews.map((view) => {
141
+ return (_jsx("aside", { "data-meda-panel-mode": mode, "aria-hidden": mode === 'closed' ? 'true' : undefined,
142
+ // No width transition: ⌘I / ⌘E are keyboard actions used many times a
143
+ // day, and an animated width re-wraps the main column every frame (and
144
+ // lags behind the cursor while resizing). The panel snaps.
145
+ className: cn('relative h-full shrink-0 overflow-hidden border-l border-shell-border bg-shell-panel', mode === 'fullscreen' && 'fixed inset-0 h-screen w-screen border-none', zIndexClass, className), style: widthStyle, children: mode !== 'closed' && (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-shell-border px-3 py-2", children: [_jsx("div", { className: "flex items-center gap-1", children: resolvedPanelViews.map((view) => {
142
146
  const isActive = view.id === activeView;
143
147
  const Icon = view.icon;
144
148
  const children = (_jsxs(_Fragment, { children: [_jsx(Icon, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: view.label })] }));
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ShellViewport } from './types.js';
3
+ export interface ShellViewportHintProviderProps {
4
+ /**
5
+ * Coarse device class detected from the request, or `null` to keep the
6
+ * desktop-first default.
7
+ */
8
+ value: ShellViewport | null;
9
+ children: ReactNode;
10
+ }
11
+ /**
12
+ * Client wrapper that supplies the SSR viewport hint.
13
+ *
14
+ * React Context providers cannot be rendered from a React Server Component,
15
+ * so an App Router server component — the place where request headers are
16
+ * actually available — must render this client component instead of touching
17
+ * `ShellViewportHintContext.Provider` itself:
18
+ *
19
+ * // app/layout.tsx — server component
20
+ * import { headers } from 'next/headers';
21
+ * import { ShellViewportHintProvider } from '@medalsocial/meda/shell';
22
+ *
23
+ * export default async function Layout({ children }) {
24
+ * const isPhone = (await headers()).get('sec-ch-ua-mobile') === '?1';
25
+ * return (
26
+ * <ShellViewportHintProvider value={isPhone ? 'mobile' : null}>
27
+ * {children}
28
+ * </ShellViewportHintProvider>
29
+ * );
30
+ * }
31
+ *
32
+ * See {@link ShellViewportHintContext} for what the hint does.
33
+ */
34
+ export declare function ShellViewportHintProvider({ value, children }: ShellViewportHintProviderProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ShellViewportHintContext } from './use-shell-viewport.js';
4
+ /**
5
+ * Client wrapper that supplies the SSR viewport hint.
6
+ *
7
+ * React Context providers cannot be rendered from a React Server Component,
8
+ * so an App Router server component — the place where request headers are
9
+ * actually available — must render this client component instead of touching
10
+ * `ShellViewportHintContext.Provider` itself:
11
+ *
12
+ * // app/layout.tsx — server component
13
+ * import { headers } from 'next/headers';
14
+ * import { ShellViewportHintProvider } from '@medalsocial/meda/shell';
15
+ *
16
+ * export default async function Layout({ children }) {
17
+ * const isPhone = (await headers()).get('sec-ch-ua-mobile') === '?1';
18
+ * return (
19
+ * <ShellViewportHintProvider value={isPhone ? 'mobile' : null}>
20
+ * {children}
21
+ * </ShellViewportHintProvider>
22
+ * );
23
+ * }
24
+ *
25
+ * See {@link ShellViewportHintContext} for what the hint does.
26
+ */
27
+ export function ShellViewportHintProvider({ value, children }) {
28
+ return (_jsx(ShellViewportHintContext.Provider, { value: value, children: children }));
29
+ }
@@ -1,4 +1,6 @@
1
1
  import type { ShellViewport } from './types.js';
2
+ /** Optional request-derived band for server rendering and hydration only. */
3
+ export declare const ShellViewportHintContext: import("react").Context<ShellViewport | null>;
2
4
  /**
3
5
  * The shell's viewport band, resolved SYNCHRONOUSLY on the first client
4
6
  * render.
@@ -12,8 +14,8 @@ import type { ShellViewport } from './types.js';
12
14
  *
13
15
  * `useSyncExternalStore` reads `matchMedia` during the first client render,
14
16
  * so client-rendered apps commit the correct tree once. Server rendering
15
- * still returns 'desktop' via the server snapshot, and hydration stays
16
- * mismatch-safe (React uses the server snapshot while hydrating, then
17
- * re-reads — SSR consumers keep exactly the old behavior).
17
+ * uses the optional request-derived hint, otherwise 'desktop'. Hydration uses
18
+ * that same snapshot before switching to the live viewport. A hint lets an
19
+ * app send mobile shell HTML before its JavaScript loads.
18
20
  */
19
21
  export declare function useShellViewport(): ShellViewport;
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { useSyncExternalStore } from 'react';
2
+ import { createContext, useContext, useSyncExternalStore } from 'react';
3
3
  const BREAKPOINTS = {
4
4
  mobile: '(max-width: 767px)',
5
5
  tablet: '(min-width: 768px) and (max-width: 1023px)',
@@ -7,6 +7,8 @@ const BREAKPOINTS = {
7
7
  wide: '(min-width: 1280px) and (max-width: 1535px)',
8
8
  ultrawide: '(min-width: 1536px)',
9
9
  };
10
+ /** Optional request-derived band for server rendering and hydration only. */
11
+ export const ShellViewportHintContext = createContext(null);
10
12
  function getMatchMedia() {
11
13
  /* v8 ignore next — SSR guard: window is always defined in jsdom test env */
12
14
  if (typeof window === 'undefined')
@@ -63,7 +65,6 @@ function getClientViewport() {
63
65
  // First render happens before subscribe — detect directly.
64
66
  return detectViewport();
65
67
  }
66
- const getServerViewport = () => 'desktop';
67
68
  /**
68
69
  * The shell's viewport band, resolved SYNCHRONOUSLY on the first client
69
70
  * render.
@@ -77,10 +78,11 @@ const getServerViewport = () => 'desktop';
77
78
  *
78
79
  * `useSyncExternalStore` reads `matchMedia` during the first client render,
79
80
  * so client-rendered apps commit the correct tree once. Server rendering
80
- * still returns 'desktop' via the server snapshot, and hydration stays
81
- * mismatch-safe (React uses the server snapshot while hydrating, then
82
- * re-reads — SSR consumers keep exactly the old behavior).
81
+ * uses the optional request-derived hint, otherwise 'desktop'. Hydration uses
82
+ * that same snapshot before switching to the live viewport. A hint lets an
83
+ * app send mobile shell HTML before its JavaScript loads.
83
84
  */
84
85
  export function useShellViewport() {
85
- return useSyncExternalStore(subscribeToViewport, getClientViewport, getServerViewport);
86
+ const hint = useContext(ShellViewportHintContext);
87
+ return useSyncExternalStore(subscribeToViewport, getClientViewport, () => hint ?? 'desktop');
86
88
  }
@@ -1,4 +1,4 @@
1
- /*! Vendored from @xyflow/react@12.11.6 dist/style.css — MIT License, (c) webkid GmbH. https://github.com/xyflow/xyflow */
1
+ /*! Vendored from @xyflow/react@12.12.0 dist/style.css — MIT License, (c) webkid GmbH. https://github.com/xyflow/xyflow */
2
2
  /* this gets exported as style.css and can be used for the default theming */
3
3
  /* these are the necessary styles for React/Svelte Flow, they get used by base.css and style.css */
4
4
  .react-flow {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/meda",
3
- "version": "3.13.0",
3
+ "version": "3.15.0",
4
4
  "description": "Shared Meda UI shell and runtime package.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -210,7 +210,7 @@
210
210
  "vaul": "^1.1.2"
211
211
  },
212
212
  "devDependencies": {
213
- "@biomejs/biome": "2.5.14",
213
+ "@biomejs/biome": "2.5.15",
214
214
  "@changesets/changelog-github": "1.0.1",
215
215
  "@changesets/cli": "3.0.3",
216
216
  "@dnd-kit/core": "6.3.1",
@@ -218,48 +218,48 @@
218
218
  "@dnd-kit/utilities": "3.2.2",
219
219
  "@fontsource-variable/geist": "5.3.0",
220
220
  "@fontsource-variable/geist-mono": "5.3.0",
221
- "@react-three/fiber": "9.7.0",
222
- "@size-limit/preset-small-lib": "13.0.3",
223
- "@storybook/addon-a11y": "10.6.0",
224
- "@storybook/addon-docs": "10.6.0",
225
- "@storybook/addon-mcp": "10.6.0",
226
- "@storybook/addon-themes": "10.6.0",
227
- "@storybook/addon-vitest": "10.6.0",
228
- "@storybook/react-vite": "10.6.0",
221
+ "@react-three/fiber": "9.8.1",
222
+ "@size-limit/preset-small-lib": "13.1.1",
223
+ "@storybook/addon-a11y": "10.6.2",
224
+ "@storybook/addon-docs": "10.6.2",
225
+ "@storybook/addon-mcp": "10.6.2",
226
+ "@storybook/addon-themes": "10.6.2",
227
+ "@storybook/addon-vitest": "10.6.2",
228
+ "@storybook/react-vite": "10.6.2",
229
229
  "@tailwindcss/cli": "4.3.3",
230
230
  "@tailwindcss/vite": "4.3.3",
231
- "@tanstack/intent": "0.4.0",
231
+ "@tanstack/intent": "0.5.5",
232
232
  "@testing-library/jest-dom": "7.0.1",
233
233
  "@testing-library/react": "16.3.3",
234
234
  "@types/react": "19.3.0",
235
235
  "@types/react-dom": "19.3.0",
236
236
  "@types/three": "0.185.4",
237
- "@vitejs/plugin-react": "6.1.1",
238
- "@vitest/browser-playwright": "4.1.11",
239
- "@vitest/coverage-istanbul": "4.1.11",
240
- "@vitest/coverage-v8": "4.1.11",
241
- "@xyflow/react": "12.11.6",
242
- "chromatic": "18.9.4",
237
+ "@vitejs/plugin-react": "6.1.2",
238
+ "@vitest/browser-playwright": "4.1.12",
239
+ "@vitest/coverage-istanbul": "4.1.12",
240
+ "@vitest/coverage-v8": "4.1.12",
241
+ "@xyflow/react": "12.12.0",
242
+ "chromatic": "18.12.0",
243
243
  "esbuild": "0.28.2",
244
244
  "fast-check": "4.10.2",
245
245
  "husky": "9.1.7",
246
- "jsdom": "30.1.0",
246
+ "jsdom": "30.1.3",
247
247
  "next-themes": "0.4.6",
248
- "playwright": "1.63.0",
249
- "react": "19.2.8",
250
- "react-dom": "19.2.8",
248
+ "playwright": "1.64.1",
249
+ "react": "19.2.9",
250
+ "react-dom": "19.2.9",
251
251
  "react-markdown": "10.1.0",
252
252
  "rehype-highlight": "7.0.2",
253
253
  "remark-gfm": "4.0.1",
254
- "size-limit": "13.0.3",
255
- "storybook": "10.6.0",
254
+ "size-limit": "13.1.1",
255
+ "storybook": "10.6.2",
256
256
  "tailwindcss": "4.3.3",
257
- "three": "0.185.1",
257
+ "three": "0.186.1",
258
258
  "typescript": "~6.0.3",
259
- "vite": "8.3.0",
260
- "vitest": "4.1.11",
259
+ "vite": "8.3.4",
260
+ "vitest": "4.1.12",
261
261
  "vitest-axe": "0.1.0",
262
- "wrangler": "4.135.0"
262
+ "wrangler": "4.151.0"
263
263
  },
264
264
  "keywords": [
265
265
  "tanstack-intent"