panelui-web 0.10.1 → 0.11.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 (99) hide show
  1. package/dist/components/alert/index.d.ts +15 -8
  2. package/dist/components/alert/index.js +65 -18
  3. package/dist/components/alert-dialog/index.d.ts +9 -3
  4. package/dist/components/alert-dialog/index.js +23 -7
  5. package/dist/components/avatar/index.d.ts +9 -3
  6. package/dist/components/avatar/index.js +34 -10
  7. package/dist/components/badge/index.d.ts +57 -0
  8. package/dist/components/badge/index.js +61 -0
  9. package/dist/components/bar-picker/index.d.ts +8 -1
  10. package/dist/components/bar-picker/index.js +35 -26
  11. package/dist/components/button/index.d.ts +68 -46
  12. package/dist/components/button/index.js +38 -2
  13. package/dist/components/button-group/index.d.ts +11 -5
  14. package/dist/components/button-group/index.js +31 -7
  15. package/dist/components/canvas/index.d.ts +9 -3
  16. package/dist/components/canvas/index.js +53 -25
  17. package/dist/components/card/index.d.ts +13 -7
  18. package/dist/components/card/index.js +45 -17
  19. package/dist/components/checkbox/index.d.ts +73 -0
  20. package/dist/components/checkbox/index.js +100 -0
  21. package/dist/components/chip/index.d.ts +79 -0
  22. package/dist/components/chip/index.js +112 -0
  23. package/dist/components/choice-cards/index.d.ts +11 -5
  24. package/dist/components/choice-cards/index.js +25 -6
  25. package/dist/components/command-dock/index.d.ts +16 -9
  26. package/dist/components/command-dock/index.js +76 -19
  27. package/dist/components/day-grid/index.d.ts +12 -6
  28. package/dist/components/day-grid/index.js +71 -27
  29. package/dist/components/dialog/index.d.ts +19 -12
  30. package/dist/components/dialog/index.js +76 -24
  31. package/dist/components/feedback/index.d.ts +20 -13
  32. package/dist/components/feedback/index.js +83 -27
  33. package/dist/components/field/index.d.ts +85 -0
  34. package/dist/components/field/index.js +90 -0
  35. package/dist/components/flow/index.d.ts +21 -14
  36. package/dist/components/flow/index.js +56 -20
  37. package/dist/components/frame/index.d.ts +17 -11
  38. package/dist/components/frame/index.js +73 -22
  39. package/dist/components/input/index.d.ts +9 -24
  40. package/dist/components/input/index.js +53 -6
  41. package/dist/components/inspector/index.d.ts +69 -63
  42. package/dist/components/inspector/index.js +31 -9
  43. package/dist/components/kpi/index.d.ts +20 -14
  44. package/dist/components/kpi/index.js +111 -36
  45. package/dist/components/label/index.d.ts +10 -4
  46. package/dist/components/label/index.js +39 -9
  47. package/dist/components/lane-board/index.d.ts +15 -9
  48. package/dist/components/lane-board/index.js +97 -57
  49. package/dist/components/menu/index.d.ts +10 -4
  50. package/dist/components/menu/index.js +28 -10
  51. package/dist/components/message/index.d.ts +15 -9
  52. package/dist/components/message/index.js +71 -19
  53. package/dist/components/not-found/index.d.ts +10 -4
  54. package/dist/components/not-found/index.js +48 -12
  55. package/dist/components/planet/index.d.ts +11 -5
  56. package/dist/components/planet/index.js +73 -20
  57. package/dist/components/prompt/index.d.ts +30 -23
  58. package/dist/components/prompt/index.js +148 -52
  59. package/dist/components/radio-group/index.d.ts +124 -0
  60. package/dist/components/radio-group/index.js +216 -0
  61. package/dist/components/range-meter/index.d.ts +9 -3
  62. package/dist/components/range-meter/index.js +61 -27
  63. package/dist/components/rolling-number/index.d.ts +8 -1
  64. package/dist/components/rolling-number/index.js +15 -6
  65. package/dist/components/select/index.d.ts +7 -1
  66. package/dist/components/select/index.js +13 -4
  67. package/dist/components/sheet/index.d.ts +8 -2
  68. package/dist/components/sheet/index.js +27 -15
  69. package/dist/components/slider/index.d.ts +11 -4
  70. package/dist/components/slider/index.js +30 -10
  71. package/dist/components/soundwave/index.d.ts +8 -2
  72. package/dist/components/soundwave/index.js +26 -6
  73. package/dist/components/stat/index.d.ts +13 -7
  74. package/dist/components/stat/index.js +45 -15
  75. package/dist/components/table/index.d.ts +128 -0
  76. package/dist/components/table/index.js +145 -0
  77. package/dist/components/tabs/index.d.ts +7 -1
  78. package/dist/components/tabs/index.js +8 -2
  79. package/dist/components/textarea/index.d.ts +0 -23
  80. package/dist/components/textarea/index.js +22 -5
  81. package/dist/components/tilt-card/index.d.ts +9 -3
  82. package/dist/components/tilt-card/index.js +28 -8
  83. package/dist/components/timeline/index.d.ts +20 -13
  84. package/dist/components/timeline/index.js +166 -59
  85. package/dist/components/toast/dither-text.d.ts +13 -0
  86. package/dist/components/toast/dither-text.js +143 -0
  87. package/dist/components/toast/dither.d.ts +29 -0
  88. package/dist/components/toast/dither.js +153 -0
  89. package/dist/components/toast/index.d.ts +108 -0
  90. package/dist/components/toast/index.js +225 -0
  91. package/dist/components/toolbar/index.d.ts +9 -3
  92. package/dist/components/toolbar/index.js +23 -6
  93. package/dist/components/workbench/index.d.ts +50 -43
  94. package/dist/components/workbench/index.js +77 -20
  95. package/dist/components/xy-pad/index.d.ts +9 -3
  96. package/dist/components/xy-pad/index.js +13 -2
  97. package/dist/index.d.ts +7 -0
  98. package/dist/index.js +7 -0
  99. package/package.json +1 -1
