@tea-ui/core 0.1.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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/card.d.ts +60 -0
- package/dist/card.js +116 -0
- package/dist/card.js.map +1 -0
- package/dist/feedback/alert.d.ts +57 -0
- package/dist/feedback/alert.js +104 -0
- package/dist/feedback/alert.js.map +1 -0
- package/dist/feedback/badge.d.ts +34 -0
- package/dist/feedback/badge.js +62 -0
- package/dist/feedback/badge.js.map +1 -0
- package/dist/feedback/index.d.ts +13 -0
- package/dist/feedback/index.js +62 -0
- package/dist/feedback/index.js.map +1 -0
- package/dist/feedback/progress.d.ts +25 -0
- package/dist/feedback/progress.js +49 -0
- package/dist/feedback/progress.js.map +1 -0
- package/dist/feedback/skeleton.d.ts +38 -0
- package/dist/feedback/skeleton.js +49 -0
- package/dist/feedback/skeleton.js.map +1 -0
- package/dist/feedback/spinner.d.ts +31 -0
- package/dist/feedback/spinner.js +55 -0
- package/dist/feedback/spinner.js.map +1 -0
- package/dist/feedback/status.d.ts +63 -0
- package/dist/feedback/status.js +156 -0
- package/dist/feedback/status.js.map +1 -0
- package/dist/feedback/toast.d.ts +72 -0
- package/dist/feedback/toast.js +211 -0
- package/dist/feedback/toast.js.map +1 -0
- package/dist/format/index.d.ts +63 -0
- package/dist/format/index.js +84 -0
- package/dist/format/index.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +163 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs/button-group.d.ts +70 -0
- package/dist/inputs/button-group.js +59 -0
- package/dist/inputs/button-group.js.map +1 -0
- package/dist/inputs/button.d.ts +52 -0
- package/dist/inputs/button.js +84 -0
- package/dist/inputs/button.js.map +1 -0
- package/dist/inputs/checkbox.d.ts +48 -0
- package/dist/inputs/checkbox.js +74 -0
- package/dist/inputs/checkbox.js.map +1 -0
- package/dist/inputs/combobox.d.ts +77 -0
- package/dist/inputs/combobox.js +273 -0
- package/dist/inputs/combobox.js.map +1 -0
- package/dist/inputs/field.d.ts +220 -0
- package/dist/inputs/field.js +183 -0
- package/dist/inputs/field.js.map +1 -0
- package/dist/inputs/icon-button.d.ts +44 -0
- package/dist/inputs/icon-button.js +30 -0
- package/dist/inputs/icon-button.js.map +1 -0
- package/dist/inputs/index.d.ts +22 -0
- package/dist/inputs/index.js +92 -0
- package/dist/inputs/index.js.map +1 -0
- package/dist/inputs/input-group.d.ts +77 -0
- package/dist/inputs/input-group.js +76 -0
- package/dist/inputs/input-group.js.map +1 -0
- package/dist/inputs/input.d.ts +58 -0
- package/dist/inputs/input.js +68 -0
- package/dist/inputs/input.js.map +1 -0
- package/dist/inputs/label.d.ts +47 -0
- package/dist/inputs/label.js +29 -0
- package/dist/inputs/label.js.map +1 -0
- package/dist/inputs/number-input.d.ts +62 -0
- package/dist/inputs/number-input.js +86 -0
- package/dist/inputs/number-input.js.map +1 -0
- package/dist/inputs/password-input.d.ts +66 -0
- package/dist/inputs/password-input.js +73 -0
- package/dist/inputs/password-input.js.map +1 -0
- package/dist/inputs/radio.d.ts +66 -0
- package/dist/inputs/radio.js +96 -0
- package/dist/inputs/radio.js.map +1 -0
- package/dist/inputs/search-input.d.ts +66 -0
- package/dist/inputs/search-input.js +76 -0
- package/dist/inputs/search-input.js.map +1 -0
- package/dist/inputs/select.d.ts +65 -0
- package/dist/inputs/select.js +139 -0
- package/dist/inputs/select.js.map +1 -0
- package/dist/inputs/slider.d.ts +67 -0
- package/dist/inputs/slider.js +107 -0
- package/dist/inputs/slider.js.map +1 -0
- package/dist/inputs/switch.d.ts +42 -0
- package/dist/inputs/switch.js +69 -0
- package/dist/inputs/switch.js.map +1 -0
- package/dist/inputs/textarea.d.ts +51 -0
- package/dist/inputs/textarea.js +82 -0
- package/dist/inputs/textarea.js.map +1 -0
- package/dist/inputs/toggle.d.ts +57 -0
- package/dist/inputs/toggle.js +91 -0
- package/dist/inputs/toggle.js.map +1 -0
- package/dist/internal/index.d.ts +5 -0
- package/dist/internal/index.js +19 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/internal/refs.d.ts +31 -0
- package/dist/internal/refs.js +34 -0
- package/dist/internal/refs.js.map +1 -0
- package/dist/internal/slot.d.ts +76 -0
- package/dist/internal/slot.js +40 -0
- package/dist/internal/slot.js.map +1 -0
- package/dist/internal/use-controllable-state.d.ts +25 -0
- package/dist/internal/use-controllable-state.js +38 -0
- package/dist/internal/use-controllable-state.js.map +1 -0
- package/dist/layout/index.d.ts +131 -0
- package/dist/layout/index.js +212 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/navigation/index.d.ts +114 -0
- package/dist/navigation/index.js +523 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/overlays/alert-dialog.d.ts +75 -0
- package/dist/overlays/alert-dialog.js +163 -0
- package/dist/overlays/alert-dialog.js.map +1 -0
- package/dist/overlays/dialog.d.ts +44 -0
- package/dist/overlays/dialog.js +121 -0
- package/dist/overlays/dialog.js.map +1 -0
- package/dist/overlays/drawer.d.ts +49 -0
- package/dist/overlays/drawer.js +125 -0
- package/dist/overlays/drawer.js.map +1 -0
- package/dist/overlays/dropdown-menu.d.ts +53 -0
- package/dist/overlays/dropdown-menu.js +192 -0
- package/dist/overlays/dropdown-menu.js.map +1 -0
- package/dist/overlays/index.d.ts +10 -0
- package/dist/overlays/index.js +169 -0
- package/dist/overlays/index.js.map +1 -0
- package/dist/overlays/popover.d.ts +58 -0
- package/dist/overlays/popover.js +105 -0
- package/dist/overlays/popover.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/typography/index.d.ts +79 -0
- package/dist/typography/index.js +280 -0
- package/dist/typography/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Slider as SliderPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "@tea-ui/utils";
|
|
5
|
+
import { dataSlot, stateAttributes, useControllableState } from "../internal/index.js";
|
|
6
|
+
import { useFieldControlProps } from "./field.js";
|
|
7
|
+
const Slider = React.forwardRef(function Slider2({
|
|
8
|
+
className,
|
|
9
|
+
label,
|
|
10
|
+
value,
|
|
11
|
+
defaultValue = [0],
|
|
12
|
+
onValueChange,
|
|
13
|
+
min = 0,
|
|
14
|
+
max = 100,
|
|
15
|
+
step,
|
|
16
|
+
orientation = "horizontal",
|
|
17
|
+
formatValue,
|
|
18
|
+
showValue = false,
|
|
19
|
+
disabled,
|
|
20
|
+
...props
|
|
21
|
+
}, ref) {
|
|
22
|
+
const field = useFieldControlProps();
|
|
23
|
+
const isDisabled = field.disabled ?? disabled ?? false;
|
|
24
|
+
const [values, setValues] = useControllableState({
|
|
25
|
+
value,
|
|
26
|
+
defaultValue,
|
|
27
|
+
onChange: onValueChange,
|
|
28
|
+
name: "Slider"
|
|
29
|
+
});
|
|
30
|
+
const format = React.useCallback(
|
|
31
|
+
(next) => formatValue ? formatValue(next) : String(next),
|
|
32
|
+
[formatValue]
|
|
33
|
+
);
|
|
34
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 flex-col gap-1", className), ...dataSlot("slider"), children: [
|
|
35
|
+
showValue ? (
|
|
36
|
+
// `aria-hidden`: the value is already on the thumb as `aria-valuetext`,
|
|
37
|
+
// and a second copy is a second announcement.
|
|
38
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-micro tabular-nums text-fg-muted", children: format(values[0] ?? min) })
|
|
39
|
+
) : null,
|
|
40
|
+
/* @__PURE__ */ jsxs(
|
|
41
|
+
SliderPrimitive.Root,
|
|
42
|
+
{
|
|
43
|
+
ref,
|
|
44
|
+
value: values,
|
|
45
|
+
onValueChange: setValues,
|
|
46
|
+
min,
|
|
47
|
+
max,
|
|
48
|
+
step,
|
|
49
|
+
orientation,
|
|
50
|
+
disabled: isDisabled,
|
|
51
|
+
"aria-label": label,
|
|
52
|
+
"aria-labelledby": props["aria-labelledby"],
|
|
53
|
+
"aria-describedby": field["aria-describedby"] ?? props["aria-describedby"],
|
|
54
|
+
"data-orientation": orientation,
|
|
55
|
+
"data-tea-touch": true,
|
|
56
|
+
className: cn(
|
|
57
|
+
"relative flex touch-none select-none items-center",
|
|
58
|
+
"data-[orientation=horizontal]:h-4 data-[orientation=horizontal]:w-full",
|
|
59
|
+
"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-4",
|
|
60
|
+
"data-[disabled]:opacity-50"
|
|
61
|
+
),
|
|
62
|
+
...stateAttributes({ disabled: isDisabled, orientation }),
|
|
63
|
+
...dataSlot("slider-root"),
|
|
64
|
+
...props,
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsx(
|
|
67
|
+
SliderPrimitive.Track,
|
|
68
|
+
{
|
|
69
|
+
className: cn(
|
|
70
|
+
"relative grow overflow-hidden rounded-none bg-surface-3",
|
|
71
|
+
"data-[orientation=horizontal]:h-1 data-[orientation=horizontal]:w-full",
|
|
72
|
+
"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1"
|
|
73
|
+
),
|
|
74
|
+
...dataSlot("slider-track"),
|
|
75
|
+
children: /* @__PURE__ */ jsx(
|
|
76
|
+
SliderPrimitive.Range,
|
|
77
|
+
{
|
|
78
|
+
className: cn("absolute bg-primary", "data-[orientation=horizontal]:h-full"),
|
|
79
|
+
...dataSlot("slider-range")
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
values.map((_, index) => /* @__PURE__ */ jsx(
|
|
85
|
+
SliderPrimitive.Thumb,
|
|
86
|
+
{
|
|
87
|
+
"aria-label": values.length > 1 ? `${label} ${index + 1}` : void 0,
|
|
88
|
+
className: cn(
|
|
89
|
+
// Pill is a documented exception: a slider thumb is a control
|
|
90
|
+
// marker, and a square one is invisible against the track.
|
|
91
|
+
"block rounded-pill border border-primary bg-primary",
|
|
92
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
93
|
+
"disabled:cursor-not-allowed"
|
|
94
|
+
),
|
|
95
|
+
...dataSlot("slider-thumb")
|
|
96
|
+
},
|
|
97
|
+
index
|
|
98
|
+
))
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] });
|
|
103
|
+
});
|
|
104
|
+
export {
|
|
105
|
+
Slider
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=slider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inputs/slider.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Slider as SliderPrimitive } from \"radix-ui\";\nimport { cn } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes, useControllableState } from \"../internal\";\nimport { useFieldControlProps } from \"./field\";\n\n/**\n * TEA UI — Slider.\n *\n * A slider, and the only TEA UI control with no sensible default value: a\n * slider with no name is a `<div>` a screen reader announces as \"slider\", and\n * a voice-control user cannot say which one they mean. `label` is required.\n *\n * Three things it gets right that a hand-rolled range never does:\n *\n * - **A real value text.** `aria-valuetext` is what a screen reader reads\n * instead of the raw number, so a percentage slider says \"40 Prozent\" and a\n * temperature slider says \"18 Grad\". `formatValue` is how you say it.\n * - **One tab stop, with arrows and Home/End.** Radix owns the roving focus;\n * the value is only reachable because the thumb is focusable.\n * - **The filled track is the value.** A range is readable with no colour\n * perception, and it is readable *by shape* — a line with a filled part and an\n * empty part, not two colours on one line.\n */\nexport interface SliderProps\n extends Omit<\n React.ComponentProps<typeof SliderPrimitive.Root>,\n \"value\" | \"defaultValue\" | \"onValueChange\" | \"orientation\" | \"dir\"\n > {\n /**\n * The accessible name. **Required** — there is no default, because a slider\n * whose name is guessed is a slider nobody can identify.\n */\n label: string;\n /** Controlled value. An array, because a range has two thumbs. */\n value?: number[] | undefined;\n /** Uncontrolled initial value. */\n defaultValue?: number[] | undefined;\n /** Called with the whole value array on every change. */\n onValueChange?: ((value: number[]) => void) | undefined;\n /** Lowest selectable value. */\n min?: number | undefined;\n /** Highest selectable value. */\n max?: number | undefined;\n /** Granularity. Left to Radix (1) when omitted. */\n step?: number | undefined;\n /** Which way the track runs. Decides the arrow keys and the fill direction. */\n orientation?: \"horizontal\" | \"vertical\";\n /**\n * Turn a number into the words a screen reader should hear, and into the\n * visible readout. Defaults to the bare number.\n */\n formatValue?: ((value: number) => string) | undefined;\n /** Show the formatted value next to the label. Off by default. */\n showValue?: boolean | undefined;\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <Field>\n * <FieldLabel>Speicherplatz</FieldLabel>\n * <Slider\n * label=\"Speicherplatz in Prozent\"\n * defaultValue={[40]}\n * showValue\n * formatValue={(v) => `${v} Prozent`}\n * />\n * </Field>\n * ```\n */\nexport const Slider = React.forwardRef<HTMLSpanElement, SliderProps>(function Slider(\n {\n className,\n label,\n value,\n defaultValue = [0],\n onValueChange,\n min = 0,\n max = 100,\n step,\n orientation = \"horizontal\",\n formatValue,\n showValue = false,\n disabled,\n ...props\n },\n ref,\n) {\n const field = useFieldControlProps();\n const isDisabled = field.disabled ?? disabled ?? false;\n\n const [values, setValues] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n name: \"Slider\",\n });\n\n const format = React.useCallback(\n (next: number) => (formatValue ? formatValue(next) : String(next)),\n [formatValue],\n );\n\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-1\", className)} {...dataSlot(\"slider\")}>\n {showValue ? (\n // `aria-hidden`: the value is already on the thumb as `aria-valuetext`,\n // and a second copy is a second announcement.\n <span aria-hidden=\"true\" className=\"text-micro tabular-nums text-fg-muted\">\n {format(values[0] ?? min)}\n </span>\n ) : null}\n <SliderPrimitive.Root\n ref={ref}\n value={values}\n onValueChange={setValues}\n min={min}\n max={max}\n step={step}\n orientation={orientation}\n disabled={isDisabled}\n aria-label={label}\n aria-labelledby={props[\"aria-labelledby\"]}\n aria-describedby={field[\"aria-describedby\"] ?? props[\"aria-describedby\"]}\n data-orientation={orientation}\n data-tea-touch\n className={cn(\n \"relative flex touch-none select-none items-center\",\n \"data-[orientation=horizontal]:h-4 data-[orientation=horizontal]:w-full\",\n \"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-4\",\n \"data-[disabled]:opacity-50\",\n )}\n {...stateAttributes({ disabled: isDisabled, orientation })}\n {...dataSlot(\"slider-root\")}\n {...props}\n >\n <SliderPrimitive.Track\n className={cn(\n \"relative grow overflow-hidden rounded-none bg-surface-3\",\n \"data-[orientation=horizontal]:h-1 data-[orientation=horizontal]:w-full\",\n \"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1\",\n )}\n {...dataSlot(\"slider-track\")}\n >\n <SliderPrimitive.Range\n className={cn(\"absolute bg-primary\", \"data-[orientation=horizontal]:h-full\")}\n {...dataSlot(\"slider-range\")}\n />\n </SliderPrimitive.Track>\n {values.map((_, index) => (\n <SliderPrimitive.Thumb\n key={index}\n aria-label={values.length > 1 ? `${label} ${index + 1}` : undefined}\n className={cn(\n // Pill is a documented exception: a slider thumb is a control\n // marker, and a square one is invisible against the track.\n \"block rounded-pill border border-primary bg-primary\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n \"disabled:cursor-not-allowed\",\n )}\n {...dataSlot(\"slider-thumb\")}\n />\n ))}\n </SliderPrimitive.Root>\n </div>\n );\n});\n"],"mappings":"AA+GQ,cAIF,YAJE;AA/GP,YAAY,WAAW;AACxB,SAAS,UAAU,uBAAuB;AAC1C,SAAS,UAAU;AAEnB,SAAS,UAAU,iBAAiB,4BAA4B;AAChE,SAAS,4BAA4B;AAoE9B,MAAM,SAAS,MAAM,WAAyC,SAASA,QAC5E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,CAAC,CAAC;AAAA,EACjB;AAAA,EACA,MAAM;AAAA,EACN,MAAM;AAAA,EACN;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GACA,KACA;AACA,QAAM,QAAQ,qBAAqB;AACnC,QAAM,aAAa,MAAM,YAAY,YAAY;AAEjD,QAAM,CAAC,QAAQ,SAAS,IAAI,qBAAqB;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AAED,QAAM,SAAS,MAAM;AAAA,IACnB,CAAC,SAAkB,cAAc,YAAY,IAAI,IAAI,OAAO,IAAI;AAAA,IAChE,CAAC,WAAW;AAAA,EACd;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,+BAA+B,SAAS,GAAI,GAAG,SAAS,QAAQ,GAChF;AAAA;AAAA;AAAA;AAAA,MAGC,oBAAC,UAAK,eAAY,QAAO,WAAU,yCAChC,iBAAO,OAAO,CAAC,KAAK,GAAG,GAC1B;AAAA,QACE;AAAA,IACJ;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC;AAAA,QACA,OAAO;AAAA,QACP,eAAe;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,cAAY;AAAA,QACZ,mBAAiB,MAAM,iBAAiB;AAAA,QACxC,oBAAkB,MAAM,kBAAkB,KAAK,MAAM,kBAAkB;AAAA,QACvE,oBAAkB;AAAA,QAClB,kBAAc;AAAA,QACd,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG,gBAAgB,EAAE,UAAU,YAAY,YAAY,CAAC;AAAA,QACxD,GAAG,SAAS,aAAa;AAAA,QACzB,GAAG;AAAA,QAEJ;AAAA;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,cACC,GAAG,SAAS,cAAc;AAAA,cAE3B;AAAA,gBAAC,gBAAgB;AAAA,gBAAhB;AAAA,kBACC,WAAW,GAAG,uBAAuB,sCAAsC;AAAA,kBAC1E,GAAG,SAAS,cAAc;AAAA;AAAA,cAC7B;AAAA;AAAA,UACF;AAAA,UACC,OAAO,IAAI,CAAC,GAAG,UACd;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cAEC,cAAY,OAAO,SAAS,IAAI,GAAG,KAAK,IAAI,QAAQ,CAAC,KAAK;AAAA,cAC1D,WAAW;AAAA;AAAA;AAAA,gBAGT;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,cACC,GAAG,SAAS,cAAc;AAAA;AAAA,YATtB;AAAA,UAUP,CACD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ,CAAC;","names":["Slider"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Switch as Switch$1 } from 'radix-ui';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TEA UI — Switch.
|
|
6
|
+
*
|
|
7
|
+
* An on/off control, for a setting that takes effect immediately. The
|
|
8
|
+
* distinction from `Checkbox` is not visual, it is semantic: a checkbox is a
|
|
9
|
+
* value submitted with a form, a switch is a setting that is already in force.
|
|
10
|
+
* Rendering one as the other teaches users the wrong expectation, so both exist.
|
|
11
|
+
*
|
|
12
|
+
* Radix's `Switch.Root` is a `<button role="switch">` with `aria-checked`, and
|
|
13
|
+
* it is labelable in exactly the same way a `<button>` is — so `FieldLabel`'s
|
|
14
|
+
* `htmlFor` gives it a name, and a standalone `label` wraps it.
|
|
15
|
+
*
|
|
16
|
+
* The state is also carried by the position of the thumb. A switch that only
|
|
17
|
+
* changed colour would be a switch nobody can read without colour vision.
|
|
18
|
+
*/
|
|
19
|
+
interface SwitchProps extends Omit<React.ComponentProps<typeof Switch$1.Root>, "checked" | "defaultChecked" | "children"> {
|
|
20
|
+
/** Controlled on/off state. */
|
|
21
|
+
checked?: boolean | undefined;
|
|
22
|
+
/** Uncontrolled initial state. */
|
|
23
|
+
defaultChecked?: boolean | undefined;
|
|
24
|
+
/** Called when the state changes, in both controlled and uncontrolled mode. */
|
|
25
|
+
onCheckedChange?: ((checked: boolean) => void) | undefined;
|
|
26
|
+
/** Text beside the switch, for a control used without a `Field`. */
|
|
27
|
+
label?: React.ReactNode | undefined;
|
|
28
|
+
className?: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <Field>
|
|
34
|
+
* <FieldLabel>Wartungsmodus</FieldLabel>
|
|
35
|
+
* <Switch defaultChecked />
|
|
36
|
+
* <FieldDescription>Neue Aufträge werden zurückgestellt.</FieldDescription>
|
|
37
|
+
* </Field>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare const Switch: React.ForwardRefExoticComponent<Omit<SwitchProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
41
|
+
|
|
42
|
+
export { Switch, type SwitchProps };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Switch as SwitchPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "@tea-ui/utils";
|
|
5
|
+
import { dataSlot, stateAttributes, useControllableState } from "../internal/index.js";
|
|
6
|
+
import { useFieldControlProps } from "./field.js";
|
|
7
|
+
const Switch = React.forwardRef(function Switch2({ className, label, checked, defaultChecked = false, onCheckedChange, disabled, required, ...props }, ref) {
|
|
8
|
+
const field = useFieldControlProps();
|
|
9
|
+
const isDisabled = field.disabled ?? disabled ?? false;
|
|
10
|
+
const isRequired = field["aria-required"] ?? required ?? false;
|
|
11
|
+
const [isChecked, setIsChecked] = useControllableState({
|
|
12
|
+
value: checked,
|
|
13
|
+
defaultValue: defaultChecked,
|
|
14
|
+
onChange: (next) => onCheckedChange?.(next),
|
|
15
|
+
name: "Switch"
|
|
16
|
+
});
|
|
17
|
+
const control = /* @__PURE__ */ jsx(
|
|
18
|
+
SwitchPrimitive.Root,
|
|
19
|
+
{
|
|
20
|
+
ref,
|
|
21
|
+
id: field.id,
|
|
22
|
+
checked: isChecked,
|
|
23
|
+
onCheckedChange: setIsChecked,
|
|
24
|
+
disabled: isDisabled,
|
|
25
|
+
required: isRequired,
|
|
26
|
+
"aria-invalid": field["aria-invalid"],
|
|
27
|
+
"aria-describedby": field["aria-describedby"],
|
|
28
|
+
"aria-required": isRequired || void 0,
|
|
29
|
+
value: isChecked ? "on" : "off",
|
|
30
|
+
"data-tea-touch": true,
|
|
31
|
+
className: cn(
|
|
32
|
+
// `rounded-pill` is a documented exception: a switch is a control
|
|
33
|
+
// marker, and a square switch reads as a checkbox.
|
|
34
|
+
"relative inline-flex h-4 w-7 shrink-0 items-center rounded-pill border border-line-strong",
|
|
35
|
+
"bg-surface-2 p-0.5",
|
|
36
|
+
"transition-[background-color,border-color] duration-[120ms] ease-standard",
|
|
37
|
+
"outline-none",
|
|
38
|
+
"focus-visible:border-ring-strong focus-visible:ring-2 focus-visible:ring-ring",
|
|
39
|
+
"data-[state=checked]:border-primary data-[state=checked]:bg-primary",
|
|
40
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
41
|
+
"aria-[invalid=true]:border-critical",
|
|
42
|
+
className
|
|
43
|
+
),
|
|
44
|
+
...stateAttributes({ disabled: isDisabled, invalid: field["aria-invalid"] === true, checked: isChecked }),
|
|
45
|
+
...dataSlot("switch"),
|
|
46
|
+
...props,
|
|
47
|
+
children: /* @__PURE__ */ jsx(
|
|
48
|
+
SwitchPrimitive.Thumb,
|
|
49
|
+
{
|
|
50
|
+
className: cn(
|
|
51
|
+
"pointer-events-none block size-3 rounded-pill bg-fg transition-transform duration-[120ms] ease-standard",
|
|
52
|
+
"data-[state=checked]:translate-x-3 data-[state=unchecked]:translate-x-0",
|
|
53
|
+
"data-[state=checked]:bg-primary-fg"
|
|
54
|
+
),
|
|
55
|
+
...dataSlot("switch-thumb")
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
if (label === void 0 || label === null || label === false) return control;
|
|
61
|
+
return /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-ui", ...dataSlot("switch-label"), children: [
|
|
62
|
+
control,
|
|
63
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
64
|
+
] });
|
|
65
|
+
});
|
|
66
|
+
export {
|
|
67
|
+
Switch
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inputs/switch.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Switch as SwitchPrimitive } from \"radix-ui\";\nimport { cn } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes, useControllableState } from \"../internal\";\nimport { useFieldControlProps } from \"./field\";\n\n/**\n * TEA UI — Switch.\n *\n * An on/off control, for a setting that takes effect immediately. The\n * distinction from `Checkbox` is not visual, it is semantic: a checkbox is a\n * value submitted with a form, a switch is a setting that is already in force.\n * Rendering one as the other teaches users the wrong expectation, so both exist.\n *\n * Radix's `Switch.Root` is a `<button role=\"switch\">` with `aria-checked`, and\n * it is labelable in exactly the same way a `<button>` is — so `FieldLabel`'s\n * `htmlFor` gives it a name, and a standalone `label` wraps it.\n *\n * The state is also carried by the position of the thumb. A switch that only\n * changed colour would be a switch nobody can read without colour vision.\n */\nexport interface SwitchProps\n extends Omit<\n React.ComponentProps<typeof SwitchPrimitive.Root>,\n \"checked\" | \"defaultChecked\" | \"children\"\n > {\n /** Controlled on/off state. */\n checked?: boolean | undefined;\n /** Uncontrolled initial state. */\n defaultChecked?: boolean | undefined;\n /** Called when the state changes, in both controlled and uncontrolled mode. */\n onCheckedChange?: ((checked: boolean) => void) | undefined;\n /** Text beside the switch, for a control used without a `Field`. */\n label?: React.ReactNode | undefined;\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <Field>\n * <FieldLabel>Wartungsmodus</FieldLabel>\n * <Switch defaultChecked />\n * <FieldDescription>Neue Aufträge werden zurückgestellt.</FieldDescription>\n * </Field>\n * ```\n */\nexport const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(function Switch(\n { className, label, checked, defaultChecked = false, onCheckedChange, disabled, required, ...props },\n ref,\n) {\n const field = useFieldControlProps();\n const isDisabled = field.disabled ?? disabled ?? false;\n const isRequired = field[\"aria-required\"] ?? required ?? false;\n\n const [isChecked, setIsChecked] = useControllableState({\n value: checked,\n defaultValue: defaultChecked,\n onChange: (next) => onCheckedChange?.(next),\n name: \"Switch\",\n });\n\n const control = (\n <SwitchPrimitive.Root\n ref={ref}\n id={field.id}\n checked={isChecked}\n onCheckedChange={setIsChecked}\n disabled={isDisabled}\n required={isRequired}\n aria-invalid={field[\"aria-invalid\"]}\n aria-describedby={field[\"aria-describedby\"]}\n aria-required={isRequired || undefined}\n value={isChecked ? \"on\" : \"off\"}\n data-tea-touch\n className={cn(\n // `rounded-pill` is a documented exception: a switch is a control\n // marker, and a square switch reads as a checkbox.\n \"relative inline-flex h-4 w-7 shrink-0 items-center rounded-pill border border-line-strong\",\n \"bg-surface-2 p-0.5\",\n \"transition-[background-color,border-color] duration-[120ms] ease-standard\",\n \"outline-none\",\n \"focus-visible:border-ring-strong focus-visible:ring-2 focus-visible:ring-ring\",\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"aria-[invalid=true]:border-critical\",\n className,\n )}\n {...stateAttributes({ disabled: isDisabled, invalid: field[\"aria-invalid\"] === true, checked: isChecked })}\n {...dataSlot(\"switch\")}\n {...props}\n >\n <SwitchPrimitive.Thumb\n // The thumb's position is the second signal: the switch is readable\n // with no colour perception at all.\n className={cn(\n \"pointer-events-none block size-3 rounded-pill bg-fg transition-transform duration-[120ms] ease-standard\",\n \"data-[state=checked]:translate-x-3 data-[state=unchecked]:translate-x-0\",\n \"data-[state=checked]:bg-primary-fg\",\n )}\n {...dataSlot(\"switch-thumb\")}\n />\n </SwitchPrimitive.Root>\n );\n\n if (label === undefined || label === null || label === false) return control;\n\n return (\n <label className=\"inline-flex items-center gap-2 text-ui\" {...dataSlot(\"switch-label\")}>\n {control}\n <span>{label}</span>\n </label>\n );\n});\n"],"mappings":"AA6FM,cAgBF,YAhBE;AA7FL,YAAY,WAAW;AACxB,SAAS,UAAU,uBAAuB;AAC1C,SAAS,UAAU;AAEnB,SAAS,UAAU,iBAAiB,4BAA4B;AAChE,SAAS,4BAA4B;AA2C9B,MAAM,SAAS,MAAM,WAA2C,SAASA,QAC9E,EAAE,WAAW,OAAO,SAAS,iBAAiB,OAAO,iBAAiB,UAAU,UAAU,GAAG,MAAM,GACnG,KACA;AACA,QAAM,QAAQ,qBAAqB;AACnC,QAAM,aAAa,MAAM,YAAY,YAAY;AACjD,QAAM,aAAa,MAAM,eAAe,KAAK,YAAY;AAEzD,QAAM,CAAC,WAAW,YAAY,IAAI,qBAAqB;AAAA,IACrD,OAAO;AAAA,IACP,cAAc;AAAA,IACd,UAAU,CAAC,SAAS,kBAAkB,IAAI;AAAA,IAC1C,MAAM;AAAA,EACR,CAAC;AAED,QAAM,UACJ;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,IAAI,MAAM;AAAA,MACV,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,UAAU;AAAA,MACV,UAAU;AAAA,MACV,gBAAc,MAAM,cAAc;AAAA,MAClC,oBAAkB,MAAM,kBAAkB;AAAA,MAC1C,iBAAe,cAAc;AAAA,MAC7B,OAAO,YAAY,OAAO;AAAA,MAC1B,kBAAc;AAAA,MACd,WAAW;AAAA;AAAA;AAAA,QAGT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG,gBAAgB,EAAE,UAAU,YAAY,SAAS,MAAM,cAAc,MAAM,MAAM,SAAS,UAAU,CAAC;AAAA,MACxG,GAAG,SAAS,QAAQ;AAAA,MACpB,GAAG;AAAA,MAEJ;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UAGC,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACC,GAAG,SAAS,cAAc;AAAA;AAAA,MAC7B;AAAA;AAAA,EACF;AAGF,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,MAAO,QAAO;AAErE,SACE,qBAAC,WAAM,WAAU,0CAA0C,GAAG,SAAS,cAAc,GAClF;AAAA;AAAA,IACD,oBAAC,UAAM,iBAAM;AAAA,KACf;AAEJ,CAAC;","names":["Switch"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { VariantProps } from '@tea-ui/utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TEA UI — Textarea.
|
|
7
|
+
*
|
|
8
|
+
* A multi-line text field with the same field wiring, the same density tokens
|
|
9
|
+
* and the same iOS-zoom floor as `Input`. It differs in exactly two ways:
|
|
10
|
+
*
|
|
11
|
+
* - it has no `size` variant, because its height comes from `rows`, not from
|
|
12
|
+
* the control token;
|
|
13
|
+
* - it can measure itself.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The base surface. Shared with `Input` deliberately: a text field and a text
|
|
17
|
+
* *area* that do not match pixel-for-pixel read as two different products.
|
|
18
|
+
*/
|
|
19
|
+
declare const textareaVariants: (props?: ({
|
|
20
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
21
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
22
|
+
interface TextareaProps extends Omit<React.ComponentProps<"textarea">, "size" | "color" | "rows">, VariantProps<typeof textareaVariants> {
|
|
23
|
+
/** Visible rows. The one way a textarea's height is set explicitly. */
|
|
24
|
+
rows?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Grow with the content instead of scrolling inside a fixed box.
|
|
27
|
+
*
|
|
28
|
+
* The recalculation sets the height to `scrollHeight`, which means the
|
|
29
|
+
* element's own scroll position is discarded on every keystroke — that is
|
|
30
|
+
* what makes naive auto-resize feel broken. The fix here is to only ever set
|
|
31
|
+
* an explicit height that is at least as large as the content, and to leave
|
|
32
|
+
* `overflow-y` alone, so a box the user has scrolled inside never jumps.
|
|
33
|
+
*/
|
|
34
|
+
autoResize?: boolean | undefined;
|
|
35
|
+
/** The value is not acceptable. See `Input`'s `invalid`. */
|
|
36
|
+
invalid?: boolean | undefined;
|
|
37
|
+
className?: string | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <Field invalid={!!error}>
|
|
43
|
+
* <FieldLabel>Notizen</FieldLabel>
|
|
44
|
+
* <Textarea rows={3} autoResize />
|
|
45
|
+
* <FieldError>{error}</FieldError>
|
|
46
|
+
* </Field>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaProps, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
|
|
50
|
+
|
|
51
|
+
export { Textarea, type TextareaProps, textareaVariants };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva, cn } from "@tea-ui/utils";
|
|
4
|
+
import { dataSlot, stateAttributes, withPrivateRef } from "../internal/index.js";
|
|
5
|
+
import { useFieldControlProps } from "./field.js";
|
|
6
|
+
import { useInputGroupContext } from "./input-group.js";
|
|
7
|
+
const textareaVariants = cva(
|
|
8
|
+
[
|
|
9
|
+
"w-full min-w-0 rounded-none border border-line bg-surface text-fg",
|
|
10
|
+
"px-[length:var(--tea-control-px)] py-2",
|
|
11
|
+
"transition-[background-color,border-color,color,box-shadow] duration-[120ms] ease-standard",
|
|
12
|
+
"outline-none",
|
|
13
|
+
"placeholder:text-fg-subtle",
|
|
14
|
+
"pointer-coarse:text-body",
|
|
15
|
+
"focus-visible:border-ring-strong focus-visible:ring-2 focus-visible:ring-ring",
|
|
16
|
+
"disabled:cursor-not-allowed disabled:border-line disabled:bg-surface-2 disabled:text-fg-subtle",
|
|
17
|
+
"read-only:cursor-default read-only:bg-surface-2 read-only:text-fg-muted",
|
|
18
|
+
"aria-[invalid=true]:border-critical aria-[invalid=true]:ring-critical"
|
|
19
|
+
],
|
|
20
|
+
{
|
|
21
|
+
variants: {
|
|
22
|
+
size: {
|
|
23
|
+
sm: "text-micro",
|
|
24
|
+
md: "text-ui",
|
|
25
|
+
lg: "text-ui"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
size: "md"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
const Textarea = React.forwardRef(function Textarea2({ className, size, rows, autoResize = false, invalid, disabled, readOnly, required, ...props }, ref) {
|
|
34
|
+
const field = useFieldControlProps();
|
|
35
|
+
const group = useInputGroupContext();
|
|
36
|
+
const [node, setNode] = React.useState(null);
|
|
37
|
+
const isInvalid = field["aria-invalid"] ?? invalid ?? false;
|
|
38
|
+
const isDisabled = field.disabled ?? disabled ?? false;
|
|
39
|
+
const isReadOnly = field.readOnly ?? readOnly ?? false;
|
|
40
|
+
const isRequired = field["aria-required"] ?? required ?? false;
|
|
41
|
+
React.useLayoutEffect(() => {
|
|
42
|
+
const element = node;
|
|
43
|
+
if (!element || !autoResize) return;
|
|
44
|
+
const scrollTop = element.scrollTop;
|
|
45
|
+
const wasOverflowing = element.scrollHeight > element.clientHeight;
|
|
46
|
+
element.style.height = "auto";
|
|
47
|
+
const next = element.scrollHeight;
|
|
48
|
+
element.style.height = `${Math.max(next, wasOverflowing ? element.clientHeight : 0)}px`;
|
|
49
|
+
if (wasOverflowing) element.scrollTop = scrollTop;
|
|
50
|
+
}, [node, autoResize, props.value]);
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
"textarea",
|
|
53
|
+
{
|
|
54
|
+
ref: withPrivateRef(ref, void 0, setNode),
|
|
55
|
+
rows,
|
|
56
|
+
id: field.id,
|
|
57
|
+
"aria-describedby": field["aria-describedby"],
|
|
58
|
+
"aria-invalid": isInvalid || void 0,
|
|
59
|
+
"aria-required": isRequired || void 0,
|
|
60
|
+
disabled: isDisabled || void 0,
|
|
61
|
+
readOnly: isReadOnly || void 0,
|
|
62
|
+
required: isRequired || void 0,
|
|
63
|
+
"data-tea-touch": true,
|
|
64
|
+
"data-auto-resize": autoResize || void 0,
|
|
65
|
+
...stateAttributes({ disabled: isDisabled, invalid: isInvalid, readOnly: isReadOnly }),
|
|
66
|
+
...dataSlot("textarea"),
|
|
67
|
+
className: cn(
|
|
68
|
+
textareaVariants({ size }),
|
|
69
|
+
// A textarea inside an `InputGroup` fills it rather than growing: the
|
|
70
|
+
// group's border is the box, and a growing control would tear it.
|
|
71
|
+
group ? "h-full resize-none overflow-auto border-0 bg-transparent px-0" : "resize-y",
|
|
72
|
+
className
|
|
73
|
+
),
|
|
74
|
+
...props
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
export {
|
|
79
|
+
Textarea,
|
|
80
|
+
textareaVariants
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=textarea.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inputs/textarea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, cn, type VariantProps } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes, withPrivateRef } from \"../internal\";\nimport { useFieldControlProps } from \"./field\";\nimport { useInputGroupContext } from \"./input-group\";\n\n/**\n * TEA UI — Textarea.\n *\n * A multi-line text field with the same field wiring, the same density tokens\n * and the same iOS-zoom floor as `Input`. It differs in exactly two ways:\n *\n * - it has no `size` variant, because its height comes from `rows`, not from\n * the control token;\n * - it can measure itself.\n */\n\n/**\n * The base surface. Shared with `Input` deliberately: a text field and a text\n * *area* that do not match pixel-for-pixel read as two different products.\n */\nexport const textareaVariants = cva(\n [\n \"w-full min-w-0 rounded-none border border-line bg-surface text-fg\",\n \"px-[length:var(--tea-control-px)] py-2\",\n \"transition-[background-color,border-color,color,box-shadow] duration-[120ms] ease-standard\",\n \"outline-none\",\n \"placeholder:text-fg-subtle\",\n \"pointer-coarse:text-body\",\n \"focus-visible:border-ring-strong focus-visible:ring-2 focus-visible:ring-ring\",\n \"disabled:cursor-not-allowed disabled:border-line disabled:bg-surface-2 disabled:text-fg-subtle\",\n \"read-only:cursor-default read-only:bg-surface-2 read-only:text-fg-muted\",\n \"aria-[invalid=true]:border-critical aria-[invalid=true]:ring-critical\",\n ],\n {\n variants: {\n size: {\n sm: \"text-micro\",\n md: \"text-ui\",\n lg: \"text-ui\",\n },\n },\n defaultVariants: {\n size: \"md\",\n },\n },\n);\n\nexport interface TextareaProps\n extends Omit<React.ComponentProps<\"textarea\">, \"size\" | \"color\" | \"rows\">,\n VariantProps<typeof textareaVariants> {\n /** Visible rows. The one way a textarea's height is set explicitly. */\n rows?: number;\n /**\n * Grow with the content instead of scrolling inside a fixed box.\n *\n * The recalculation sets the height to `scrollHeight`, which means the\n * element's own scroll position is discarded on every keystroke — that is\n * what makes naive auto-resize feel broken. The fix here is to only ever set\n * an explicit height that is at least as large as the content, and to leave\n * `overflow-y` alone, so a box the user has scrolled inside never jumps.\n */\n autoResize?: boolean | undefined;\n /** The value is not acceptable. See `Input`'s `invalid`. */\n invalid?: boolean | undefined;\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <Field invalid={!!error}>\n * <FieldLabel>Notizen</FieldLabel>\n * <Textarea rows={3} autoResize />\n * <FieldError>{error}</FieldError>\n * </Field>\n * ```\n */\nexport const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n { className, size, rows, autoResize = false, invalid, disabled, readOnly, required, ...props },\n ref,\n) {\n const field = useFieldControlProps();\n const group = useInputGroupContext();\n const [node, setNode] = React.useState<HTMLTextAreaElement | null>(null);\n\n const isInvalid = field[\"aria-invalid\"] ?? invalid ?? false;\n const isDisabled = field.disabled ?? disabled ?? false;\n const isReadOnly = field.readOnly ?? readOnly ?? false;\n const isRequired = field[\"aria-required\"] ?? required ?? false;\n\n // Measure after layout, and only when asked. `scrollHeight` is 0 until the\n // element is in the document, so this has to be an effect and not a render.\n React.useLayoutEffect(() => {\n const element = node;\n if (!element || !autoResize) return;\n\n // Remember the user's scroll offset across the measurement: a box the user\n // has scrolled to the bottom of must not spring back to the top because\n // the height was recomputed.\n const scrollTop = element.scrollTop;\n const wasOverflowing = element.scrollHeight > element.clientHeight;\n\n element.style.height = \"auto\";\n const next = element.scrollHeight;\n // Never shrink below what the user is looking at.\n element.style.height = `${Math.max(next, wasOverflowing ? element.clientHeight : 0)}px`;\n if (wasOverflowing) element.scrollTop = scrollTop;\n }, [node, autoResize, props.value]);\n\n return (\n <textarea\n ref={withPrivateRef(ref, undefined, setNode)}\n rows={rows}\n id={field.id}\n aria-describedby={field[\"aria-describedby\"]}\n aria-invalid={isInvalid || undefined}\n aria-required={isRequired || undefined}\n disabled={isDisabled || undefined}\n readOnly={isReadOnly || undefined}\n required={isRequired || undefined}\n data-tea-touch\n data-auto-resize={autoResize || undefined}\n {...stateAttributes({ disabled: isDisabled, invalid: isInvalid, readOnly: isReadOnly })}\n {...dataSlot(\"textarea\")}\n className={cn(\n textareaVariants({ size }),\n // A textarea inside an `InputGroup` fills it rather than growing: the\n // group's border is the box, and a growing control would tear it.\n group ? \"h-full resize-none overflow-auto border-0 bg-transparent px-0\" : \"resize-y\",\n className,\n )}\n {...props}\n />\n );\n});\n"],"mappings":"AAgHI;AAhHH,YAAY,WAAW;AACxB,SAAS,KAAK,UAA6B;AAE3C,SAAS,UAAU,iBAAiB,sBAAsB;AAC1D,SAAS,4BAA4B;AACrC,SAAS,4BAA4B;AAiB9B,MAAM,mBAAmB;AAAA,EAC9B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAgCO,MAAM,WAAW,MAAM,WAA+C,SAASA,UACpF,EAAE,WAAW,MAAM,MAAM,aAAa,OAAO,SAAS,UAAU,UAAU,UAAU,GAAG,MAAM,GAC7F,KACA;AACA,QAAM,QAAQ,qBAAqB;AACnC,QAAM,QAAQ,qBAAqB;AACnC,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAqC,IAAI;AAEvE,QAAM,YAAY,MAAM,cAAc,KAAK,WAAW;AACtD,QAAM,aAAa,MAAM,YAAY,YAAY;AACjD,QAAM,aAAa,MAAM,YAAY,YAAY;AACjD,QAAM,aAAa,MAAM,eAAe,KAAK,YAAY;AAIzD,QAAM,gBAAgB,MAAM;AAC1B,UAAM,UAAU;AAChB,QAAI,CAAC,WAAW,CAAC,WAAY;AAK7B,UAAM,YAAY,QAAQ;AAC1B,UAAM,iBAAiB,QAAQ,eAAe,QAAQ;AAEtD,YAAQ,MAAM,SAAS;AACvB,UAAM,OAAO,QAAQ;AAErB,YAAQ,MAAM,SAAS,GAAG,KAAK,IAAI,MAAM,iBAAiB,QAAQ,eAAe,CAAC,CAAC;AACnF,QAAI,eAAgB,SAAQ,YAAY;AAAA,EAC1C,GAAG,CAAC,MAAM,YAAY,MAAM,KAAK,CAAC;AAElC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,eAAe,KAAK,QAAW,OAAO;AAAA,MAC3C;AAAA,MACA,IAAI,MAAM;AAAA,MACV,oBAAkB,MAAM,kBAAkB;AAAA,MAC1C,gBAAc,aAAa;AAAA,MAC3B,iBAAe,cAAc;AAAA,MAC7B,UAAU,cAAc;AAAA,MACxB,UAAU,cAAc;AAAA,MACxB,UAAU,cAAc;AAAA,MACxB,kBAAc;AAAA,MACd,oBAAkB,cAAc;AAAA,MAC/B,GAAG,gBAAgB,EAAE,UAAU,YAAY,SAAS,WAAW,UAAU,WAAW,CAAC;AAAA,MACrF,GAAG,SAAS,UAAU;AAAA,MACvB,WAAW;AAAA,QACT,iBAAiB,EAAE,KAAK,CAAC;AAAA;AAAA;AAAA,QAGzB,QAAQ,kEAAkE;AAAA,QAC1E;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["Textarea"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Toggle as Toggle$1, ToggleGroup as ToggleGroup$1 } from 'radix-ui';
|
|
4
|
+
import { VariantProps } from '@tea-ui/utils';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* TEA UI — Toggle and ToggleGroup.
|
|
8
|
+
*
|
|
9
|
+
* A button that stays pressed. Two properties separate it from every other
|
|
10
|
+
* control here, and both follow from the fact that it *persists*:
|
|
11
|
+
*
|
|
12
|
+
* - **`aria-pressed` is a state, not a decoration.** A toggle whose only signal
|
|
13
|
+
* is a highlight is a plain button to a screen reader user: there is no way
|
|
14
|
+
* to find out whether the thing they are about to change is already changed.
|
|
15
|
+
* Radix sets it; the pressed *fill* is the second signal, so the state
|
|
16
|
+
* survives a monochrome screenshot and a colour-vision deficiency.
|
|
17
|
+
* - **It belongs in a group.** A lone toggle is usually a checkbox that got the
|
|
18
|
+
* wrong component, and the styling here assumes peers.
|
|
19
|
+
*
|
|
20
|
+
* `ToggleGroup` is single-select (`type="single"`, `role="radiogroup"`, arrows
|
|
21
|
+
* move and select) or multi-select (`type="multiple"`, `role="group"`, arrows
|
|
22
|
+
* move, Space toggles). Both are Radix, because roving focus across a group is
|
|
23
|
+
* three separate rules and a hand-rolled version gets one of them wrong.
|
|
24
|
+
*/
|
|
25
|
+
declare const toggleVariants: (props?: ({
|
|
26
|
+
variant?: "default" | "outline" | null | undefined;
|
|
27
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
28
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
29
|
+
interface ToggleProps extends Omit<React.ComponentProps<typeof Toggle$1.Root>, "className">, VariantProps<typeof toggleVariants> {
|
|
30
|
+
/** The pressed state, when controlled. */
|
|
31
|
+
pressed?: boolean | undefined;
|
|
32
|
+
/** Initial pressed state, when uncontrolled. */
|
|
33
|
+
defaultPressed?: boolean | undefined;
|
|
34
|
+
/** Called when the pressed state changes. */
|
|
35
|
+
onPressedChange?: ((pressed: boolean) => void) | undefined;
|
|
36
|
+
className?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <Toggle pressed={showGrid} onPressedChange={setShowGrid} aria-label="Gitternetz anzeigen">
|
|
42
|
+
* <Grid3x3 />
|
|
43
|
+
* </Toggle>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
declare const Toggle: React.ForwardRefExoticComponent<Omit<ToggleProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
47
|
+
interface ToggleGroupProps extends Omit<React.ComponentProps<typeof ToggleGroup$1.Root>, "type" | "orientation" | "dir" | "children">, VariantProps<typeof toggleVariants> {
|
|
48
|
+
/** `single` presses one at a time; `multiple` presses any number. */
|
|
49
|
+
type: "single" | "multiple";
|
|
50
|
+
children?: React.ReactNode;
|
|
51
|
+
/** Which way the items run. Decides the arrow keys. */
|
|
52
|
+
orientation?: "horizontal" | "vertical";
|
|
53
|
+
className?: string | undefined;
|
|
54
|
+
}
|
|
55
|
+
declare const ToggleGroup: React.ForwardRefExoticComponent<Omit<ToggleGroupProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
56
|
+
|
|
57
|
+
export { Toggle, ToggleGroup, type ToggleGroupProps, type ToggleProps, toggleVariants };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Toggle as TogglePrimitive, ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
|
|
4
|
+
import { cva, cn } from "@tea-ui/utils";
|
|
5
|
+
import { dataSlot, stateAttributes } from "../internal/index.js";
|
|
6
|
+
import { useButtonGroupContext } from "./button-group.js";
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
[
|
|
9
|
+
"relative inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap",
|
|
10
|
+
"rounded-none border border-transparent font-medium",
|
|
11
|
+
"transition-[background-color,border-color,color,box-shadow] duration-[120ms] ease-standard",
|
|
12
|
+
"outline-none",
|
|
13
|
+
"focus-visible:ring-2 focus-visible:ring-ring",
|
|
14
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
15
|
+
"active:translate-y-px"
|
|
16
|
+
],
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
variant: {
|
|
20
|
+
default: "border-line bg-surface-3 text-fg hover:bg-line",
|
|
21
|
+
outline: "border-line bg-transparent text-fg hover:border-line-strong hover:bg-surface-2"
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
sm: "h-[calc(var(--tea-control-h)-0.25rem)] px-[calc(var(--tea-control-px)-0.25rem)] text-micro",
|
|
25
|
+
md: "control-h px-[length:var(--tea-control-px)] text-ui",
|
|
26
|
+
lg: "h-[calc(var(--tea-control-h)+0.25rem)] px-[calc(var(--tea-control-px)+0.25rem)] text-ui"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: "outline",
|
|
31
|
+
size: "md"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
const ToggleGroupStyleContext = React.createContext(null);
|
|
36
|
+
const Toggle = React.forwardRef(function Toggle2({ className, variant, size, pressed, defaultPressed, onPressedChange, ...props }, ref) {
|
|
37
|
+
const buttonGroup = useButtonGroupContext();
|
|
38
|
+
const group = React.useContext(ToggleGroupStyleContext);
|
|
39
|
+
return /* @__PURE__ */ jsx(
|
|
40
|
+
TogglePrimitive.Root,
|
|
41
|
+
{
|
|
42
|
+
ref,
|
|
43
|
+
pressed,
|
|
44
|
+
defaultPressed,
|
|
45
|
+
onPressedChange,
|
|
46
|
+
"data-tea-touch": true,
|
|
47
|
+
"data-tea-grouped": buttonGroup ? buttonGroup.orientation : void 0,
|
|
48
|
+
className: cn(
|
|
49
|
+
toggleVariants({ variant: variant ?? group?.variant, size: size ?? group?.size }),
|
|
50
|
+
"data-[state=on]:border-primary data-[state=on]:bg-primary-subtle data-[state=on]:text-primary",
|
|
51
|
+
className
|
|
52
|
+
),
|
|
53
|
+
...dataSlot("toggle"),
|
|
54
|
+
...props
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
const ToggleGroupRootPrimitive = ToggleGroupPrimitive.Root;
|
|
59
|
+
const ToggleGroup = React.forwardRef(function ToggleGroup2({ children, className, type, orientation = "horizontal", variant, size, ...props }, ref) {
|
|
60
|
+
const style = React.useMemo(
|
|
61
|
+
() => ({ variant: variant ?? "outline", size: size ?? "md" }),
|
|
62
|
+
[variant, size]
|
|
63
|
+
);
|
|
64
|
+
return /* @__PURE__ */ jsx(ToggleGroupStyleContext.Provider, { value: style, children: /* @__PURE__ */ jsx(
|
|
65
|
+
ToggleGroupRootPrimitive,
|
|
66
|
+
{
|
|
67
|
+
...props,
|
|
68
|
+
ref,
|
|
69
|
+
type,
|
|
70
|
+
orientation,
|
|
71
|
+
"data-orientation": orientation,
|
|
72
|
+
"data-tea-touch": true,
|
|
73
|
+
className: cn(
|
|
74
|
+
"inline-flex items-stretch rounded-none",
|
|
75
|
+
orientation === "vertical" ? "flex-col" : "flex-row",
|
|
76
|
+
"[&>*+*]:-ms-px",
|
|
77
|
+
className
|
|
78
|
+
),
|
|
79
|
+
...stateAttributes({ orientation }),
|
|
80
|
+
...dataSlot("toggle-group"),
|
|
81
|
+
...props,
|
|
82
|
+
children
|
|
83
|
+
}
|
|
84
|
+
) });
|
|
85
|
+
});
|
|
86
|
+
export {
|
|
87
|
+
Toggle,
|
|
88
|
+
ToggleGroup,
|
|
89
|
+
toggleVariants
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=toggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/inputs/toggle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Toggle as TogglePrimitive, ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\";\nimport { cva, cn, type VariantProps } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes } from \"../internal\";\nimport { useButtonGroupContext } from \"./button-group\";\n\n/**\n * TEA UI — Toggle and ToggleGroup.\n *\n * A button that stays pressed. Two properties separate it from every other\n * control here, and both follow from the fact that it *persists*:\n *\n * - **`aria-pressed` is a state, not a decoration.** A toggle whose only signal\n * is a highlight is a plain button to a screen reader user: there is no way\n * to find out whether the thing they are about to change is already changed.\n * Radix sets it; the pressed *fill* is the second signal, so the state\n * survives a monochrome screenshot and a colour-vision deficiency.\n * - **It belongs in a group.** A lone toggle is usually a checkbox that got the\n * wrong component, and the styling here assumes peers.\n *\n * `ToggleGroup` is single-select (`type=\"single\"`, `role=\"radiogroup\"`, arrows\n * move and select) or multi-select (`type=\"multiple\"`, `role=\"group\"`, arrows\n * move, Space toggles). Both are Radix, because roving focus across a group is\n * three separate rules and a hand-rolled version gets one of them wrong.\n */\n\nexport const toggleVariants = cva(\n [\n \"relative inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"rounded-none border border-transparent font-medium\",\n \"transition-[background-color,border-color,color,box-shadow] duration-[120ms] ease-standard\",\n \"outline-none\",\n \"focus-visible:ring-2 focus-visible:ring-ring\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"active:translate-y-px\",\n ],\n {\n variants: {\n variant: {\n default: \"border-line bg-surface-3 text-fg hover:bg-line\",\n outline: \"border-line bg-transparent text-fg hover:border-line-strong hover:bg-surface-2\",\n },\n size: {\n sm: \"h-[calc(var(--tea-control-h)-0.25rem)] px-[calc(var(--tea-control-px)-0.25rem)] text-micro\",\n md: \"control-h px-[length:var(--tea-control-px)] text-ui\",\n lg: \"h-[calc(var(--tea-control-h)+0.25rem)] px-[calc(var(--tea-control-px)+0.25rem)] text-ui\",\n },\n },\n defaultVariants: {\n variant: \"outline\",\n size: \"md\",\n },\n },\n);\n\n/**\n * Group-wide styling, read by each `Toggle` in the group.\n *\n * A context rather than cloning the group's children: cloning means a `Toggle`\n * wrapped in a consumer's own component silently loses the group's size, which\n * is the kind of bug nobody reports and everybody sees.\n */\ninterface ToggleGroupStyleContextValue {\n variant: ToggleProps[\"variant\"];\n size: ToggleProps[\"size\"];\n}\n\nconst ToggleGroupStyleContext = React.createContext<ToggleGroupStyleContextValue | null>(null);\n\nexport interface ToggleProps\n extends Omit<React.ComponentProps<typeof TogglePrimitive.Root>, \"className\">,\n VariantProps<typeof toggleVariants> {\n /** The pressed state, when controlled. */\n pressed?: boolean | undefined;\n /** Initial pressed state, when uncontrolled. */\n defaultPressed?: boolean | undefined;\n /** Called when the pressed state changes. */\n onPressedChange?: ((pressed: boolean) => void) | undefined;\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <Toggle pressed={showGrid} onPressedChange={setShowGrid} aria-label=\"Gitternetz anzeigen\">\n * <Grid3x3 />\n * </Toggle>\n * ```\n */\nexport const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(function Toggle(\n { className, variant, size, pressed, defaultPressed, onPressedChange, ...props },\n ref,\n) {\n const buttonGroup = useButtonGroupContext();\n const group = React.useContext(ToggleGroupStyleContext);\n\n return (\n <TogglePrimitive.Root\n ref={ref}\n pressed={pressed}\n defaultPressed={defaultPressed}\n onPressedChange={onPressedChange}\n data-tea-touch\n data-tea-grouped={buttonGroup ? buttonGroup.orientation : undefined}\n className={cn(\n toggleVariants({ variant: variant ?? group?.variant, size: size ?? group?.size }),\n \"data-[state=on]:border-primary data-[state=on]:bg-primary-subtle data-[state=on]:text-primary\",\n className,\n )}\n {...dataSlot(\"toggle\")}\n {...props}\n />\n );\n});\n\nexport interface ToggleGroupProps\n extends Omit<\n React.ComponentProps<typeof ToggleGroupPrimitive.Root>,\n \"type\" | \"orientation\" | \"dir\" | \"children\"\n >,\n VariantProps<typeof toggleVariants> {\n /** `single` presses one at a time; `multiple` presses any number. */\n type: \"single\" | \"multiple\";\n children?: React.ReactNode;\n /** Which way the items run. Decides the arrow keys. */\n orientation?: \"horizontal\" | \"vertical\";\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <ToggleGroup type=\"multiple\" aria-label=\"Spalten\" size=\"sm\">\n * <Toggle value=\"cpu\">CPU</Toggle>\n * <Toggle value=\"ram\">RAM</Toggle>\n * </ToggleGroup>\n * ```\n */\n/**\n * Radix narrows `type` to a literal per variant, which makes a forwardable\n * `ToggleGroupProps[\"type\"]` union unrepresentable in its own prop type. The\n * component is cast once, here, rather than at every call site.\n */\nconst ToggleGroupRootPrimitive = ToggleGroupPrimitive.Root as unknown as React.ComponentType<\n React.ComponentProps<\"div\"> & Record<string, unknown>\n>;\nexport const ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(function ToggleGroup(\n { children, className, type, orientation = \"horizontal\", variant, size, ...props },\n ref,\n) {\n // Radix supplies the role: `radiogroup` for single, `group` for multiple.\n const style = React.useMemo<ToggleGroupStyleContextValue>(\n () => ({ variant: variant ?? \"outline\", size: size ?? \"md\" }),\n [variant, size],\n );\n\n return (\n <ToggleGroupStyleContext.Provider value={style}>\n {/* Radix types `type` as a per-variant literal, so a caller forwarding a\n `ToggleGroupProps[\"type\"]` union is genuinely correct — only the\n narrowing is unrepresentable in the type. Runtime behaviour is Radix's. */}\n <ToggleGroupRootPrimitive\n {...(props as React.ComponentProps<\"div\">)}\n ref={ref}\n type={type}\n orientation={orientation}\n data-orientation={orientation}\n data-tea-touch\n className={cn(\n \"inline-flex items-stretch rounded-none\",\n orientation === \"vertical\" ? \"flex-col\" : \"flex-row\",\n \"[&>*+*]:-ms-px\",\n className,\n )}\n {...stateAttributes({ orientation })}\n {...dataSlot(\"toggle-group\")}\n {...props}\n >\n {children}\n </ToggleGroupRootPrimitive>\n </ToggleGroupStyleContext.Provider>\n );\n});\n"],"mappings":"AAkGI;AAlGH,YAAY,WAAW;AACxB,SAAS,UAAU,iBAAiB,eAAe,4BAA4B;AAC/E,SAAS,KAAK,UAA6B;AAE3C,SAAS,UAAU,uBAAuB;AAC1C,SAAS,6BAA6B;AAsB/B,MAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAcA,MAAM,0BAA0B,MAAM,cAAmD,IAAI;AAsBtF,MAAM,SAAS,MAAM,WAA2C,SAASA,QAC9E,EAAE,WAAW,SAAS,MAAM,SAAS,gBAAgB,iBAAiB,GAAG,MAAM,GAC/E,KACA;AACA,QAAM,cAAc,sBAAsB;AAC1C,QAAM,QAAQ,MAAM,WAAW,uBAAuB;AAEtD,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAc;AAAA,MACd,oBAAkB,cAAc,YAAY,cAAc;AAAA,MAC1D,WAAW;AAAA,QACT,eAAe,EAAE,SAAS,WAAW,OAAO,SAAS,MAAM,QAAQ,OAAO,KAAK,CAAC;AAAA,QAChF;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG,SAAS,QAAQ;AAAA,MACpB,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;AA8BD,MAAM,2BAA2B,qBAAqB;AAG/C,MAAM,cAAc,MAAM,WAA6C,SAASC,aACrF,EAAE,UAAU,WAAW,MAAM,cAAc,cAAc,SAAS,MAAM,GAAG,MAAM,GACjF,KACA;AAEA,QAAM,QAAQ,MAAM;AAAA,IAClB,OAAO,EAAE,SAAS,WAAW,WAAW,MAAM,QAAQ,KAAK;AAAA,IAC3D,CAAC,SAAS,IAAI;AAAA,EAChB;AAEA,SACE,oBAAC,wBAAwB,UAAxB,EAAiC,OAAO,OAIvC;AAAA,IAAC;AAAA;AAAA,MACE,GAAI;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAkB;AAAA,MAClB,kBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aAAa,aAAa;AAAA,QAC1C;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG,gBAAgB,EAAE,YAAY,CAAC;AAAA,MAClC,GAAG,SAAS,cAAc;AAAA,MAC1B,GAAG;AAAA,MAEH;AAAA;AAAA,EACH,GACF;AAEJ,CAAC;","names":["Toggle","ToggleGroup"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { UseControllableStateParams, useControllableState } from './use-controllable-state.js';
|
|
2
|
+
export { PossibleRef, composeRefs, setRefs, splitProps } from './refs.js';
|
|
3
|
+
export { RenderProps, SlotProps, StateAttributes, dataSlot, renderElement, stateAttributes, withPrivateRef } from './slot.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import '@tea-ui/utils';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useControllableState } from "./use-controllable-state.js";
|
|
2
|
+
import { composeRefs, setRefs, splitProps } from "./refs.js";
|
|
3
|
+
import {
|
|
4
|
+
dataSlot,
|
|
5
|
+
renderElement,
|
|
6
|
+
stateAttributes,
|
|
7
|
+
withPrivateRef
|
|
8
|
+
} from "./slot.js";
|
|
9
|
+
export {
|
|
10
|
+
composeRefs,
|
|
11
|
+
dataSlot,
|
|
12
|
+
renderElement,
|
|
13
|
+
setRefs,
|
|
14
|
+
splitProps,
|
|
15
|
+
stateAttributes,
|
|
16
|
+
useControllableState,
|
|
17
|
+
withPrivateRef
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/internal/index.ts"],"sourcesContent":["/**\n * TEA UI — internal helpers.\n *\n * NOT part of the public API. Nothing in here is exported from\n * `@tea-ui/core`, and nothing in here is covered by semver. It exists so the\n * public surface can stay small and intentional while the implementation stays\n * consistent.\n */\nexport { useControllableState, type UseControllableStateParams } from \"./use-controllable-state\";\nexport { composeRefs, setRefs, splitProps, type PossibleRef } from \"./refs\";\nexport {\n dataSlot,\n renderElement,\n stateAttributes,\n withPrivateRef,\n type RenderProps,\n type SlotProps,\n type StateAttributes,\n} from \"./slot\";\n"],"mappings":"AAQA,SAAS,4BAA6D;AACtE,SAAS,aAAa,SAAS,kBAAoC;AACnE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ref plumbing for DOM-backed components.
|
|
5
|
+
*
|
|
6
|
+
* Every interactive TEA UI component forwards its ref, and every TEA UI
|
|
7
|
+
* component accepts an external ref. That means every component needs the same
|
|
8
|
+
* three-way merge between its own ref, the forwarded ref, and any prop ref a
|
|
9
|
+
* Radix primitive hands to it. Doing it once here is the only way it is done
|
|
10
|
+
* consistently.
|
|
11
|
+
*/
|
|
12
|
+
type PossibleRef<T> = React.Ref<T> | undefined;
|
|
13
|
+
/** A ref that can hold several refs at once. */
|
|
14
|
+
declare function setRefs<T>(...refs: PossibleRef<T>[]): (value: T) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Merge a component's internal ref with a forwarded one into a single
|
|
17
|
+
* callback ref. Internal first, so a component always knows its own node.
|
|
18
|
+
*/
|
|
19
|
+
declare function composeRefs<T>(...refs: PossibleRef<T>[]): (value: T) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Split an incoming props object into "ours to handle" and "the consumer's",
|
|
22
|
+
* merging anything that is not ours forward. This is how a TEA UI wrapper stays
|
|
23
|
+
* transparent: unknown props are never swallowed, and every known prop is
|
|
24
|
+
* consumed exactly once.
|
|
25
|
+
*/
|
|
26
|
+
declare function splitProps<T extends Record<string, unknown>, K extends keyof T>(props: T, ownKeys: readonly K[]): {
|
|
27
|
+
own: Pick<T, K>;
|
|
28
|
+
rest: Omit<T, K>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { type PossibleRef, composeRefs, setRefs, splitProps };
|