@medalsocial/meda 2.8.1 → 3.2.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.
Files changed (103) hide show
  1. package/README.md +130 -1
  2. package/dist/booking/add-child-sheet.d.ts +38 -0
  3. package/dist/booking/add-child-sheet.js +88 -0
  4. package/dist/booking/booking-skeleton.d.ts +24 -0
  5. package/dist/booking/booking-skeleton.js +17 -0
  6. package/dist/booking/confirmation.d.ts +85 -0
  7. package/dist/booking/confirmation.js +102 -0
  8. package/dist/booking/details-screen.d.ts +118 -0
  9. package/dist/booking/details-screen.js +314 -0
  10. package/dist/booking/format.d.ts +64 -0
  11. package/dist/booking/format.js +1 -0
  12. package/dist/booking/index.d.ts +35 -0
  13. package/dist/booking/index.js +40 -0
  14. package/dist/booking/internal/label-parts.d.ts +8 -0
  15. package/dist/booking/internal/label-parts.js +19 -0
  16. package/dist/booking/internal/ui.d.ts +33 -0
  17. package/dist/booking/internal/ui.js +35 -0
  18. package/dist/booking/label-keys.d.ts +34 -0
  19. package/dist/booking/label-keys.js +56 -0
  20. package/dist/booking/labels.d.ts +15 -0
  21. package/dist/booking/labels.js +17 -0
  22. package/dist/booking/live-status.d.ts +14 -0
  23. package/dist/booking/live-status.js +17 -0
  24. package/dist/booking/login-panel.d.ts +108 -0
  25. package/dist/booking/login-panel.js +239 -0
  26. package/dist/booking/login-sheet.d.ts +41 -0
  27. package/dist/booking/login-sheet.js +49 -0
  28. package/dist/booking/manage-screen.d.ts +113 -0
  29. package/dist/booking/manage-screen.js +369 -0
  30. package/dist/booking/otp-slots.d.ts +42 -0
  31. package/dist/booking/otp-slots.js +109 -0
  32. package/dist/booking/portal/account-card.d.ts +24 -0
  33. package/dist/booking/portal/account-card.js +18 -0
  34. package/dist/booking/portal/action-result.d.ts +30 -0
  35. package/dist/booking/portal/action-result.js +18 -0
  36. package/dist/booking/portal/age-confirm-card.d.ts +39 -0
  37. package/dist/booking/portal/age-confirm-card.js +42 -0
  38. package/dist/booking/portal/child-cards.d.ts +65 -0
  39. package/dist/booking/portal/child-cards.js +59 -0
  40. package/dist/booking/portal/data-controls.d.ts +48 -0
  41. package/dist/booking/portal/data-controls.js +103 -0
  42. package/dist/booking/portal/family-editor.d.ts +141 -0
  43. package/dist/booking/portal/family-editor.js +422 -0
  44. package/dist/booking/portal/logout-button.d.ts +21 -0
  45. package/dist/booking/portal/logout-button.js +27 -0
  46. package/dist/booking/portal/portal-shell.d.ts +71 -0
  47. package/dist/booking/portal/portal-shell.js +38 -0
  48. package/dist/booking/portal/portal-unreachable.d.ts +31 -0
  49. package/dist/booking/portal/portal-unreachable.js +19 -0
  50. package/dist/booking/portal/profile-form.d.ts +75 -0
  51. package/dist/booking/portal/profile-form.js +144 -0
  52. package/dist/booking/portal/rebook-cards.d.ts +44 -0
  53. package/dist/booking/portal/rebook-cards.js +35 -0
  54. package/dist/booking/portal/upcoming-bookings.d.ts +65 -0
  55. package/dist/booking/portal/upcoming-bookings.js +88 -0
  56. package/dist/booking/portal/vipps-link-row.d.ts +54 -0
  57. package/dist/booking/portal/vipps-link-row.js +80 -0
  58. package/dist/booking/portal/visit-history.d.ts +63 -0
  59. package/dist/booking/portal/visit-history.js +73 -0
  60. package/dist/booking/service-screen.d.ts +137 -0
  61. package/dist/booking/service-screen.js +233 -0
  62. package/dist/booking/slots.d.ts +12 -0
  63. package/dist/booking/slots.js +5 -0
  64. package/dist/booking/stylist-screen.d.ts +117 -0
  65. package/dist/booking/stylist-screen.js +226 -0
  66. package/dist/booking/summary-bar.d.ts +26 -0
  67. package/dist/booking/summary-bar.js +32 -0
  68. package/dist/booking/time-screen.d.ts +191 -0
  69. package/dist/booking/time-screen.js +418 -0
  70. package/dist/booking/types.d.ts +284 -0
  71. package/dist/booking/types.js +15 -0
  72. package/dist/booking/vipps-button.d.ts +49 -0
  73. package/dist/booking/vipps-button.js +33 -0
  74. package/dist/booking/who-screen.d.ts +121 -0
  75. package/dist/booking/who-screen.js +164 -0
  76. package/dist/primitives/avatar.d.ts +24 -0
  77. package/dist/primitives/avatar.js +44 -0
  78. package/dist/primitives/button.d.ts +21 -0
  79. package/dist/primitives/button.js +51 -0
  80. package/dist/primitives/checkbox.d.ts +23 -0
  81. package/dist/primitives/checkbox.js +20 -0
  82. package/dist/primitives/field.d.ts +47 -0
  83. package/dist/primitives/field.js +48 -0
  84. package/dist/primitives/index.d.ts +16 -0
  85. package/dist/primitives/index.js +8 -0
  86. package/dist/primitives/input.d.ts +10 -0
  87. package/dist/primitives/input.js +9 -0
  88. package/dist/primitives/sheet.d.ts +68 -0
  89. package/dist/primitives/sheet.js +303 -0
  90. package/dist/primitives/textarea.d.ts +14 -0
  91. package/dist/primitives/textarea.js +13 -0
  92. package/dist/primitives/toggle-group.d.ts +46 -0
  93. package/dist/primitives/toggle-group.js +148 -0
  94. package/dist/styles/base.css +40 -0
  95. package/dist/styles/booking.css +10 -0
  96. package/dist/styles/bridge.css +159 -0
  97. package/dist/styles/calendar.css +8 -0
  98. package/dist/styles/primitives.css +10 -0
  99. package/dist/styles/theme.css +12 -162
  100. package/dist/styles/vendor/xyflow.css +626 -0
  101. package/dist/styles/workflow-builder.css +17 -0
  102. package/package.json +45 -9
  103. package/skills/components/SKILL.md +53 -0