@@ -34,6 +34,12 @@
34
34
  * JavaScript.
35
35
  */
36
36
  import type { ComponentProps } from 'react';
37
+ import { useRender } from '@base-ui/react/use-render';
38
+ import type { ElementType as RenderElementType } from 'react';
39
+ /** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
40
+ type RenderProp<Tag extends RenderElementType> = {
41
+ render?: useRender.ComponentProps<Tag>['render'];
42
+ };
37
43
  export interface CardProps extends ComponentProps<'div'> {
38
44
  /** `sm` tightens the padding and the title, for a card in a dense grid. Default `default`. */
39
45
  size?: 'default' | 'sm';
@@ -43,14 +49,14 @@ export interface CardProps extends ComponentProps<'div'> {
43
49
  */
44
50
  as?: 'div' | 'article' | 'section' | 'li';
45
51
  }
46
- declare function CardRoot({ size, as: Element, className, ...props }: CardProps): import("react").JSX.Element;
52
+ declare function CardRoot({ size, as, render, className, ...props }: CardProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
47
53
  declare namespace CardRoot {
48
54
  var displayName: string;
49
55
  }
50
56
  export interface CardHeaderProps extends ComponentProps<'div'> {
51
57
  }
52
58
  /** The title, the line under it, and the action beside them. */
53
- declare function CardHeader({ className, ...props }: CardHeaderProps): import("react").JSX.Element;
59
+ declare function CardHeader({ className, render, ...props }: CardHeaderProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
54
60
  declare namespace CardHeader {
55
61
  var displayName: string;
56
62
  }
@@ -59,28 +65,28 @@ export interface CardTitleProps extends ComponentProps<'h3'> {
59
65
  as?: 'h2' | 'h3' | 'h4' | 'div';
60
66
  }
61
67
  /** What the card is about. A heading, so the page outline includes it. */
62
- declare function CardTitle({ as: Element, className, ...props }: CardTitleProps): import("react").JSX.Element;
68
+ declare function CardTitle({ as, render, className, ...props }: CardTitleProps & RenderProp<'h3'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
63
69
  declare namespace CardTitle {
64
70
  var displayName: string;
65
71
  }
66
72
  export interface CardDescriptionProps extends ComponentProps<'p'> {
67
73
  }
68
74
  /** A line under the title. */
69
- declare function CardDescription({ className, ...props }: CardDescriptionProps): import("react").JSX.Element;
75
+ declare function CardDescription({ className, render, ...props }: CardDescriptionProps & RenderProp<'p'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
70
76
  declare namespace CardDescription {
71
77
  var displayName: string;
72
78
  }
73
79
  export interface CardActionProps extends ComponentProps<'div'> {
74
80
  }
75
81
  /** A control for the whole card, held in the header's top trailing corner. */
76
- declare function CardAction({ className, ...props }: CardActionProps): import("react").JSX.Element;
82
+ declare function CardAction({ className, render, ...props }: CardActionProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
77
83
  declare namespace CardAction {
78
84
  var displayName: string;
79
85
  }
80
86
  export interface CardContentProps extends ComponentProps<'div'> {
81
87
  }
82
88
  /** The body. */
83
- declare function CardContent({ className, ...props }: CardContentProps): import("react").JSX.Element;
89
+ declare function CardContent({ className, render, ...props }: CardContentProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
84
90
  declare namespace CardContent {
85
91
  var displayName: string;
86
92
  }
@@ -92,7 +98,7 @@ export interface CardFooterProps extends ComponentProps<'div'> {
92
98
  variant?: 'plain' | 'panel';
93
99
  }
94
100
  /** The row at the bottom, usually the card's actions. */
95
- declare function CardFooter({ variant, className, ...props }: CardFooterProps): import("react").JSX.Element;
101
+ declare function CardFooter({ variant, className, render, ...props }: CardFooterProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
96
102
  declare namespace CardFooter {
97
103
  var displayName: string;
98
104
  }
@@ -1,5 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ 'use client';
2
2
  import { tv } from 'tailwind-variants';
3
+ import { mergeProps } from '@base-ui/react/merge-props';
4
+ import { useRender } from '@base-ui/react/use-render';
3
5
  const cardVariants = tv({
4
6
  slots: {
5
7
  root: 'group/card flex flex-col rounded-2xl border bg-card text-card-foreground shadow-xs',
@@ -34,47 +36,73 @@ const cardVariants = tv({
34
36
  },
35
37
  },
36
38
  });
37
- function CardRoot({ size = 'default', as: Element = 'div', className, ...props }) {
39
+ function CardRoot({ size = 'default', as = 'div', render, className, ...props }) {
38
40
  const slots = cardVariants();
39
- const Tag = Element;
40
- return _jsx(Tag, { "data-slot": "card", "data-size": size, className: slots.root({ className }), ...props });
41
+ return useRender({
42
+ defaultTagName: as,
43
+ render,
44
+ props: mergeProps({ 'data-slot': 'card', 'data-size': size, className: slots.root({ className }) }, props),
45
+ });
41
46
  }
42
47
  CardRoot.displayName = 'Card';
43
48
  /** The title, the line under it, and the action beside them. */
44
- function CardHeader({ className, ...props }) {
49
+ function CardHeader({ className, render, ...props }) {
45
50
  const slots = cardVariants();
46
- return _jsx("div", { "data-slot": "card-header", className: slots.header({ className }), ...props });
51
+ return useRender({
52
+ defaultTagName: 'div',
53
+ render,
54
+ props: mergeProps({ 'data-slot': 'card-header', className: slots.header({ className }) }, props),
55
+ });
47
56
  }
48
57
  CardHeader.displayName = 'Card.Header';
49
58
  /** What the card is about. A heading, so the page outline includes it. */
50
- function CardTitle({ as: Element = 'h3', className, ...props }) {
59
+ function CardTitle({ as = 'h3', render, className, ...props }) {
51
60
  const slots = cardVariants();
52
- const Tag = Element;
53
- return _jsx(Tag, { "data-slot": "card-title", className: slots.title({ className }), ...props });
61
+ return useRender({
62
+ defaultTagName: as,
63
+ render,
64
+ props: mergeProps({ 'data-slot': 'card-title', className: slots.title({ className }) }, props),
65
+ });
54
66
  }
55
67
  CardTitle.displayName = 'Card.Title';
56
68
  /** A line under the title. */
57
- function CardDescription({ className, ...props }) {
69
+ function CardDescription({ className, render, ...props }) {
58
70
  const slots = cardVariants();
59
- return _jsx("p", { "data-slot": "card-description", className: slots.description({ className }), ...props });
71
+ return useRender({
72
+ defaultTagName: 'p',
73
+ render,
74
+ props: mergeProps({ 'data-slot': 'card-description', className: slots.description({ className }) }, props),
75
+ });
60
76
  }
61
77
  CardDescription.displayName = 'Card.Description';
62
78
  /** A control for the whole card, held in the header's top trailing corner. */
63
- function CardAction({ className, ...props }) {
79
+ function CardAction({ className, render, ...props }) {
64
80
  const slots = cardVariants();
65
- return _jsx("div", { "data-slot": "card-action", className: slots.action({ className }), ...props });
81
+ return useRender({
82
+ defaultTagName: 'div',
83
+ render,
84
+ props: mergeProps({ 'data-slot': 'card-action', className: slots.action({ className }) }, props),
85
+ });
66
86
  }
67
87
  CardAction.displayName = 'Card.Action';
68
88
  /** The body. */
69
- function CardContent({ className, ...props }) {
89
+ function CardContent({ className, render, ...props }) {
70
90
  const slots = cardVariants();
71
- return _jsx("div", { "data-slot": "card-content", className: slots.content({ className }), ...props });
91
+ return useRender({
92
+ defaultTagName: 'div',
93
+ render,
94
+ props: mergeProps({ 'data-slot': 'card-content', className: slots.content({ className }) }, props),
95
+ });
72
96
  }
73
97
  CardContent.displayName = 'Card.Content';
74
98
  /** The row at the bottom, usually the card's actions. */
75
- function CardFooter({ variant = 'plain', className, ...props }) {
99
+ function CardFooter({ variant = 'plain', className, render, ...props }) {
76
100
  const slots = cardVariants({ footer: variant });
77
- return _jsx("div", { "data-slot": "card-footer", className: slots.footer({ className }), ...props });
101
+ return useRender({
102
+ defaultTagName: 'div',
103
+ render,
104
+ props: mergeProps({ 'data-slot': 'card-footer', className: slots.footer({ className }) }, props),
105
+ });
78
106
  }
79
107
  CardFooter.displayName = 'Card.Footer';
80
108
  export const Card = Object.assign(CardRoot, {
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Checkbox — one choice that is on or off, usually among others that are too.
3
+ *
4
+ * ```tsx
5
+ * <Label>
6
+ * <Checkbox defaultChecked />
7
+ * <Label.Text>Email me a receipt</Label.Text>
8
+ * </Label>
9
+ *
10
+ * <Checkbox.Group defaultValue={['sms']} aria-label="Notify me by">
11
+ * <Label><Checkbox value="email" /><Label.Text>Email</Label.Text></Label>
12
+ * <Label><Checkbox value="sms" /><Label.Text>Text message</Label.Text></Label>
13
+ * </Checkbox.Group>
14
+ * ```
15
+ *
16
+ * ## The tick is drawn, not swapped in
17
+ *
18
+ * When it is checked, the tick is drawn as a line from its short end to its
19
+ * long one, the way a pen would make it, and the box fills behind it. Unchecked,
20
+ * the line goes back the way it came. A box that only changes colour is easy to
21
+ * miss in a long list; one that draws its mark is not. With reduced motion the
22
+ * tick appears at once.
23
+ *
24
+ * ## Some, but not all
25
+ *
26
+ * `indeterminate` draws a dash instead of a tick, for a box that stands for a
27
+ * group whose members are mixed. A `Checkbox.Group` with `allValues` does the
28
+ * bookkeeping: a checkbox in it marked `parent` is checked when every member
29
+ * is, a dash when some are, and checks or clears them all when pressed.
30
+ *
31
+ * ## Choosing a colour
32
+ *
33
+ * A checkbox fills with `primary`. `success`, `info` and `destructive` are for
34
+ * a box whose meaning is that colour — "I accept the risk" is `destructive`.
35
+ */
36
+ import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
37
+ import { CheckboxGroup as BaseCheckboxGroup } from '@base-ui/react/checkbox-group';
38
+ import type { CSSProperties } from 'react';
39
+ export type CheckboxSize = 'sm' | 'md' | 'lg';
40
+ export type CheckboxColor = 'primary' | 'success' | 'info' | 'destructive';
41
+ export interface CheckboxProps extends Omit<BaseCheckbox.Root.Props, 'className' | 'color'> {
42
+ /** Default `md`. */
43
+ size?: CheckboxSize;
44
+ /** The colour it fills with when checked. `primary` by default. */
45
+ color?: CheckboxColor;
46
+ /** What a screen reader calls it, when there is no visible label tied to it. */
47
+ label?: string;
48
+ className?: string;
49
+ style?: CSSProperties;
50
+ }
51
+ /**
52
+ * A checkbox. Takes `checked`/`onCheckedChange` or `defaultChecked`,
53
+ * `indeterminate`, and `name` and `value` for a form or a `Checkbox.Group`.
54
+ */
55
+ declare function CheckboxRoot({ size, color, label, className, ...props }: CheckboxProps): import("react").JSX.Element;
56
+ declare namespace CheckboxRoot {
57
+ var displayName: string;
58
+ }
59
+ export interface CheckboxGroupProps extends Omit<BaseCheckboxGroup.Props, 'className'> {
60
+ className?: string;
61
+ }
62
+ /**
63
+ * Holds the values of the checkboxes inside it as one array. Takes
64
+ * `value`/`onValueChange` or `defaultValue`, and `allValues` for a `parent` box.
65
+ */
66
+ declare function CheckboxGroup({ className, ...props }: CheckboxGroupProps): import("react").JSX.Element;
67
+ declare namespace CheckboxGroup {
68
+ var displayName: string;
69
+ }
70
+ export declare const Checkbox: typeof CheckboxRoot & {
71
+ Group: typeof CheckboxGroup;
72
+ };
73
+ export { CheckboxGroup };
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Checkbox — one choice that is on or off, usually among others that are too.
4
+ *
5
+ * ```tsx
6
+ * <Label>
7
+ * <Checkbox defaultChecked />
8
+ * <Label.Text>Email me a receipt</Label.Text>
9
+ * </Label>
10
+ *
11
+ * <Checkbox.Group defaultValue={['sms']} aria-label="Notify me by">
12
+ * <Label><Checkbox value="email" /><Label.Text>Email</Label.Text></Label>
13
+ * <Label><Checkbox value="sms" /><Label.Text>Text message</Label.Text></Label>
14
+ * </Checkbox.Group>
15
+ * ```
16
+ *
17
+ * ## The tick is drawn, not swapped in
18
+ *
19
+ * When it is checked, the tick is drawn as a line from its short end to its
20
+ * long one, the way a pen would make it, and the box fills behind it. Unchecked,
21
+ * the line goes back the way it came. A box that only changes colour is easy to
22
+ * miss in a long list; one that draws its mark is not. With reduced motion the
23
+ * tick appears at once.
24
+ *
25
+ * ## Some, but not all
26
+ *
27
+ * `indeterminate` draws a dash instead of a tick, for a box that stands for a
28
+ * group whose members are mixed. A `Checkbox.Group` with `allValues` does the
29
+ * bookkeeping: a checkbox in it marked `parent` is checked when every member
30
+ * is, a dash when some are, and checks or clears them all when pressed.
31
+ *
32
+ * ## Choosing a colour
33
+ *
34
+ * A checkbox fills with `primary`. `success`, `info` and `destructive` are for
35
+ * a box whose meaning is that colour — "I accept the risk" is `destructive`.
36
+ */
37
+ import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
38
+ import { CheckboxGroup as BaseCheckboxGroup } from '@base-ui/react/checkbox-group';
39
+ import { tv } from 'tailwind-variants';
40
+ const checkboxVariants = tv({
41
+ slots: {
42
+ root: [
43
+ 'group/checkbox relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-[0.3rem] border outline-none',
44
+ 'border-input bg-background shadow-xs dark:bg-input/30',
45
+ 'transition-[background-color,border-color,box-shadow] duration-150',
46
+ 'hover:not-data-disabled:border-[color-mix(in_oklab,var(--input),var(--foreground)_25%)]',
47
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
48
+ // Checked or mixed: the box fills with the colour, and the border goes with it.
49
+ 'data-checked:border-(--checkbox-color) data-checked:bg-(--checkbox-color)',
50
+ 'data-indeterminate:border-(--checkbox-color) data-indeterminate:bg-(--checkbox-color)',
51
+ 'aria-invalid:border-destructive aria-invalid:ring-destructive/20',
52
+ 'data-disabled:cursor-not-allowed data-disabled:opacity-50',
53
+ // A wider target than the box, so it is not a fiddle to hit on a phone.
54
+ "after:absolute after:-inset-2 after:content-['']",
55
+ ],
56
+ indicator: 'pointer-events-none absolute inset-0 flex items-center justify-center text-(--checkbox-mark)',
57
+ mark: 'size-[78%] overflow-visible',
58
+ // The tick is one path of length ~1; the dash offset draws and undraws it.
59
+ tick: [
60
+ '[stroke-dasharray:1] [stroke-dashoffset:1] transition-[stroke-dashoffset] duration-200 ease-out',
61
+ 'group-data-checked/checkbox:[stroke-dashoffset:0] group-data-checked/checkbox:delay-75',
62
+ 'motion-reduce:transition-none',
63
+ ],
64
+ dash: 'origin-center scale-x-0 transition-transform duration-150 group-data-indeterminate/checkbox:scale-x-100 motion-reduce:transition-none',
65
+ group: 'flex flex-col gap-3',
66
+ },
67
+ variants: {
68
+ size: {
69
+ sm: { root: 'size-3.5' },
70
+ md: { root: 'size-4' },
71
+ lg: { root: 'size-5 rounded-[0.375rem]' },
72
+ },
73
+ color: {
74
+ primary: { root: '[--checkbox-color:var(--primary)] [--checkbox-mark:var(--primary-foreground)]' },
75
+ success: { root: '[--checkbox-color:var(--success)] [--checkbox-mark:white]' },
76
+ info: { root: '[--checkbox-color:var(--info)] [--checkbox-mark:white]' },
77
+ destructive: { root: '[--checkbox-color:var(--destructive)] [--checkbox-mark:white]' },
78
+ },
79
+ },
80
+ defaultVariants: { size: 'md', color: 'primary' },
81
+ });
82
+ /**
83
+ * A checkbox. Takes `checked`/`onCheckedChange` or `defaultChecked`,
84
+ * `indeterminate`, and `name` and `value` for a form or a `Checkbox.Group`.
85
+ */
86
+ function CheckboxRoot({ size = 'md', color, label, className, ...props }) {
87
+ const slots = checkboxVariants({ size, color });
88
+ return (_jsx(BaseCheckbox.Root, { "data-slot": "checkbox", "data-size": size, "aria-label": label, className: slots.root({ className }), ...props, children: _jsx(BaseCheckbox.Indicator, { "data-slot": "checkbox-indicator", keepMounted: true, className: slots.indicator(), children: _jsxs("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", fill: "none", className: slots.mark(), children: [_jsx("path", { className: slots.tick(), pathLength: 1, d: "M3.5 8.5 6.6 11.5 12.5 4.75", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { className: slots.dash(), d: "M4 8h8", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round" })] }) }) }));
89
+ }
90
+ CheckboxRoot.displayName = 'Checkbox';
91
+ /**
92
+ * Holds the values of the checkboxes inside it as one array. Takes
93
+ * `value`/`onValueChange` or `defaultValue`, and `allValues` for a `parent` box.
94
+ */
95
+ function CheckboxGroup({ className, ...props }) {
96
+ return _jsx(BaseCheckboxGroup, { "data-slot": "checkbox-group", className: checkboxVariants().group({ className }), ...props });
97
+ }
98
+ CheckboxGroup.displayName = 'Checkbox.Group';
99
+ export const Checkbox = Object.assign(CheckboxRoot, { Group: CheckboxGroup });
100
+ export { CheckboxGroup };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Chip — a small pill that can be pressed: a filter turned on and off, or a value taken back out.
3
+ *
4
+ * ```tsx
5
+ * <Chip.Group defaultValue={['open']} aria-label="Show">
6
+ * <Chip value="open">Open</Chip>
7
+ * <Chip value="merged">Merged</Chip>
8
+ * <Chip value="closed">Closed</Chip>
9
+ * </Chip.Group>
10
+ *
11
+ * <Chip onRemove={() => drop('design')}>design</Chip>
12
+ * ```
13
+ *
14
+ * ## Two jobs, one shape
15
+ *
16
+ * A chip on its own, or in a `Chip.Group`, is a toggle: pressing it turns a
17
+ * filter on, and a tick slides in at its start while the pill fills, so a
18
+ * row of them reads as a row of switches rather than a row of tags. The
19
+ * group holds the values that are on as one array; pass `multiple={false}`
20
+ * for a row where only one can be on.
21
+ *
22
+ * Given `onRemove`, a chip is a value that has been added — a recipient, a
23
+ * tag — and carries a small × to take it back out. It is not a toggle then,
24
+ * and pressing Backspace or Delete while its × has focus removes it too.
25
+ *
26
+ * For a label that only says what something is, and is never pressed, use
27
+ * Badge.
28
+ */
29
+ import { Toggle as BaseToggle } from '@base-ui/react/toggle';
30
+ import { ToggleGroup as BaseToggleGroup } from '@base-ui/react/toggle-group';
31
+ import type { ComponentProps, CSSProperties, ReactNode } from 'react';
32
+ export type ChipSize = 'sm' | 'md' | 'lg';
33
+ export type ChipColor = 'primary' | 'success' | 'info' | 'warning' | 'destructive';
34
+ interface ChipBaseProps {
35
+ /** Default `md`. */
36
+ size?: ChipSize;
37
+ /** The colour it takes when on. `primary` by default. */
38
+ color?: ChipColor;
39
+ className?: string;
40
+ style?: CSSProperties;
41
+ children?: ReactNode;
42
+ }
43
+ export interface ChipToggleProps extends ChipBaseProps, Omit<BaseToggle.Props, 'className' | 'color' | 'children' | 'style'> {
44
+ onRemove?: undefined;
45
+ }
46
+ export interface ChipRemovableProps extends ChipBaseProps, Omit<ComponentProps<'span'>, 'color' | 'children'> {
47
+ /**
48
+ * Makes the chip a value that can be taken out: it shows a × that calls
49
+ * this. A removable chip is not a toggle.
50
+ */
51
+ onRemove: () => void;
52
+ /** What the × is called to a screen reader. Default `Remove <text>`, when the text is a string. */
53
+ removeLabel?: string;
54
+ disabled?: boolean;
55
+ }
56
+ export type ChipProps = ChipToggleProps | ChipRemovableProps;
57
+ /**
58
+ * A toggle pill. Takes `pressed`/`onPressedChange` or `defaultPressed` on its
59
+ * own, and `value` inside a `Chip.Group`. Given `onRemove`, a removable value instead.
60
+ */
61
+ declare function ChipRoot(props: ChipProps): import("react").JSX.Element;
62
+ declare namespace ChipRoot {
63
+ var displayName: string;
64
+ }
65
+ export interface ChipGroupProps extends Omit<BaseToggleGroup.Props<string>, 'className'> {
66
+ className?: string;
67
+ }
68
+ /**
69
+ * A row of toggle chips whose values are held as one array. Several can be on
70
+ * at once; pass `multiple={false}` for one at a time.
71
+ */
72
+ declare function ChipGroup({ multiple, className, ...props }: ChipGroupProps): import("react").JSX.Element;
73
+ declare namespace ChipGroup {
74
+ var displayName: string;
75
+ }
76
+ export declare const Chip: typeof ChipRoot & {
77
+ Group: typeof ChipGroup;
78
+ };
79
+ export { ChipGroup };
@@ -0,0 +1,112 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Chip — a small pill that can be pressed: a filter turned on and off, or a value taken back out.
5
+ *
6
+ * ```tsx
7
+ * <Chip.Group defaultValue={['open']} aria-label="Show">
8
+ * <Chip value="open">Open</Chip>
9
+ * <Chip value="merged">Merged</Chip>
10
+ * <Chip value="closed">Closed</Chip>
11
+ * </Chip.Group>
12
+ *
13
+ * <Chip onRemove={() => drop('design')}>design</Chip>
14
+ * ```
15
+ *
16
+ * ## Two jobs, one shape
17
+ *
18
+ * A chip on its own, or in a `Chip.Group`, is a toggle: pressing it turns a
19
+ * filter on, and a tick slides in at its start while the pill fills, so a
20
+ * row of them reads as a row of switches rather than a row of tags. The
21
+ * group holds the values that are on as one array; pass `multiple={false}`
22
+ * for a row where only one can be on.
23
+ *
24
+ * Given `onRemove`, a chip is a value that has been added — a recipient, a
25
+ * tag — and carries a small × to take it back out. It is not a toggle then,
26
+ * and pressing Backspace or Delete while its × has focus removes it too.
27
+ *
28
+ * For a label that only says what something is, and is never pressed, use
29
+ * Badge.
30
+ */
31
+ import { Toggle as BaseToggle } from '@base-ui/react/toggle';
32
+ import { ToggleGroup as BaseToggleGroup } from '@base-ui/react/toggle-group';
33
+ import { tv } from 'tailwind-variants';
34
+ const chipVariants = tv({
35
+ slots: {
36
+ root: [
37
+ 'group/chip inline-flex shrink-0 items-center whitespace-nowrap rounded-full border font-medium outline-none select-none',
38
+ 'border-border bg-background text-foreground shadow-xs dark:bg-input/30',
39
+ 'transition-[background-color,border-color,color,padding] duration-200',
40
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
41
+ 'data-disabled:pointer-events-none data-disabled:opacity-50',
42
+ '[&_svg:not([class*=size-])]:size-3.5 [&_svg]:shrink-0',
43
+ ],
44
+ // A toggle: tinted in its colour when on.
45
+ toggle: [
46
+ 'cursor-pointer hover:bg-muted/60',
47
+ 'data-pressed:border-[color-mix(in_oklab,var(--chip-color)_40%,var(--background))]',
48
+ 'data-pressed:bg-[color-mix(in_oklab,var(--chip-color)_12%,var(--background))] dark:data-pressed:bg-[color-mix(in_oklab,var(--chip-color)_20%,var(--background))]',
49
+ 'data-pressed:text-[color-mix(in_oklab,var(--chip-color),var(--foreground)_35%)]',
50
+ ],
51
+ // The tick's box opens from nothing, so the label slides over to make room for it.
52
+ check: [
53
+ 'grid w-0 overflow-hidden opacity-0 transition-[width,opacity,margin] duration-200 ease-out',
54
+ 'group-data-pressed/chip:w-3.5 group-data-pressed/chip:opacity-100 group-data-pressed/chip:me-1',
55
+ 'motion-reduce:transition-none',
56
+ ],
57
+ remove: [
58
+ 'relative -me-1 ms-1 inline-flex size-4 cursor-pointer items-center justify-center rounded-full text-muted-foreground outline-none',
59
+ 'hover:bg-foreground/10 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring',
60
+ // A bigger target than the mark.
61
+ "after:absolute after:-inset-1.5 after:content-['']",
62
+ ],
63
+ group: 'flex flex-wrap gap-2',
64
+ },
65
+ variants: {
66
+ size: {
67
+ sm: { root: 'h-6 px-2 text-xs' },
68
+ md: { root: 'h-7 px-2.5 text-[0.8125rem]' },
69
+ lg: { root: 'h-8 px-3 text-sm' },
70
+ },
71
+ color: {
72
+ primary: { root: '[--chip-color:var(--primary)]' },
73
+ success: { root: '[--chip-color:var(--success)]' },
74
+ info: { root: '[--chip-color:var(--info)]' },
75
+ warning: { root: '[--chip-color:var(--warning)]' },
76
+ destructive: { root: '[--chip-color:var(--destructive)]' },
77
+ },
78
+ },
79
+ defaultVariants: { size: 'md', color: 'primary' },
80
+ });
81
+ /**
82
+ * A toggle pill. Takes `pressed`/`onPressedChange` or `defaultPressed` on its
83
+ * own, and `value` inside a `Chip.Group`. Given `onRemove`, a removable value instead.
84
+ */
85
+ function ChipRoot(props) {
86
+ if (props.onRemove) {
87
+ const { size = 'md', color, className, children, onRemove, removeLabel, disabled, ...rest } = props;
88
+ const slots = chipVariants({ size, color });
89
+ const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove');
90
+ const onKeyDown = (event) => {
91
+ if (event.key === 'Backspace' || event.key === 'Delete') {
92
+ event.preventDefault();
93
+ onRemove();
94
+ }
95
+ };
96
+ return (_jsxs("span", { "data-slot": "chip", "data-removable": "", "data-disabled": disabled ? '' : undefined, className: slots.root({ className }), ...rest, children: [children, _jsx("button", { type: "button", "data-slot": "chip-remove", "aria-label": label, disabled: disabled, onClick: onRemove, onKeyDown: onKeyDown, className: slots.remove(), children: _jsx("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", fill: "none", className: "size-3", children: _jsx("path", { d: "M4.5 4.5l7 7m0-7l-7 7", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round" }) }) })] }));
97
+ }
98
+ const { size = 'md', color, className, children, onRemove: _onRemove, ...rest } = props;
99
+ const slots = chipVariants({ size, color });
100
+ return (_jsxs(BaseToggle, { "data-slot": "chip", "data-size": size, className: slots.root({ className: [slots.toggle(), className] }), ...rest, children: [_jsx("span", { "data-slot": "chip-check", "aria-hidden": "true", className: slots.check(), children: _jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-3.5", children: _jsx("path", { d: "M3.5 8.5 6.6 11.5 12.5 4.75", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }) }), children] }));
101
+ }
102
+ ChipRoot.displayName = 'Chip';
103
+ /**
104
+ * A row of toggle chips whose values are held as one array. Several can be on
105
+ * at once; pass `multiple={false}` for one at a time.
106
+ */
107
+ function ChipGroup({ multiple = true, className, ...props }) {
108
+ return (_jsx(BaseToggleGroup, { "data-slot": "chip-group", multiple: multiple, className: chipVariants().group({ className }), ...props }));
109
+ }
110
+ ChipGroup.displayName = 'Chip.Group';
111
+ export const Chip = Object.assign(ChipRoot, { Group: ChipGroup });
112
+ export { ChipGroup };
@@ -28,6 +28,12 @@
28
28
  import { Radio } from '@base-ui/react/radio';
29
29
  import { RadioGroup } from '@base-ui/react/radio-group';
30
30
  import { type ComponentProps, type CSSProperties, type ReactNode } from 'react';
31
+ import { useRender } from '@base-ui/react/use-render';
32
+ import type { ElementType as RenderElementType } from 'react';
33
+ /** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
34
+ type RenderProp<Tag extends RenderElementType> = {
35
+ render?: useRender.ComponentProps<Tag>['render'];
36
+ };
31
37
  export type ChoiceCardsColor = 'primary' | 'info' | 'success' | 'warning' | 'destructive';
32
38
  declare const choiceCardsVariants: import("tailwind-variants").TVReturnType<{
33
39
  color: {
@@ -81,7 +87,7 @@ declare const choiceCardsVariants: import("tailwind-variants").TVReturnType<{
81
87
  text: string;
82
88
  label: string;
83
89
  description: string;
84
- }, import("tailwind-variants").TVReturnType<{
90
+ }, import("tailwind-variants").TVReturnTypeLike<{
85
91
  color: {
86
92
  primary: {
87
93
  root: string;
@@ -107,7 +113,7 @@ declare const choiceCardsVariants: import("tailwind-variants").TVReturnType<{
107
113
  text: string;
108
114
  label: string;
109
115
  description: string;
110
- }, undefined, unknown, unknown, undefined>>;
116
+ }>>;
111
117
  export interface ChoiceCardsProps extends Omit<RadioGroup.Props<string>, 'className' | 'style' | 'children'> {
112
118
  /** How many cards stand in a row. Default: one per item, up to four. */
113
119
  columns?: number;
@@ -140,21 +146,21 @@ declare namespace ChoiceCardsItem {
140
146
  export interface ChoiceCardsMediaProps extends ComponentProps<'span'> {
141
147
  }
142
148
  /** The picture of the option: a drawing, an icon or an image, centred in a rounded frame. */
143
- declare function ChoiceCardsMedia({ className, ...props }: ChoiceCardsMediaProps): import("react").JSX.Element;
149
+ declare function ChoiceCardsMedia({ className, render, ...props }: ChoiceCardsMediaProps & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
144
150
  declare namespace ChoiceCardsMedia {
145
151
  var displayName: string;
146
152
  }
147
153
  export interface ChoiceCardsLabelProps extends ComponentProps<'span'> {
148
154
  }
149
155
  /** The option's name, under its picture. It is what a screen reader calls the card. */
150
- declare function ChoiceCardsLabel({ className, ...props }: ChoiceCardsLabelProps): import("react").JSX.Element;
156
+ declare function ChoiceCardsLabel({ className, render, ...props }: ChoiceCardsLabelProps & RenderProp<'span'>): import("react").JSX.Element;
151
157
  declare namespace ChoiceCardsLabel {
152
158
  var displayName: string;
153
159
  }
154
160
  export interface ChoiceCardsDescriptionProps extends ComponentProps<'span'> {
155
161
  }
156
162
  /** A line under the label: what the option costs or does. */
157
- declare function ChoiceCardsDescription({ className, ...props }: ChoiceCardsDescriptionProps): import("react").JSX.Element;
163
+ declare function ChoiceCardsDescription({ className, render, ...props }: ChoiceCardsDescriptionProps & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
158
164
  declare namespace ChoiceCardsDescription {
159
165
  var displayName: string;
160
166
  }
@@ -31,6 +31,8 @@ import { Radio } from '@base-ui/react/radio';
31
31
  import { RadioGroup } from '@base-ui/react/radio-group';
32
32
  import { createContext, useContext, useId } from 'react';
33
33
  import { tv } from 'tailwind-variants';
34
+ import { mergeProps } from '@base-ui/react/merge-props';
35
+ import { useRender } from '@base-ui/react/use-render';
34
36
  const SPRING = 'ease-[cubic-bezier(0.3,1.35,0.45,1)]';
35
37
  const choiceCardsVariants = tv({
36
38
  slots: {
@@ -97,9 +99,13 @@ function ChoiceCardsItem({ className, children, ...props }) {
97
99
  }
98
100
  ChoiceCardsItem.displayName = 'ChoiceCards.Item';
99
101
  /** The picture of the option: a drawing, an icon or an image, centred in a rounded frame. */
100
- function ChoiceCardsMedia({ className, ...props }) {
102
+ function ChoiceCardsMedia({ className, render, ...props }) {
101
103
  const slots = choiceCardsVariants();
102
- return _jsx("span", { "aria-hidden": true, "data-slot": "choice-cards-media", className: slots.media({ className }), ...props });
104
+ return useRender({
105
+ defaultTagName: 'span',
106
+ render,
107
+ props: mergeProps({ 'aria-hidden': true, 'data-slot': 'choice-cards-media', className: slots.media({ className }) }, props),
108
+ });
103
109
  }
104
110
  ChoiceCardsMedia.displayName = 'ChoiceCards.Media';
105
111
  /** Gathers the label and description under the picture, with the card's padding. */
@@ -110,17 +116,30 @@ function useItem(part) {
110
116
  return item;
111
117
  }
112
118
  /** The option's name, under its picture. It is what a screen reader calls the card. */
113
- function ChoiceCardsLabel({ className, ...props }) {
119
+ function ChoiceCardsLabel({ className, render, ...props }) {
114
120
  const { labelId } = useItem('ChoiceCards.Label');
115
121
  const slots = choiceCardsVariants();
116
- return (_jsx("span", { "data-slot": "choice-cards-text", className: slots.text(), children: _jsx("span", { id: labelId, "data-slot": "choice-cards-label", className: slots.label({ className }), ...props }) }));
122
+ const element = useRender({
123
+ defaultTagName: 'span',
124
+ render,
125
+ props: mergeProps({ id: labelId, 'data-slot': 'choice-cards-label', className: slots.label({ className }) }, props),
126
+ });
127
+ return (_jsx("span", { "data-slot": "choice-cards-text", className: slots.text(), children: element }));
117
128
  }
118
129
  ChoiceCardsLabel.displayName = 'ChoiceCards.Label';
119
130
  /** A line under the label: what the option costs or does. */
120
- function ChoiceCardsDescription({ className, ...props }) {
131
+ function ChoiceCardsDescription({ className, render, ...props }) {
121
132
  const { descriptionId } = useItem('ChoiceCards.Description');
122
133
  const slots = choiceCardsVariants();
123
- return (_jsx("span", { id: descriptionId, "data-slot": "choice-cards-description", className: slots.description({ className: ['-mt-1.5 px-1.5 pb-1', className] }), ...props }));
134
+ return useRender({
135
+ defaultTagName: 'span',
136
+ render,
137
+ props: mergeProps({
138
+ id: descriptionId,
139
+ 'data-slot': 'choice-cards-description',
140
+ className: slots.description({ className: ['-mt-1.5 px-1.5 pb-1', className] }),
141
+ }, props),
142
+ });
124
143
  }
125
144
  ChoiceCardsDescription.displayName = 'ChoiceCards.Description';
126
145
  export const ChoiceCards = Object.assign(ChoiceCardsRoot, {