@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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/dist/card.d.ts +60 -0
  4. package/dist/card.js +116 -0
  5. package/dist/card.js.map +1 -0
  6. package/dist/feedback/alert.d.ts +57 -0
  7. package/dist/feedback/alert.js +104 -0
  8. package/dist/feedback/alert.js.map +1 -0
  9. package/dist/feedback/badge.d.ts +34 -0
  10. package/dist/feedback/badge.js +62 -0
  11. package/dist/feedback/badge.js.map +1 -0
  12. package/dist/feedback/index.d.ts +13 -0
  13. package/dist/feedback/index.js +62 -0
  14. package/dist/feedback/index.js.map +1 -0
  15. package/dist/feedback/progress.d.ts +25 -0
  16. package/dist/feedback/progress.js +49 -0
  17. package/dist/feedback/progress.js.map +1 -0
  18. package/dist/feedback/skeleton.d.ts +38 -0
  19. package/dist/feedback/skeleton.js +49 -0
  20. package/dist/feedback/skeleton.js.map +1 -0
  21. package/dist/feedback/spinner.d.ts +31 -0
  22. package/dist/feedback/spinner.js +55 -0
  23. package/dist/feedback/spinner.js.map +1 -0
  24. package/dist/feedback/status.d.ts +63 -0
  25. package/dist/feedback/status.js +156 -0
  26. package/dist/feedback/status.js.map +1 -0
  27. package/dist/feedback/toast.d.ts +72 -0
  28. package/dist/feedback/toast.js +211 -0
  29. package/dist/feedback/toast.js.map +1 -0
  30. package/dist/format/index.d.ts +63 -0
  31. package/dist/format/index.js +84 -0
  32. package/dist/format/index.js.map +1 -0
  33. package/dist/index.d.ts +43 -0
  34. package/dist/index.js +163 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/inputs/button-group.d.ts +70 -0
  37. package/dist/inputs/button-group.js +59 -0
  38. package/dist/inputs/button-group.js.map +1 -0
  39. package/dist/inputs/button.d.ts +52 -0
  40. package/dist/inputs/button.js +84 -0
  41. package/dist/inputs/button.js.map +1 -0
  42. package/dist/inputs/checkbox.d.ts +48 -0
  43. package/dist/inputs/checkbox.js +74 -0
  44. package/dist/inputs/checkbox.js.map +1 -0
  45. package/dist/inputs/combobox.d.ts +77 -0
  46. package/dist/inputs/combobox.js +273 -0
  47. package/dist/inputs/combobox.js.map +1 -0
  48. package/dist/inputs/field.d.ts +220 -0
  49. package/dist/inputs/field.js +183 -0
  50. package/dist/inputs/field.js.map +1 -0
  51. package/dist/inputs/icon-button.d.ts +44 -0
  52. package/dist/inputs/icon-button.js +30 -0
  53. package/dist/inputs/icon-button.js.map +1 -0
  54. package/dist/inputs/index.d.ts +22 -0
  55. package/dist/inputs/index.js +92 -0
  56. package/dist/inputs/index.js.map +1 -0
  57. package/dist/inputs/input-group.d.ts +77 -0
  58. package/dist/inputs/input-group.js +76 -0
  59. package/dist/inputs/input-group.js.map +1 -0
  60. package/dist/inputs/input.d.ts +58 -0
  61. package/dist/inputs/input.js +68 -0
  62. package/dist/inputs/input.js.map +1 -0
  63. package/dist/inputs/label.d.ts +47 -0
  64. package/dist/inputs/label.js +29 -0
  65. package/dist/inputs/label.js.map +1 -0
  66. package/dist/inputs/number-input.d.ts +62 -0
  67. package/dist/inputs/number-input.js +86 -0
  68. package/dist/inputs/number-input.js.map +1 -0
  69. package/dist/inputs/password-input.d.ts +66 -0
  70. package/dist/inputs/password-input.js +73 -0
  71. package/dist/inputs/password-input.js.map +1 -0
  72. package/dist/inputs/radio.d.ts +66 -0
  73. package/dist/inputs/radio.js +96 -0
  74. package/dist/inputs/radio.js.map +1 -0
  75. package/dist/inputs/search-input.d.ts +66 -0
  76. package/dist/inputs/search-input.js +76 -0
  77. package/dist/inputs/search-input.js.map +1 -0
  78. package/dist/inputs/select.d.ts +65 -0
  79. package/dist/inputs/select.js +139 -0
  80. package/dist/inputs/select.js.map +1 -0
  81. package/dist/inputs/slider.d.ts +67 -0
  82. package/dist/inputs/slider.js +107 -0
  83. package/dist/inputs/slider.js.map +1 -0
  84. package/dist/inputs/switch.d.ts +42 -0
  85. package/dist/inputs/switch.js +69 -0
  86. package/dist/inputs/switch.js.map +1 -0
  87. package/dist/inputs/textarea.d.ts +51 -0
  88. package/dist/inputs/textarea.js +82 -0
  89. package/dist/inputs/textarea.js.map +1 -0
  90. package/dist/inputs/toggle.d.ts +57 -0
  91. package/dist/inputs/toggle.js +91 -0
  92. package/dist/inputs/toggle.js.map +1 -0
  93. package/dist/internal/index.d.ts +5 -0
  94. package/dist/internal/index.js +19 -0
  95. package/dist/internal/index.js.map +1 -0
  96. package/dist/internal/refs.d.ts +31 -0
  97. package/dist/internal/refs.js +34 -0
  98. package/dist/internal/refs.js.map +1 -0
  99. package/dist/internal/slot.d.ts +76 -0
  100. package/dist/internal/slot.js +40 -0
  101. package/dist/internal/slot.js.map +1 -0
  102. package/dist/internal/use-controllable-state.d.ts +25 -0
  103. package/dist/internal/use-controllable-state.js +38 -0
  104. package/dist/internal/use-controllable-state.js.map +1 -0
  105. package/dist/layout/index.d.ts +131 -0
  106. package/dist/layout/index.js +212 -0
  107. package/dist/layout/index.js.map +1 -0
  108. package/dist/navigation/index.d.ts +114 -0
  109. package/dist/navigation/index.js +523 -0
  110. package/dist/navigation/index.js.map +1 -0
  111. package/dist/overlays/alert-dialog.d.ts +75 -0
  112. package/dist/overlays/alert-dialog.js +163 -0
  113. package/dist/overlays/alert-dialog.js.map +1 -0
  114. package/dist/overlays/dialog.d.ts +44 -0
  115. package/dist/overlays/dialog.js +121 -0
  116. package/dist/overlays/dialog.js.map +1 -0
  117. package/dist/overlays/drawer.d.ts +49 -0
  118. package/dist/overlays/drawer.js +125 -0
  119. package/dist/overlays/drawer.js.map +1 -0
  120. package/dist/overlays/dropdown-menu.d.ts +53 -0
  121. package/dist/overlays/dropdown-menu.js +192 -0
  122. package/dist/overlays/dropdown-menu.js.map +1 -0
  123. package/dist/overlays/index.d.ts +10 -0
  124. package/dist/overlays/index.js +169 -0
  125. package/dist/overlays/index.js.map +1 -0
  126. package/dist/overlays/popover.d.ts +58 -0
  127. package/dist/overlays/popover.js +105 -0
  128. package/dist/overlays/popover.js.map +1 -0
  129. package/dist/styles.css +2 -0
  130. package/dist/typography/index.d.ts +79 -0
  131. package/dist/typography/index.js +280 -0
  132. package/dist/typography/index.js.map +1 -0
  133. package/package.json +60 -0