@@ -0,0 +1,24 @@
1
+ import { type ComponentProps } from 'react';
2
+ export type AvatarSize = 'sm' | 'md' | 'lg';
3
+ export interface AvatarProps extends Omit<ComponentProps<'span'>, 'children'> {
4
+ /** Person or entity name. Drives the accessible name and the initials. */
5
+ name: string;
6
+ /** Image URL. Falls back to initials when missing or when it fails to load. */
7
+ src?: string | null;
8
+ /** Override the computed initials (e.g. a single brand letter). */
9
+ initials?: string;
10
+ size?: AvatarSize;
11
+ }
12
+ /**
13
+ * Up to two initials from a name, letters only: the first letter of the first
14
+ * and of the last word. Digits, punctuation and emoji are ignored, so
15
+ * "Ali (Medal) 2" gives "AM" and " " gives "". Any script is supported via
16
+ * Unicode letter classes ("Åse Øien" -> "ÅØ").
17
+ */
18
+ export declare function getInitials(name: string): string;
19
+ /**
20
+ * Image avatar with an initials fallback. The wrapper carries `role="img"` +
21
+ * `aria-label={name}`, so the name is announced once whether the photo or the
22
+ * initials are showing.
23
+ */
24
+ export declare function Avatar({ name, src, initials, size, className, ...props }: AvatarProps): import("react").JSX.Element;
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useState } from 'react';
4
+ import { cn } from '../lib/utils.js';
5
+ const SIZE_CLASSES = {
6
+ sm: 'size-8 text-xs',
7
+ md: 'size-10 text-sm',
8
+ lg: 'size-12 text-base',
9
+ };
10
+ /**
11
+ * Up to two initials from a name, letters only: the first letter of the first
12
+ * and of the last word. Digits, punctuation and emoji are ignored, so
13
+ * "Ali (Medal) 2" gives "AM" and " " gives "". Any script is supported via
14
+ * Unicode letter classes ("Åse Øien" -> "ÅØ").
15
+ */
16
+ export function getInitials(name) {
17
+ const words = name
18
+ .split(/\s+/)
19
+ .map((word) => word.replace(/[^\p{L}]/gu, ''))
20
+ .filter(Boolean);
21
+ if (words.length === 0)
22
+ return '';
23
+ const first = words[0] ?? '';
24
+ const last = words.length > 1 ? (words[words.length - 1] ?? '') : '';
25
+ return (Array.from(first)[0] ?? '').concat(Array.from(last)[0] ?? '').toLocaleUpperCase();
26
+ }
27
+ /**
28
+ * Image avatar with an initials fallback. The wrapper carries `role="img"` +
29
+ * `aria-label={name}`, so the name is announced once whether the photo or the
30
+ * initials are showing.
31
+ */
32
+ export function Avatar({ name, src, initials, size = 'md', className, ...props }) {
33
+ // Keyed by URL, so a new `src` automatically gets a fresh chance.
34
+ const [failedSrc, setFailedSrc] = useState(null);
35
+ const showImage = Boolean(src) && failedSrc !== src;
36
+ const text = initials ?? getInitials(name);
37
+ // An SSR-rendered <img> can fail before React hydrates and attaches
38
+ // onError; catch that case by inspecting the element once it mounts.
39
+ const imageRef = useCallback((image) => {
40
+ if (image?.complete && image.naturalWidth === 0 && src)
41
+ setFailedSrc(src);
42
+ }, [src]);
43
+ return (_jsx("span", { role: "img", "aria-label": name, "data-slot": "avatar", "data-size": size, "data-state": showImage ? 'image' : 'fallback', className: cn('relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground', SIZE_CLASSES[size], className), ...props, children: showImage ? (_jsx("img", { ref: imageRef, src: src ?? undefined, alt: "", decoding: "async", loading: "lazy", onError: () => setFailedSrc(src ?? null), className: "size-full object-cover" })) : (_jsx("span", { "aria-hidden": "true", "data-slot": "avatar-fallback", children: text })) }));
44
+ }
@@ -0,0 +1,21 @@
1
+ import type { ButtonHTMLAttributes, ComponentProps, ReactNode } from 'react';
2
+ import { type RenderElement } from '../lib/render-element.js';
3
+ export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'outline';
4
+ export type ButtonSize = 'sm' | 'md' | 'lg';
5
+ export interface ButtonProps extends ComponentProps<'button'> {
6
+ variant?: ButtonVariant;
7
+ size?: ButtonSize;
8
+ /**
9
+ * Shows a spinner, sets `aria-busy`, and swallows clicks while keeping the
10
+ * button focusable (so focus is not lost mid-submit) and its width stable.
11
+ */
12
+ loading?: boolean;
13
+ /** Screen-reader text announced while `loading`. Defaults to "Loading". */
14
+ loadingLabel?: string;
15
+ /**
16
+ * Render as another element (e.g. a router `<Link>` or `<a>`), merging
17
+ * meda's props and classes onto it — same pattern as `AuthProviderButton`.
18
+ */
19
+ render?: RenderElement<ButtonHTMLAttributes<HTMLElement>>;
20
+ }
21
+ export declare function Button({ variant, size, loading, loadingLabel, render, type, disabled, className, children, onClick, ...props }: ButtonProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { LoaderCircle } from 'lucide-react';
4
+ import { renderElement } from '../lib/render-element.js';
5
+ import { cn } from '../lib/utils.js';
6
+ // Semantic tokens only, so a consumer's own `--primary` / `--secondary` /
7
+ // `--accent` re-theme the button without touching meda.
8
+ const VARIANT_CLASSES = {
9
+ primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
10
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
11
+ ghost: 'text-foreground hover:bg-accent hover:text-accent-foreground',
12
+ outline: 'border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground',
13
+ };
14
+ const SIZE_CLASSES = {
15
+ sm: 'h-8 gap-1.5 px-3 text-xs [&_svg]:size-3.5',
16
+ md: 'h-10 gap-2 px-4 text-sm [&_svg]:size-4',
17
+ lg: 'h-11 gap-2 px-5 text-base [&_svg]:size-4',
18
+ };
19
+ export function Button({ variant = 'primary', size = 'md', loading = false, loadingLabel = 'Loading', render, type = 'button', disabled, className, children, onClick, ...props }) {
20
+ // A rendered link can't be natively disabled, so swallow its clicks too.
21
+ const inert = loading || (render !== undefined && Boolean(disabled));
22
+ const handleClick = (event) => {
23
+ if (inert) {
24
+ event.preventDefault();
25
+ return;
26
+ }
27
+ onClick?.(event);
28
+ };
29
+ const content = (_jsxs(_Fragment, { children: [loading ? (_jsxs(_Fragment, { children: [_jsx(LoaderCircle, { "data-slot": "button-spinner", "aria-hidden": "true", className: "animate-spin motion-reduce:animate-none" }), _jsx("span", { className: "sr-only", children: loadingLabel })] })) : null, children] }));
30
+ const buttonProps = {
31
+ ...props,
32
+ 'data-slot': 'button',
33
+ 'data-variant': variant,
34
+ 'data-size': size,
35
+ 'data-loading': loading || undefined,
36
+ 'aria-busy': loading || undefined,
37
+ 'aria-disabled': loading || undefined,
38
+ className: cn('relative inline-flex shrink-0 select-none items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 data-loading:cursor-progress [&_svg]:pointer-events-none [&_svg]:shrink-0', VARIANT_CLASSES[variant], SIZE_CLASSES[size], className),
39
+ onClick: handleClick,
40
+ children: content,
41
+ };
42
+ if (render) {
43
+ // Non-button hosts (links) have no `disabled`; expose it via ARIA instead.
44
+ return renderElement(render, {
45
+ ...buttonProps,
46
+ 'aria-disabled': inert || undefined,
47
+ 'data-disabled': disabled || undefined,
48
+ });
49
+ }
50
+ return _jsx("button", { type: type, disabled: disabled, ...buttonProps });
51
+ }
@@ -0,0 +1,23 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface CheckboxProps extends Omit<ComponentProps<'input'>, 'type' | 'size'> {
3
+ /**
4
+ * Called with the new checked state. Fires alongside `onChange`, so a form
5
+ * can use whichever it prefers.
6
+ */
7
+ onCheckedChange?: (checked: boolean) => void;
8
+ /**
9
+ * Marks the box invalid: sets `aria-invalid="true"` and the destructive
10
+ * border. An explicit `aria-invalid` prop still wins.
11
+ */
12
+ invalid?: boolean;
13
+ /** Class for the wrapping `<span>` (the input itself takes `className`). */
14
+ rootClassName?: string;
15
+ }
16
+ /**
17
+ * A native `<input type="checkbox">` drawn as a 16 px box with a check mark.
18
+ * Native on purpose: it submits with a form, works with `<label htmlFor>`,
19
+ * answers Space without JavaScript, and costs no dialog/portal machinery.
20
+ * The input carries `data-slot="checkbox"` and a `data-invalid` hook; style
21
+ * the checked state with `checked:` / `peer-checked:`.
22
+ */
23
+ export declare function Checkbox({ className, rootClassName, invalid, onCheckedChange, onChange, checked, defaultChecked, 'aria-invalid': ariaInvalid, ...props }: CheckboxProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Check } from 'lucide-react';
4
+ import { cn } from '../lib/utils.js';
5
+ /**
6
+ * A native `<input type="checkbox">` drawn as a 16 px box with a check mark.
7
+ * Native on purpose: it submits with a form, works with `<label htmlFor>`,
8
+ * answers Space without JavaScript, and costs no dialog/portal machinery.
9
+ * The input carries `data-slot="checkbox"` and a `data-invalid` hook; style
10
+ * the checked state with `checked:` / `peer-checked:`.
11
+ */
12
+ export function Checkbox({ className, rootClassName, invalid = false, onCheckedChange, onChange, checked, defaultChecked, 'aria-invalid': ariaInvalid, ...props }) {
13
+ const resolvedInvalid = ariaInvalid ?? (invalid || undefined);
14
+ return (_jsxs("span", { "data-slot": "checkbox-root", className: cn('relative inline-flex size-4 shrink-0', rootClassName), children: [_jsx("input", { type: "checkbox", "data-slot": "checkbox", "data-invalid": resolvedInvalid === true || resolvedInvalid === 'true' ? '' : undefined, "aria-invalid": resolvedInvalid, checked: checked, defaultChecked: defaultChecked, onChange: (event) => {
15
+ onChange?.(event);
16
+ onCheckedChange?.(event.currentTarget.checked);
17
+ }, className: cn(
18
+ // Same focus / invalid treatment as Input, so the two sit together.
19
+ 'peer size-4 shrink-0 cursor-pointer appearance-none rounded-[4px] border border-input bg-background transition-colors outline-none checked:border-primary checked:bg-primary focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/30 disabled:cursor-not-allowed disabled:opacity-50', className), ...props }), _jsx(Check, { "aria-hidden": "true", "data-slot": "checkbox-indicator", className: "pointer-events-none absolute inset-0 m-auto size-3.5 text-primary-foreground opacity-0 peer-checked:opacity-100" })] }));
20
+ }
@@ -0,0 +1,47 @@
1
+ import type { ComponentProps } from 'react';
2
+ export type FieldOrientation = 'vertical' | 'horizontal';
3
+ export interface FieldProps extends ComponentProps<'div'> {
4
+ /**
5
+ * `vertical` (default) stacks label, control, description and error.
6
+ * `horizontal` puts a checkbox/switch beside its label.
7
+ */
8
+ orientation?: FieldOrientation;
9
+ /**
10
+ * Dims the label. A disabled control inside the field does this on its
11
+ * own (`:has(:disabled)`); the prop is for fields whose control cannot be.
12
+ */
13
+ disabled?: boolean;
14
+ }
15
+ export type FieldLabelProps = ComponentProps<'label'>;
16
+ export type FieldDescriptionProps = ComponentProps<'p'>;
17
+ export type FieldSetProps = ComponentProps<'fieldset'>;
18
+ export type FieldLegendProps = ComponentProps<'legend'>;
19
+ export interface FieldErrorProps extends ComponentProps<'p'> {
20
+ /**
21
+ * Several messages at once (e.g. from a schema validator). Each renders as
22
+ * its own `<p data-slot="field-error">` inside one wrapper that takes `id`
23
+ * and the other props; duplicates are shown once.
24
+ */
25
+ errors?: readonly string[];
26
+ }
27
+ declare function FieldRoot({ orientation, disabled, className, ...props }: FieldProps): import("react").JSX.Element;
28
+ declare function FieldLabel({ className, ...props }: FieldLabelProps): import("react").JSX.Element;
29
+ declare function FieldDescription({ className, ...props }: FieldDescriptionProps): import("react").JSX.Element;
30
+ declare function FieldError({ className, children, errors, ...props }: FieldErrorProps): import("react").JSX.Element | null;
31
+ declare function FieldSet({ className, ...props }: FieldSetProps): import("react").JSX.Element;
32
+ declare function FieldLegend({ className, ...props }: FieldLegendProps): import("react").JSX.Element;
33
+ /**
34
+ * Form field layout: `Field` groups a control with `Field.Label`,
35
+ * `Field.Description` and `Field.Error`. It owns no state and no ids — wire
36
+ * `htmlFor` / `aria-describedby` yourself, so the markup stays obvious and
37
+ * works with any form library. `Field.Set` + `Field.Legend` group related
38
+ * fields (radio sets, consent boxes).
39
+ */
40
+ export declare const Field: typeof FieldRoot & {
41
+ Label: typeof FieldLabel;
42
+ Description: typeof FieldDescription;
43
+ Error: typeof FieldError;
44
+ Set: typeof FieldSet;
45
+ Legend: typeof FieldLegend;
46
+ };
47
+ export {};
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../lib/utils.js';
4
+ const ORIENTATION_CLASSES = {
5
+ vertical: '',
6
+ horizontal: 'grid-cols-[auto_1fr] items-center',
7
+ };
8
+ function FieldRoot({ orientation = 'vertical', disabled, className, ...props }) {
9
+ return (_jsx("div", { "data-slot": "field", "data-orientation": orientation, "data-disabled": disabled ? 'true' : undefined, className: cn('group/field grid gap-2', ORIENTATION_CLASSES[orientation], className), ...props }));
10
+ }
11
+ function FieldLabel({ className, ...props }) {
12
+ return (_jsx("label", { "data-slot": "field-label", className: cn('flex select-none items-center gap-2 text-sm font-medium leading-none group-has-disabled/field:cursor-not-allowed group-has-disabled/field:opacity-50 group-data-[disabled=true]/field:pointer-events-none group-data-[disabled=true]/field:opacity-50', className), ...props }));
13
+ }
14
+ function FieldDescription({ className, ...props }) {
15
+ return (_jsx("p", { "data-slot": "field-description", className: cn('text-sm text-muted-foreground', className), ...props }));
16
+ }
17
+ function FieldError({ className, children, errors, ...props }) {
18
+ const unique = errors ? Array.from(new Set(errors)) : [];
19
+ if (unique.length > 0) {
20
+ // One element owns the id (and every other prop), so `aria-describedby`
21
+ // pointing at it reads out all of the messages.
22
+ const { id, ...rest } = props;
23
+ return (_jsx("div", { id: id, "data-slot": "field-errors", className: "flex flex-col gap-1", ...rest, children: unique.map((error) => (_jsx("p", { "data-slot": "field-error", className: cn('text-sm text-destructive', className), children: error }, error))) }));
24
+ }
25
+ if (children == null || children === false)
26
+ return null;
27
+ return (_jsx("p", { "data-slot": "field-error", className: cn('text-sm text-destructive', className), ...props, children: children }));
28
+ }
29
+ function FieldSet({ className, ...props }) {
30
+ return _jsx("fieldset", { "data-slot": "field-set", className: cn('grid gap-6', className), ...props });
31
+ }
32
+ function FieldLegend({ className, ...props }) {
33
+ return (_jsx("legend", { "data-slot": "field-legend", className: cn('text-base font-medium leading-none text-foreground', className), ...props }));
34
+ }
35
+ /**
36
+ * Form field layout: `Field` groups a control with `Field.Label`,
37
+ * `Field.Description` and `Field.Error`. It owns no state and no ids — wire
38
+ * `htmlFor` / `aria-describedby` yourself, so the markup stays obvious and
39
+ * works with any form library. `Field.Set` + `Field.Legend` group related
40
+ * fields (radio sets, consent boxes).
41
+ */
42
+ export const Field = Object.assign(FieldRoot, {
43
+ Label: FieldLabel,
44
+ Description: FieldDescription,
45
+ Error: FieldError,
46
+ Set: FieldSet,
47
+ Legend: FieldLegend,
48
+ });
@@ -1,10 +1,26 @@
1
+ export type { AvatarProps, AvatarSize } from './avatar.js';
2
+ export { Avatar, getInitials } from './avatar.js';
3
+ export type { ButtonProps, ButtonSize, ButtonVariant } from './button.js';
4
+ export { Button } from './button.js';
1
5
  export type { CardBodyProps, CardFooterProps, CardHeaderProps, CardProps, } from './card.js';
2
6
  export { Card } from './card.js';
7
+ export type { CheckboxProps } from './checkbox.js';
8
+ export { Checkbox } from './checkbox.js';
3
9
  export type { EmptyStateProps, EmptyStateVariant } from './empty-state.js';
4
10
  export { EmptyState } from './empty-state.js';
11
+ export type { FieldDescriptionProps, FieldErrorProps, FieldLabelProps, FieldLegendProps, FieldOrientation, FieldProps, FieldSetProps, } from './field.js';
12
+ export { Field } from './field.js';
5
13
  export type { FilterRailGroupProps, FilterRailProps } from './filter-rail.js';
6
14
  export { FilterRail } from './filter-rail.js';
15
+ export type { InputProps } from './input.js';
16
+ export { Input } from './input.js';
17
+ export type { SheetCloseProps, SheetContentProps, SheetProps, SheetSide, SheetTriggerProps, } from './sheet.js';
18
+ export { Sheet } from './sheet.js';
7
19
  export type { SkeletonProps } from './skeleton.js';
8
20
  export { Skeleton } from './skeleton.js';
9
21
  export type { StatusPillProps, StatusPillSize, StatusPillTone } from './status-pill.js';
10
22
  export { StatusPill } from './status-pill.js';
23
+ export type { TextareaProps } from './textarea.js';
24
+ export { Textarea } from './textarea.js';
25
+ export type { ToggleGroupItemProps, ToggleGroupMultipleProps, ToggleGroupOrientation, ToggleGroupProps, ToggleGroupSingleProps, ToggleGroupSize, } from './toggle-group.js';
26
+ export { ToggleGroup } from './toggle-group.js';
@@ -1,5 +1,13 @@
1
+ export { Avatar, getInitials } from './avatar.js';
2
+ export { Button } from './button.js';
1
3
  export { Card } from './card.js';
4
+ export { Checkbox } from './checkbox.js';
2
5
  export { EmptyState } from './empty-state.js';
6
+ export { Field } from './field.js';
3
7
  export { FilterRail } from './filter-rail.js';
8
+ export { Input } from './input.js';
9
+ export { Sheet } from './sheet.js';
4
10
  export { Skeleton } from './skeleton.js';
5
11
  export { StatusPill } from './status-pill.js';
12
+ export { Textarea } from './textarea.js';
13
+ export { ToggleGroup } from './toggle-group.js';
@@ -0,0 +1,10 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface InputProps extends ComponentProps<'input'> {
3
+ /**
4
+ * Marks the field invalid: sets `aria-invalid="true"` and the destructive
5
+ * border/ring. Pair it with `aria-describedby` pointing at the error text.
6
+ * An explicit `aria-invalid` prop still wins.
7
+ */
8
+ invalid?: boolean;
9
+ }
10
+ export declare function Input({ invalid, type, className, 'aria-invalid': ariaInvalid, ...props }: InputProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../lib/utils.js';
4
+ export function Input({ invalid = false, type = 'text', className, 'aria-invalid': ariaInvalid, ...props }) {
5
+ const resolvedInvalid = ariaInvalid ?? (invalid || undefined);
6
+ return (_jsx("input", { type: type, "data-slot": "input", "data-invalid": resolvedInvalid === true || resolvedInvalid === 'true' ? '' : undefined, "aria-invalid": resolvedInvalid, className: cn(
7
+ // text-base below `sm` keeps iOS Safari from zooming on focus.
8
+ 'flex h-10 w-full min-w-0 rounded-md border border-input bg-background px-3 py-2 text-base text-foreground transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/30 file:border-0 file:bg-transparent file:text-sm file:font-medium sm:text-sm', className), ...props }));
9
+ }
@@ -0,0 +1,68 @@
1
+ import { type ButtonHTMLAttributes, type ComponentProps, type ReactNode } from 'react';
2
+ import { type RenderElement } from '../lib/render-element.js';
3
+ export type SheetSide = 'responsive' | 'bottom';
4
+ export interface SheetProps {
5
+ /** Controlled open state. */
6
+ open?: boolean;
7
+ /** Initial open state when uncontrolled. */
8
+ defaultOpen?: boolean;
9
+ /** Called with the requested state (trigger, close button, Escape, backdrop). */
10
+ onOpenChange?: (open: boolean) => void;
11
+ /**
12
+ * `false` ignores every close request (Escape, backdrop, `Sheet.Close`,
13
+ * a native `dialog.close()`) — use it while a submit is in flight so the
14
+ * sheet cannot vanish under the user. Default `true`.
15
+ */
16
+ dismissible?: boolean;
17
+ children?: ReactNode;
18
+ }
19
+ export interface SheetTriggerProps extends ComponentProps<'button'> {
20
+ /** Render as another element, merging the trigger's props onto it. */
21
+ render?: RenderElement<ButtonHTMLAttributes<HTMLElement>>;
22
+ }
23
+ export interface SheetContentProps extends Omit<ComponentProps<'dialog'>, 'open'> {
24
+ /**
25
+ * `responsive` (default): a bottom sheet on phones, a centred dialog from
26
+ * `md` up. `bottom` keeps the bottom sheet at every width.
27
+ */
28
+ side?: SheetSide;
29
+ /** Class for the padded inner wrapper that holds the children. */
30
+ bodyClassName?: string;
31
+ }
32
+ export interface SheetCloseProps extends ComponentProps<'button'> {
33
+ /** Render as another element (e.g. a meda `Button`), merging close behaviour onto it. */
34
+ render?: RenderElement<ButtonHTMLAttributes<HTMLElement>>;
35
+ }
36
+ declare function SheetRoot({ open: openProp, defaultOpen, onOpenChange, dismissible, children, }: SheetProps): import("react").JSX.Element;
37
+ declare function SheetTrigger({ render, onClick, className, children, ...props }: SheetTriggerProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
38
+ declare function SheetContent({ side, className, bodyClassName, children, onCancel, onClick, onClose, onKeyDown, onPointerDown, ...props }: SheetContentProps): import("react").JSX.Element;
39
+ declare function SheetTitle({ className, id, ...props }: ComponentProps<'h2'>): import("react").JSX.Element;
40
+ declare function SheetDescription({ className, id, ...props }: ComponentProps<'p'>): import("react").JSX.Element;
41
+ declare function SheetClose({ render, onClick, children, ...props }: SheetCloseProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
42
+ /**
43
+ * A modal sheet on the native `<dialog>` element: a bottom sheet on phones and
44
+ * a centred dialog from `md` up. `showModal()` gives the top layer, an inert
45
+ * background and a Tab trap for free; the component adds controlled state,
46
+ * Escape / backdrop dismissal, scroll lock and focus return to the trigger.
47
+ * The entry fade is skipped under `prefers-reduced-motion`.
48
+ *
49
+ * ```tsx
50
+ * <Sheet>
51
+ * <Sheet.Trigger>Add child</Sheet.Trigger>
52
+ * <Sheet.Content>
53
+ * <Sheet.Title>Add child</Sheet.Title>
54
+ * <Sheet.Description>Only for this booking.</Sheet.Description>
55
+ * …
56
+ * <Sheet.Close aria-label="Close">×</Sheet.Close>
57
+ * </Sheet.Content>
58
+ * </Sheet>
59
+ * ```
60
+ */
61
+ export declare const Sheet: typeof SheetRoot & {
62
+ Trigger: typeof SheetTrigger;
63
+ Content: typeof SheetContent;
64
+ Title: typeof SheetTitle;
65
+ Description: typeof SheetDescription;
66
+ Close: typeof SheetClose;
67
+ };
68
+ export {};