@@ -0,0 +1,96 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { RadioGroup as RadioGroupPrimitive } from "radix-ui";
4
+ import { cn } from "@tea-ui/utils";
5
+ import { dataSlot, stateAttributes } from "../internal/index.js";
6
+ import { useFieldControlProps } from "./field.js";
7
+ const RadioGroup = React.forwardRef(function RadioGroup2({
8
+ children,
9
+ className,
10
+ value,
11
+ defaultValue,
12
+ onValueChange,
13
+ orientation = "vertical",
14
+ label,
15
+ disabled,
16
+ required,
17
+ ...props
18
+ }, ref) {
19
+ const field = useFieldControlProps();
20
+ const isDisabled = field.disabled ?? disabled ?? false;
21
+ const isRequired = field["aria-required"] ?? required ?? false;
22
+ return /* @__PURE__ */ jsx(
23
+ RadioGroupPrimitive.Root,
24
+ {
25
+ ref,
26
+ ...dataSlot("radio-group"),
27
+ value,
28
+ defaultValue,
29
+ onValueChange,
30
+ orientation,
31
+ disabled: isDisabled,
32
+ required: isRequired,
33
+ "aria-label": label,
34
+ "aria-describedby": field["aria-describedby"],
35
+ "aria-invalid": field["aria-invalid"],
36
+ "aria-required": isRequired || void 0,
37
+ "data-orientation": orientation,
38
+ className: cn("flex min-w-0 gap-3", orientation === "vertical" ? "flex-col" : "flex-row", className),
39
+ ...stateAttributes({ disabled: isDisabled, orientation }),
40
+ ...props,
41
+ children
42
+ }
43
+ );
44
+ });
45
+ const Radio = React.forwardRef(function Radio2({ className, label, children, value, disabled, ...props }, ref) {
46
+ return /* @__PURE__ */ jsxs(
47
+ RadioGroupPrimitive.Item,
48
+ {
49
+ ref,
50
+ value,
51
+ disabled,
52
+ "data-tea-touch": true,
53
+ className: cn(
54
+ "group inline-flex items-center gap-2 text-ui",
55
+ "rounded-none",
56
+ "data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
57
+ className
58
+ ),
59
+ ...dataSlot("radio"),
60
+ ...props,
61
+ children: [
62
+ /* @__PURE__ */ jsx(
63
+ "span",
64
+ {
65
+ "aria-hidden": "true",
66
+ className: cn(
67
+ // `rounded-pill` is a documented exception: a radio control is a
68
+ // marker, and a square one reads as a checkbox.
69
+ "flex size-4 shrink-0 items-center justify-center rounded-pill border border-line-strong bg-surface",
70
+ "transition-[background-color,border-color,box-shadow] duration-[120ms] ease-standard",
71
+ "group-data-[state=checked]:border-primary group-data-[state=checked]:bg-primary",
72
+ "group-focus-visible:ring-2 group-focus-visible:ring-ring"
73
+ ),
74
+ children: /* @__PURE__ */ jsx(
75
+ "span",
76
+ {
77
+ className: cn(
78
+ "size-2 rounded-pill bg-primary-fg",
79
+ // The dot *is* the state, in shape as well as colour.
80
+ "scale-0 transition-transform duration-[120ms] ease-standard",
81
+ "group-data-[state=checked]:scale-100"
82
+ )
83
+ }
84
+ )
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsx("span", { className: "text-fg group-data-[state=checked]:font-medium", children: label ?? children })
88
+ ]
89
+ }
90
+ );
91
+ });
92
+ export {
93
+ Radio,
94
+ RadioGroup
95
+ };
96
+ //# sourceMappingURL=radio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inputs/radio.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport { cn } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes } from \"../internal\";\nimport { useFieldControlProps } from \"./field\";\n\n/**\n * TEA UI — RadioGroup and Radio.\n *\n * One choice out of a small set. The pair is a family for a reason: the\n * grouping *is* the semantics. `role=\"radiogroup\"` is what tells a screen reader\n * that the arrow keys move between the options, and that leaving the group\n * leaves the choice. A column of loose radios has neither.\n *\n * Two details that are easy to lose:\n *\n * - **Arrow-key navigation is not optional and is not ours.** Radix implements\n * it, and it is the only correct implementation: arrows move *and select*,\n * Tab enters and leaves the group as a single stop, and the roving tabindex\n * means the whole group is one tab stop rather than four. A hand-rolled\n * version gets one of those three wrong every time.\n * - **A radio with no group is a radio group of one.** The `Radio` reads the\n * group context and wires itself, so it cannot end up outside one.\n */\n\nexport interface RadioGroupProps\n extends Omit<\n React.ComponentProps<typeof RadioGroupPrimitive.Root>,\n \"value\" | \"defaultValue\" | \"onValueChange\" | \"orientation\" | \"dir\"\n > {\n /** Controlled value — the `value` of the selected `Radio`. */\n value?: string | undefined;\n /** Uncontrolled initial value. */\n defaultValue?: string | undefined;\n /** Called when the selection changes, in both controlled and uncontrolled mode. */\n onValueChange?: ((value: string) => void) | undefined;\n /** Layout direction. Decides the arrow keys and the row/column flow. */\n orientation?: \"horizontal\" | \"vertical\";\n /**\n * Name for the group. Give it one whenever the group is not already named by\n * a `FieldGroup` legend: a `role=\"radiogroup\"` with no name is an entry in the\n * structure list that says nothing. A `Field` + `FieldLabel` pair is *not* a\n * substitute here — a label cannot be associated with a `role=\"radiogroup\"`\n * container, only with each option.\n */\n label?: string | undefined;\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <FieldGroup legend=\"Zeitraum\">\n * <RadioGroup defaultValue=\"week\">\n * <Radio value=\"day\">Tag</Radio>\n * <Radio value=\"week\">Woche</Radio>\n * </RadioGroup>\n * </FieldGroup>\n * ```\n */\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(function RadioGroup(\n {\n children,\n className,\n value,\n defaultValue,\n onValueChange,\n orientation = \"vertical\",\n label,\n disabled,\n required,\n ...props\n },\n ref,\n) {\n const field = useFieldControlProps();\n const isDisabled = field.disabled ?? disabled ?? false;\n const isRequired = field[\"aria-required\"] ?? required ?? false;\n\n return (\n <RadioGroupPrimitive.Root\n ref={ref}\n {...dataSlot(\"radio-group\")}\n value={value}\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n orientation={orientation}\n disabled={isDisabled}\n required={isRequired}\n aria-label={label}\n aria-describedby={field[\"aria-describedby\"]}\n aria-invalid={field[\"aria-invalid\"]}\n aria-required={isRequired || undefined}\n data-orientation={orientation}\n className={cn(\"flex min-w-0 gap-3\", orientation === \"vertical\" ? \"flex-col\" : \"flex-row\", className)}\n {...stateAttributes({ disabled: isDisabled, orientation })}\n {...props}\n >\n {children}\n </RadioGroupPrimitive.Root>\n );\n});\n\nexport interface RadioProps\n extends Omit<React.ComponentProps<typeof RadioGroupPrimitive.Item>, \"children\"> {\n /** Text beside the control. Makes the option self-naming. */\n label?: React.ReactNode | undefined;\n children?: React.ReactNode;\n className?: string | undefined;\n}\n\n/**\n * One option. Takes `label` (or children) and wires itself to the surrounding\n * `RadioGroup` through Radix's own context, so `name` and selection cannot\n * disagree.\n */\nexport const Radio = React.forwardRef<HTMLButtonElement, RadioProps>(function Radio(\n { className, label, children, value, disabled, ...props },\n ref,\n) {\n return (\n <RadioGroupPrimitive.Item\n ref={ref}\n value={value}\n disabled={disabled}\n data-tea-touch\n className={cn(\n \"group inline-flex items-center gap-2 text-ui\",\n \"rounded-none\",\n \"data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50\",\n className,\n )}\n {...dataSlot(\"radio\")}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n // `rounded-pill` is a documented exception: a radio control is a\n // marker, and a square one reads as a checkbox.\n \"flex size-4 shrink-0 items-center justify-center rounded-pill border border-line-strong bg-surface\",\n \"transition-[background-color,border-color,box-shadow] duration-[120ms] ease-standard\",\n \"group-data-[state=checked]:border-primary group-data-[state=checked]:bg-primary\",\n \"group-focus-visible:ring-2 group-focus-visible:ring-ring\",\n )}\n >\n <span\n className={cn(\n \"size-2 rounded-pill bg-primary-fg\",\n // The dot *is* the state, in shape as well as colour.\n \"scale-0 transition-transform duration-[120ms] ease-standard\",\n \"group-data-[state=checked]:scale-100\",\n )}\n />\n </span>\n <span className=\"text-fg group-data-[state=checked]:font-medium\">{label ?? children}</span>\n </RadioGroupPrimitive.Item>\n );\n});\n"],"mappings":"AAiFI,cAyCA,YAzCA;AAjFH,YAAY,WAAW;AACxB,SAAS,cAAc,2BAA2B;AAClD,SAAS,UAAU;AAEnB,SAAS,UAAU,uBAAuB;AAC1C,SAAS,4BAA4B;AAwD9B,MAAM,aAAa,MAAM,WAA4C,SAASA,YACnF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GACA,KACA;AACA,QAAM,QAAQ,qBAAqB;AACnC,QAAM,aAAa,MAAM,YAAY,YAAY;AACjD,QAAM,aAAa,MAAM,eAAe,KAAK,YAAY;AAEzD,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC;AAAA,MACC,GAAG,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,UAAU;AAAA,MACV,cAAY;AAAA,MACZ,oBAAkB,MAAM,kBAAkB;AAAA,MAC1C,gBAAc,MAAM,cAAc;AAAA,MAClC,iBAAe,cAAc;AAAA,MAC7B,oBAAkB;AAAA,MAClB,WAAW,GAAG,sBAAsB,gBAAgB,aAAa,aAAa,YAAY,SAAS;AAAA,MAClG,GAAG,gBAAgB,EAAE,UAAU,YAAY,YAAY,CAAC;AAAA,MACxD,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ,CAAC;AAeM,MAAM,QAAQ,MAAM,WAA0C,SAASC,OAC5E,EAAE,WAAW,OAAO,UAAU,OAAO,UAAU,GAAG,MAAM,GACxD,KACA;AACA,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG,SAAS,OAAO;AAAA,MACnB,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAW;AAAA;AAAA;AAAA,cAGT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA;AAAA,kBAEA;AAAA,kBACA;AAAA,gBACF;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QACA,oBAAC,UAAK,WAAU,kDAAkD,mBAAS,UAAS;AAAA;AAAA;AAAA,EACtF;AAEJ,CAAC;","names":["RadioGroup","Radio"]}
@@ -0,0 +1,66 @@
1
+ import * as React from 'react';
2
+ import { InputProps } from './input.js';
3
+ import 'class-variance-authority/types';
4
+ import '@tea-ui/utils';
5
+
6
+ /**
7
+ * TEA UI — SearchInput.
8
+ *
9
+ * An input, a decorative search icon and a clear button, already wired
10
+ * together. The audit found three of these hand-typed, each with its own
11
+ * opinion about the icon's size and position, and not one clear button with a
12
+ * name.
13
+ *
14
+ * What this component decides once, for everybody:
15
+ *
16
+ * - **The icon is decorative.** A magnifier is not a name. It is
17
+ * `aria-hidden`, and the accessible name comes from `label` — required, for
18
+ * the same reason it is on `IconButton`.
19
+ * - **The clear button has a name, and it only exists when there is something
20
+ * to clear.** A permanently present, permanently disabled clear button is a
21
+ * target a user aims at twice.
22
+ * - **Enter submits; Escape is left alone.** Escape is the browser's "revert
23
+ * this field" gesture, and taking it over to close a search box strands the
24
+ * user mid-query with a field that looks empty and is not.
25
+ */
26
+ interface SearchInputProps extends Omit<InputProps, "value" | "defaultValue" | "onChange" | "type" | "size" | "onSubmit"> {
27
+ /** The accessible name. **Required** — the icon is not a label. */
28
+ label: string;
29
+ /** Controlled query. */
30
+ value?: string | undefined;
31
+ /** Uncontrolled initial query. */
32
+ defaultValue?: string | undefined;
33
+ /** Called on every keystroke, in both controlled and uncontrolled mode. */
34
+ onValueChange?: ((value: string) => void) | undefined;
35
+ /** Called when the user presses Enter with a non-empty query. */
36
+ onSubmit?: ((value: string) => void) | undefined;
37
+ /**
38
+ * A search is running. Swaps the icon for a spinner and sets `aria-busy`; it
39
+ * deliberately does **not** disable the field, because a user who is still
40
+ * typing the query must be able to finish typing it.
41
+ */
42
+ loading?: boolean | undefined;
43
+ /** Offer a clear button. On by default. */
44
+ clearable?: boolean | undefined;
45
+ /** Name for the clear button. Defaults to `COPY.actions.reset`. */
46
+ clearLabel?: string | undefined;
47
+ /** The control height. */
48
+ size?: InputProps["size"];
49
+ className?: string | undefined;
50
+ }
51
+ /**
52
+ * @example
53
+ * ```tsx
54
+ * <SearchInput
55
+ * label="Server durchsuchen"
56
+ * placeholder="Name, IP oder Hostname"
57
+ * value={query}
58
+ * onValueChange={setQuery}
59
+ * onSubmit={run}
60
+ * loading={isSearching}
61
+ * />
62
+ * ```
63
+ */
64
+ declare const SearchInput: React.ForwardRefExoticComponent<Omit<SearchInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
65
+
66
+ export { SearchInput, type SearchInputProps };
@@ -0,0 +1,76 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Search, X } from "@tea-ui/icons";
4
+ import { cn } from "@tea-ui/utils";
5
+ import { COPY } from "@tea-ui/ux-standards";
6
+ import { dataSlot, useControllableState, withPrivateRef } from "../internal/index.js";
7
+ import { Spinner } from "../feedback/spinner.js";
8
+ import { IconButton } from "./icon-button.js";
9
+ import { Input } from "./input.js";
10
+ import { InputGroup, InputGroupEnd, InputGroupStart } from "./input-group.js";
11
+ const SearchInput = React.forwardRef(
12
+ function SearchInput2({
13
+ label,
14
+ value,
15
+ defaultValue = "",
16
+ onValueChange,
17
+ onSubmit,
18
+ loading = false,
19
+ clearable = true,
20
+ clearLabel,
21
+ size,
22
+ disabled,
23
+ readOnly,
24
+ className,
25
+ ...props
26
+ }, ref) {
27
+ const [query, setQuery] = useControllableState({
28
+ value,
29
+ defaultValue,
30
+ onChange: onValueChange,
31
+ name: "SearchInput"
32
+ });
33
+ const inputRef = React.useRef(null);
34
+ const canClear = clearable && query.length > 0 && !loading && !disabled && !readOnly;
35
+ return /* @__PURE__ */ jsxs(InputGroup, { label, className: cn(className), ...dataSlot("search-input"), children: [
36
+ /* @__PURE__ */ jsx(InputGroupStart, { children: loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm", label: COPY.states.loading, className: "-ms-1" }) : /* @__PURE__ */ jsx(Search, { "aria-hidden": "true", className: "size-4" }) }),
37
+ /* @__PURE__ */ jsx(
38
+ Input,
39
+ {
40
+ ref: withPrivateRef(ref, void 0, inputRef),
41
+ type: "search",
42
+ size,
43
+ "aria-label": label,
44
+ "aria-busy": loading || void 0,
45
+ value: query,
46
+ onChange: (event) => setQuery(event.target.value),
47
+ onKeyDown: (event) => {
48
+ if (event.key === "Enter" && query.length > 0) {
49
+ event.preventDefault();
50
+ onSubmit?.(query);
51
+ }
52
+ },
53
+ disabled,
54
+ readOnly,
55
+ ...props
56
+ }
57
+ ),
58
+ canClear ? /* @__PURE__ */ jsx(InputGroupEnd, { children: /* @__PURE__ */ jsx(
59
+ IconButton,
60
+ {
61
+ label: clearLabel ?? COPY.actions.reset,
62
+ size: "sm",
63
+ onClick: () => {
64
+ setQuery("");
65
+ inputRef.current?.focus();
66
+ },
67
+ children: /* @__PURE__ */ jsx(X, {})
68
+ }
69
+ ) }) : null
70
+ ] });
71
+ }
72
+ );
73
+ export {
74
+ SearchInput
75
+ };
76
+ //# sourceMappingURL=search-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inputs/search-input.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Search, X } from \"@tea-ui/icons\";\nimport { cn } from \"@tea-ui/utils\";\nimport { COPY } from \"@tea-ui/ux-standards\";\n\nimport { dataSlot, useControllableState, withPrivateRef } from \"../internal\";\nimport { Spinner } from \"../feedback/spinner\";\nimport { IconButton } from \"./icon-button\";\nimport { Input, type InputProps } from \"./input\";\nimport { InputGroup, InputGroupEnd, InputGroupStart } from \"./input-group\";\n\n/**\n * TEA UI — SearchInput.\n *\n * An input, a decorative search icon and a clear button, already wired\n * together. The audit found three of these hand-typed, each with its own\n * opinion about the icon's size and position, and not one clear button with a\n * name.\n *\n * What this component decides once, for everybody:\n *\n * - **The icon is decorative.** A magnifier is not a name. It is\n * `aria-hidden`, and the accessible name comes from `label` — required, for\n * the same reason it is on `IconButton`.\n * - **The clear button has a name, and it only exists when there is something\n * to clear.** A permanently present, permanently disabled clear button is a\n * target a user aims at twice.\n * - **Enter submits; Escape is left alone.** Escape is the browser's \"revert\n * this field\" gesture, and taking it over to close a search box strands the\n * user mid-query with a field that looks empty and is not.\n */\nexport interface SearchInputProps\n extends Omit<InputProps, \"value\" | \"defaultValue\" | \"onChange\" | \"type\" | \"size\" | \"onSubmit\"> {\n /** The accessible name. **Required** — the icon is not a label. */\n label: string;\n /** Controlled query. */\n value?: string | undefined;\n /** Uncontrolled initial query. */\n defaultValue?: string | undefined;\n /** Called on every keystroke, in both controlled and uncontrolled mode. */\n onValueChange?: ((value: string) => void) | undefined;\n /** Called when the user presses Enter with a non-empty query. */\n onSubmit?: ((value: string) => void) | undefined;\n /**\n * A search is running. Swaps the icon for a spinner and sets `aria-busy`; it\n * deliberately does **not** disable the field, because a user who is still\n * typing the query must be able to finish typing it.\n */\n loading?: boolean | undefined;\n /** Offer a clear button. On by default. */\n clearable?: boolean | undefined;\n /** Name for the clear button. Defaults to `COPY.actions.reset`. */\n clearLabel?: string | undefined;\n /** The control height. */\n size?: InputProps[\"size\"];\n className?: string | undefined;\n}\n\n/**\n * @example\n * ```tsx\n * <SearchInput\n * label=\"Server durchsuchen\"\n * placeholder=\"Name, IP oder Hostname\"\n * value={query}\n * onValueChange={setQuery}\n * onSubmit={run}\n * loading={isSearching}\n * />\n * ```\n */\nexport const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(\n function SearchInput(\n {\n label,\n value,\n defaultValue = \"\",\n onValueChange,\n onSubmit,\n loading = false,\n clearable = true,\n clearLabel,\n size,\n disabled,\n readOnly,\n className,\n ...props\n },\n ref,\n ) {\n const [query, setQuery] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n name: \"SearchInput\",\n });\n\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const canClear = clearable && query.length > 0 && !loading && !disabled && !readOnly;\n\n return (\n <InputGroup label={label} className={cn(className)} {...dataSlot(\"search-input\")}>\n <InputGroupStart>\n {loading ? (\n <Spinner size=\"sm\" label={COPY.states.loading} className=\"-ms-1\" />\n ) : (\n <Search aria-hidden=\"true\" className=\"size-4\" />\n )}\n </InputGroupStart>\n {/* `type=\"search\"` already carries the implicit `searchbox` role and the\n native clear affordance; the role is not restated. */}\n <Input\n ref={withPrivateRef(ref, undefined, inputRef)}\n type=\"search\"\n size={size}\n aria-label={label}\n aria-busy={loading || undefined}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" && query.length > 0) {\n // The query belongs to the consumer, not to a form, so Enter must\n // not also submit an enclosing `<form>` by accident.\n event.preventDefault();\n onSubmit?.(query);\n }\n }}\n disabled={disabled}\n readOnly={readOnly}\n {...props}\n />\n {canClear ? (\n <InputGroupEnd>\n <IconButton\n label={clearLabel ?? COPY.actions.reset}\n size=\"sm\"\n onClick={() => {\n setQuery(\"\");\n // Focus goes back to the field, because the button that held it\n // is about to disappear. Focus falling to `<body>` sends a\n // keyboard user back to the top of the document.\n inputRef.current?.focus();\n }}\n >\n <X />\n </IconButton>\n </InputGroupEnd>\n ) : null}\n </InputGroup>\n );\n },\n);\n"],"mappings":"AAqGM,SAGM,KAHN;AArGL,YAAY,WAAW;AACxB,SAAS,QAAQ,SAAS;AAC1B,SAAS,UAAU;AACnB,SAAS,YAAY;AAErB,SAAS,UAAU,sBAAsB,sBAAsB;AAC/D,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAC3B,SAAS,aAA8B;AACvC,SAAS,YAAY,eAAe,uBAAuB;AA8DpD,MAAM,cAAc,MAAM;AAAA,EAC/B,SAASA,aACP;AAAA,IACE;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,KACA;AACA,UAAM,CAAC,OAAO,QAAQ,IAAI,qBAAqB;AAAA,MAC7C;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,MAAM;AAAA,IACR,CAAC;AAED,UAAM,WAAW,MAAM,OAAgC,IAAI;AAC3D,UAAM,WAAW,aAAa,MAAM,SAAS,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC;AAE5E,WACE,qBAAC,cAAW,OAAc,WAAW,GAAG,SAAS,GAAI,GAAG,SAAS,cAAc,GAC7E;AAAA,0BAAC,mBACE,oBACC,oBAAC,WAAQ,MAAK,MAAK,OAAO,KAAK,OAAO,SAAS,WAAU,SAAQ,IAEjE,oBAAC,UAAO,eAAY,QAAO,WAAU,UAAS,GAElD;AAAA,MAGA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK,eAAe,KAAK,QAAW,QAAQ;AAAA,UAC5C,MAAK;AAAA,UACL;AAAA,UACA,cAAY;AAAA,UACZ,aAAW,WAAW;AAAA,UACtB,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,UAChD,WAAW,CAAC,UAAU;AACpB,gBAAI,MAAM,QAAQ,WAAW,MAAM,SAAS,GAAG;AAG7C,oBAAM,eAAe;AACrB,yBAAW,KAAK;AAAA,YAClB;AAAA,UACF;AAAA,UACA;AAAA,UACA;AAAA,UACC,GAAG;AAAA;AAAA,MACN;AAAA,MACC,WACC,oBAAC,iBACC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,cAAc,KAAK,QAAQ;AAAA,UAClC,MAAK;AAAA,UACL,SAAS,MAAM;AACb,qBAAS,EAAE;AAIX,qBAAS,SAAS,MAAM;AAAA,UAC1B;AAAA,UAEA,8BAAC,KAAE;AAAA;AAAA,MACL,GACF,IACE;AAAA,OACN;AAAA,EAEJ;AACF;","names":["SearchInput"]}
@@ -0,0 +1,65 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+ import { Select as Select$1 } from 'radix-ui';
4
+ import { VariantProps } from '@tea-ui/utils';
5
+
6
+ /**
7
+ * TEA UI — Select.
8
+ *
9
+ * A native `<select>` is correct whenever the option list is short and known,
10
+ * and this component is the answer when it is not: searchable, grouped,
11
+ * scrollable, or too long to trust to a native listbox on mobile. The audit
12
+ * found three hand-typed native `<select>`s in one product — none of which had
13
+ * an accessible name — sitting next to a fully built Radix Select that nobody
14
+ * used.
15
+ *
16
+ * Two audit defects are fixed here rather than carried forward:
17
+ * - The available-height constraint. The source project's version was
18
+ * `max-h-[--radix-select-content-available-height]`, which is not a valid
19
+ * arbitrary value and silently resolved to nothing, so a long list escaped
20
+ * the viewport. It is `max-h-[var(--radix-select-content-available-height)]`
21
+ * here.
22
+ * - The trigger height. Both source projects hardcoded a fixed height, so a
23
+ * compact table could not contain a compact select. It reads
24
+ * `--tea-control-h` here.
25
+ */
26
+ declare const selectTriggerVariants: (props?: ({
27
+ variant?: "outline" | "ghost" | null | undefined;
28
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
29
+ interface SelectProps extends Omit<React.ComponentProps<typeof Select$1.Root>, "children"> {
30
+ children: React.ReactNode;
31
+ /** The controlled value. */
32
+ value?: string | undefined;
33
+ /** The uncontrolled initial value. */
34
+ defaultValue?: string | undefined;
35
+ /** Called with the new value. */
36
+ onValueChange?: ((value: string) => void) | undefined;
37
+ }
38
+ declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLSpanElement>>;
39
+ declare const SelectGroup: React.ForwardRefExoticComponent<Select$1.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
40
+ declare const SelectValue: React.ForwardRefExoticComponent<Select$1.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
41
+ declare const SelectSeparator: React.ForwardRefExoticComponent<Select$1.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>>;
42
+ interface SelectTriggerProps extends Omit<React.ComponentProps<typeof Select$1.Trigger>, "className">, VariantProps<typeof selectTriggerVariants> {
43
+ className?: string | undefined;
44
+ }
45
+ declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectTriggerProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
46
+ interface SelectContentProps extends React.ComponentProps<typeof Select$1.Content> {
47
+ className?: string | undefined;
48
+ /** Where the list is placed relative to the trigger. */
49
+ position?: React.ComponentProps<typeof Select$1.Content>["position"];
50
+ }
51
+ declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
52
+ interface SelectItemProps extends React.ComponentProps<typeof Select$1.Item> {
53
+ className?: string | undefined;
54
+ /**
55
+ * A short explanation shown under the label in the list. It is not truncated
56
+ * away and it is not a tooltip — this is the place a confusing option
57
+ * explains itself.
58
+ */
59
+ description?: string | undefined;
60
+ }
61
+ declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
62
+ declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<Select$1.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
63
+ declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<Select$1.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
64
+
65
+ export { Select, SelectContent, type SelectContentProps, SelectGroup, SelectItem, type SelectItemProps, type SelectProps, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, selectTriggerVariants };
@@ -0,0 +1,139 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Select as SelectPrimitive } from "radix-ui";
4
+ import { Check, ChevronDown, ChevronUp } from "@tea-ui/icons";
5
+ import { cn, cva } from "@tea-ui/utils";
6
+ import { dataSlot, stateAttributes } from "../internal/index.js";
7
+ import { useFieldControlProps } from "./field.js";
8
+ const selectTriggerVariants = cva(
9
+ [
10
+ "control-h inline-flex w-full items-center justify-between gap-2 border px-[length:var(--tea-control-px)]",
11
+ "bg-surface text-ui text-fg transition-colors duration-fast ease-standard",
12
+ "data-[placeholder]:text-fg-muted",
13
+ "disabled:cursor-not-allowed disabled:opacity-50",
14
+ "aria-invalid:border-critical aria-invalid:bg-critical-subtle"
15
+ ],
16
+ {
17
+ variants: {
18
+ variant: {
19
+ outline: "border-line data-[hover]:border-line-strong",
20
+ ghost: "border-transparent bg-transparent data-[hover]:bg-surface-3"
21
+ }
22
+ },
23
+ defaultVariants: { variant: "outline" }
24
+ }
25
+ );
26
+ const SelectRootPrimitive = SelectPrimitive.Root;
27
+ const Select = React.forwardRef(function Select2({ children, ...props }, ref) {
28
+ return /* @__PURE__ */ jsx(SelectRootPrimitive, { ref, ...props, ...dataSlot("select", "root"), children });
29
+ });
30
+ const SelectGroup = SelectPrimitive.Group;
31
+ const SelectValue = SelectPrimitive.Value;
32
+ const SelectSeparator = SelectPrimitive.Separator;
33
+ const SelectTrigger = React.forwardRef(
34
+ function SelectTrigger2({ className, variant, children, disabled, ...props }, ref) {
35
+ const field = useFieldControlProps();
36
+ return /* @__PURE__ */ jsxs(
37
+ SelectPrimitive.Trigger,
38
+ {
39
+ ref,
40
+ disabled: field.disabled ?? disabled,
41
+ "aria-invalid": field["aria-invalid"] || void 0,
42
+ "aria-required": field["aria-required"] || void 0,
43
+ className: cn(selectTriggerVariants({ variant }), className),
44
+ "data-tea-touch": true,
45
+ ...dataSlot("select", "trigger"),
46
+ ...stateAttributes({ disabled: field.disabled ?? disabled }),
47
+ ...props,
48
+ children: [
49
+ children,
50
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { size: 16, "aria-hidden": "true", className: "shrink-0 text-fg-muted" }) })
51
+ ]
52
+ }
53
+ );
54
+ }
55
+ );
56
+ const SelectContent = React.forwardRef(
57
+ function SelectContent2({ className, children, position = "popper", ...props }, ref) {
58
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
59
+ SelectPrimitive.Content,
60
+ {
61
+ ref,
62
+ position,
63
+ className: cn(
64
+ "z-popover max-h-[var(--radix-select-content-available-height)] min-w-[var(--radix-select-trigger-width)]",
65
+ "border border-line bg-surface-2 shadow-overlay",
66
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
67
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
68
+ className
69
+ ),
70
+ ...dataSlot("select", "content"),
71
+ ...props,
72
+ children: [
73
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
74
+ /* @__PURE__ */ jsx(SelectPrimitive.Viewport, { className: "p-1", children }),
75
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
76
+ ]
77
+ }
78
+ ) });
79
+ }
80
+ );
81
+ const SelectItem = React.forwardRef(
82
+ function SelectItem2({ className, children, description, ...props }, ref) {
83
+ return /* @__PURE__ */ jsxs(
84
+ SelectPrimitive.Item,
85
+ {
86
+ ref,
87
+ className: cn(
88
+ "relative flex cursor-default select-none flex-col gap-0.5 py-2 pe-8 ps-3 text-ui text-fg outline-none",
89
+ "data-[highlighted]:bg-accent-subtle data-[highlighted]:text-fg",
90
+ "data-[state=checked]:text-primary",
91
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
92
+ className
93
+ ),
94
+ ...dataSlot("select", "item"),
95
+ ...props,
96
+ children: [
97
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }),
98
+ description ? /* @__PURE__ */ jsx("span", { className: "text-micro text-fg-muted", children: description }) : null,
99
+ /* @__PURE__ */ jsx("span", { className: "absolute end-2 flex size-4 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { size: 14, "aria-hidden": "true" }) }) })
100
+ ]
101
+ }
102
+ );
103
+ }
104
+ );
105
+ const SelectScrollUpButton = React.forwardRef(function SelectScrollUpButton2({ className, ...props }, ref) {
106
+ return /* @__PURE__ */ jsx(
107
+ SelectPrimitive.ScrollUpButton,
108
+ {
109
+ ref,
110
+ className: cn("flex cursor-default items-center justify-center py-1 text-fg-muted", className),
111
+ ...props,
112
+ children: /* @__PURE__ */ jsx(ChevronUp, { size: 14, "aria-hidden": "true" })
113
+ }
114
+ );
115
+ });
116
+ const SelectScrollDownButton = React.forwardRef(function SelectScrollDownButton2({ className, ...props }, ref) {
117
+ return /* @__PURE__ */ jsx(
118
+ SelectPrimitive.ScrollDownButton,
119
+ {
120
+ ref,
121
+ className: cn("flex cursor-default items-center justify-center py-1 text-fg-muted", className),
122
+ ...props,
123
+ children: /* @__PURE__ */ jsx(ChevronDown, { size: 14, "aria-hidden": "true" })
124
+ }
125
+ );
126
+ });
127
+ export {
128
+ Select,
129
+ SelectContent,
130
+ SelectGroup,
131
+ SelectItem,
132
+ SelectScrollDownButton,
133
+ SelectScrollUpButton,
134
+ SelectSeparator,
135
+ SelectTrigger,
136
+ SelectValue,
137
+ selectTriggerVariants
138
+ };
139
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inputs/select.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Select as SelectPrimitive } from \"radix-ui\";\nimport { Check, ChevronDown, ChevronUp } from \"@tea-ui/icons\";\nimport { cn, cva, type VariantProps } from \"@tea-ui/utils\";\n\nimport { dataSlot, stateAttributes } from \"../internal\";\nimport { useFieldControlProps } from \"./field\";\n\n/**\n * TEA UI — Select.\n *\n * A native `<select>` is correct whenever the option list is short and known,\n * and this component is the answer when it is not: searchable, grouped,\n * scrollable, or too long to trust to a native listbox on mobile. The audit\n * found three hand-typed native `<select>`s in one product — none of which had\n * an accessible name — sitting next to a fully built Radix Select that nobody\n * used.\n *\n * Two audit defects are fixed here rather than carried forward:\n * - The available-height constraint. The source project's version was\n * `max-h-[--radix-select-content-available-height]`, which is not a valid\n * arbitrary value and silently resolved to nothing, so a long list escaped\n * the viewport. It is `max-h-[var(--radix-select-content-available-height)]`\n * here.\n * - The trigger height. Both source projects hardcoded a fixed height, so a\n * compact table could not contain a compact select. It reads\n * `--tea-control-h` here.\n */\nexport const selectTriggerVariants = cva(\n [\n \"control-h inline-flex w-full items-center justify-between gap-2 border px-[length:var(--tea-control-px)]\",\n \"bg-surface text-ui text-fg transition-colors duration-fast ease-standard\",\n \"data-[placeholder]:text-fg-muted\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"aria-invalid:border-critical aria-invalid:bg-critical-subtle\",\n ],\n {\n variants: {\n variant: {\n outline: \"border-line data-[hover]:border-line-strong\",\n ghost: \"border-transparent bg-transparent data-[hover]:bg-surface-3\",\n },\n },\n defaultVariants: { variant: \"outline\" },\n },\n);\n\nexport interface SelectProps\n extends Omit<React.ComponentProps<typeof SelectPrimitive.Root>, \"children\"> {\n children: React.ReactNode;\n /** The controlled value. */\n value?: string | undefined;\n /** The uncontrolled initial value. */\n defaultValue?: string | undefined;\n /** Called with the new value. */\n onValueChange?: ((value: string) => void) | undefined;\n}\n\n/**\n * Radix discriminates the Select root's props on `multiple`, which makes a\n * forwardable optional-value signature unrepresentable in its own types. The\n * cast is done once, here, rather than at every call site.\n */\nconst SelectRootPrimitive = SelectPrimitive.Root as unknown as React.ComponentType<\n React.ComponentProps<\"span\"> & Record<string, unknown>\n>;\n\nexport const Select = React.forwardRef<HTMLSpanElement, SelectProps>(function Select(\n { children, ...props },\n ref,\n) {\n return (\n <SelectRootPrimitive ref={ref} {...props} {...dataSlot(\"select\", \"root\")}>\n {children}\n </SelectRootPrimitive>\n );\n});\n\nexport const SelectGroup = SelectPrimitive.Group;\nexport const SelectValue = SelectPrimitive.Value;\nexport const SelectSeparator = SelectPrimitive.Separator;\n\nexport interface SelectTriggerProps\n extends Omit<React.ComponentProps<typeof SelectPrimitive.Trigger>, \"className\">,\n VariantProps<typeof selectTriggerVariants> {\n className?: string | undefined;\n}\n\nexport const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerProps>(\n function SelectTrigger({ className, variant, children, disabled, ...props }, ref) {\n const field = useFieldControlProps();\n return (\n <SelectPrimitive.Trigger\n ref={ref}\n disabled={field.disabled ?? disabled}\n aria-invalid={field[\"aria-invalid\"] || undefined}\n aria-required={field[\"aria-required\"] || undefined}\n className={cn(selectTriggerVariants({ variant }), className)}\n data-tea-touch\n {...dataSlot(\"select\", \"trigger\")}\n {...stateAttributes({ disabled: field.disabled ?? disabled })}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown size={16} aria-hidden=\"true\" className=\"shrink-0 text-fg-muted\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n },\n);\n\nexport interface SelectContentProps extends React.ComponentProps<typeof SelectPrimitive.Content> {\n className?: string | undefined;\n /** Where the list is placed relative to the trigger. */\n position?: React.ComponentProps<typeof SelectPrimitive.Content>[\"position\"];\n}\n\nexport const SelectContent = React.forwardRef<HTMLDivElement, SelectContentProps>(\n function SelectContent({ className, children, position = \"popper\", ...props }, ref) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n className={cn(\n \"z-popover max-h-[var(--radix-select-content-available-height)] min-w-[var(--radix-select-trigger-width)]\",\n \"border border-line bg-surface-2 shadow-overlay\",\n \"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n \"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\n className,\n )}\n {...dataSlot(\"select\", \"content\")}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport className=\"p-1\">{children}</SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n },\n);\n\nexport interface SelectItemProps extends React.ComponentProps<typeof SelectPrimitive.Item> {\n className?: string | undefined;\n /**\n * A short explanation shown under the label in the list. It is not truncated\n * away and it is not a tooltip — this is the place a confusing option\n * explains itself.\n */\n description?: string | undefined;\n}\n\nexport const SelectItem = React.forwardRef<HTMLDivElement, SelectItemProps>(\n function SelectItem({ className, children, description, ...props }, ref) {\n return (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none flex-col gap-0.5 py-2 pe-8 ps-3 text-ui text-fg outline-none\",\n \"data-[highlighted]:bg-accent-subtle data-[highlighted]:text-fg\",\n \"data-[state=checked]:text-primary\",\n \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...dataSlot(\"select\", \"item\")}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n {description ? (\n <span className=\"text-micro text-fg-muted\">{description}</span>\n ) : null}\n <span className=\"absolute end-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check size={14} aria-hidden=\"true\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n </SelectPrimitive.Item>\n );\n },\n);\n\nexport const SelectScrollUpButton = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>\n>(function SelectScrollUpButton({ className, ...props }, ref) {\n return (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\"flex cursor-default items-center justify-center py-1 text-fg-muted\", className)}\n {...props}\n >\n <ChevronUp size={14} aria-hidden=\"true\" />\n </SelectPrimitive.ScrollUpButton>\n );\n});\n\nexport const SelectScrollDownButton = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>\n>(function SelectScrollDownButton({ className, ...props }, ref) {\n return (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\"flex cursor-default items-center justify-center py-1 text-fg-muted\", className)}\n {...props}\n >\n <ChevronDown size={14} aria-hidden=\"true\" />\n </SelectPrimitive.ScrollDownButton>\n );\n});\n"],"mappings":"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","names":["Select","SelectTrigger","SelectContent","SelectItem","SelectScrollUpButton","SelectScrollDownButton"]}
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { Slider as Slider$1 } from 'radix-ui';
3
+
4
+ /**
5
+ * TEA UI — Slider.
6
+ *
7
+ * A slider, and the only TEA UI control with no sensible default value: a
8
+ * slider with no name is a `<div>` a screen reader announces as "slider", and
9
+ * a voice-control user cannot say which one they mean. `label` is required.
10
+ *
11
+ * Three things it gets right that a hand-rolled range never does:
12
+ *
13
+ * - **A real value text.** `aria-valuetext` is what a screen reader reads
14
+ * instead of the raw number, so a percentage slider says "40 Prozent" and a
15
+ * temperature slider says "18 Grad". `formatValue` is how you say it.
16
+ * - **One tab stop, with arrows and Home/End.** Radix owns the roving focus;
17
+ * the value is only reachable because the thumb is focusable.
18
+ * - **The filled track is the value.** A range is readable with no colour
19
+ * perception, and it is readable *by shape* — a line with a filled part and an
20
+ * empty part, not two colours on one line.
21
+ */
22
+ interface SliderProps extends Omit<React.ComponentProps<typeof Slider$1.Root>, "value" | "defaultValue" | "onValueChange" | "orientation" | "dir"> {
23
+ /**
24
+ * The accessible name. **Required** — there is no default, because a slider
25
+ * whose name is guessed is a slider nobody can identify.
26
+ */
27
+ label: string;
28
+ /** Controlled value. An array, because a range has two thumbs. */
29
+ value?: number[] | undefined;
30
+ /** Uncontrolled initial value. */
31
+ defaultValue?: number[] | undefined;
32
+ /** Called with the whole value array on every change. */
33
+ onValueChange?: ((value: number[]) => void) | undefined;
34
+ /** Lowest selectable value. */
35
+ min?: number | undefined;
36
+ /** Highest selectable value. */
37
+ max?: number | undefined;
38
+ /** Granularity. Left to Radix (1) when omitted. */
39
+ step?: number | undefined;
40
+ /** Which way the track runs. Decides the arrow keys and the fill direction. */
41
+ orientation?: "horizontal" | "vertical";
42
+ /**
43
+ * Turn a number into the words a screen reader should hear, and into the
44
+ * visible readout. Defaults to the bare number.
45
+ */
46
+ formatValue?: ((value: number) => string) | undefined;
47
+ /** Show the formatted value next to the label. Off by default. */
48
+ showValue?: boolean | undefined;
49
+ className?: string | undefined;
50
+ }
51
+ /**
52
+ * @example
53
+ * ```tsx
54
+ * <Field>
55
+ * <FieldLabel>Speicherplatz</FieldLabel>
56
+ * <Slider
57
+ * label="Speicherplatz in Prozent"
58
+ * defaultValue={[40]}
59
+ * showValue
60
+ * formatValue={(v) => `${v} Prozent`}
61
+ * />
62
+ * </Field>
63
+ * ```
64
+ */
65
+ declare const Slider: React.ForwardRefExoticComponent<Omit<SliderProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
66
+
67
+ export { Slider, type SliderProps };