@behivetech/atoms.select 0.0.0-stage → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,46 @@
1
- # Temporary Holding Version
1
+ # Select
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A bare select control (built on Radix's `Select`) — the Radix trigger button is this component's own root element. It has no built-in label/hint/error; wrap it in `FormField` for that chrome.
4
+
5
+ ## Usage
6
+
7
+ ```jsx
8
+ import { Select } from '@behivetech/atoms.select';
9
+ import { FormField } from '@behivetech/forms.form-field';
10
+
11
+ const [value, setValue] = useState('');
12
+ const roleOptions = [
13
+ { label: 'Editor', value: 'editor' },
14
+ { label: 'Admin', value: 'admin' },
15
+ ];
16
+
17
+ <Select
18
+ placeholder="Choose a color"
19
+ options={[
20
+ { label: 'Red', value: 'red' },
21
+ { label: 'Green', value: 'green' },
22
+ { label: 'Blue', value: 'blue' },
23
+ ]}
24
+ value={value}
25
+ onValueChange={setValue}
26
+ />
27
+
28
+ <FormField label="Role" htmlFor="role">
29
+ <Select id="role" options={roleOptions} value={value} onValueChange={setValue} />
30
+ </FormField>
31
+ ```
32
+
33
+ Pass `aria-invalid="true"` to visually mark the field as invalid (pair with `FormField`'s `error` prop for the message).
34
+
35
+ ## Props
36
+
37
+ | Prop | Type | Default | Description |
38
+ | --------------- | ------------------------- | ------- | ------------------------------------------------- |
39
+ | `options` | `SelectOption[]` | — | Array of `{ label, value, disabled? }` (required) |
40
+ | `value` | `string` | — | Controlled selected value |
41
+ | `defaultValue` | `string` | — | Uncontrolled initial value |
42
+ | `onValueChange` | `(value: string) => void` | — | Called when the selection changes |
43
+ | `placeholder` | `string` | — | Placeholder shown when nothing is selected |
44
+ | `disabled` | `boolean` | `false` | Disables the field |
45
+ | `required` | `boolean` | `false` | Marks field as required |
46
+ | `className` | `string` | — | Additional class names |
package/dist/index.cjs ADDED
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // ../../../../node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.js
34
+ var require_classnames = __commonJS({
35
+ "../../../../node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.js"(exports2, module2) {
36
+ "use strict";
37
+ (function() {
38
+ "use strict";
39
+ var hasOwn = {}.hasOwnProperty;
40
+ function classNames() {
41
+ var classes = "";
42
+ for (var i = 0; i < arguments.length; i++) {
43
+ var arg = arguments[i];
44
+ if (arg) {
45
+ classes = appendClass(classes, parseValue(arg));
46
+ }
47
+ }
48
+ return classes;
49
+ }
50
+ function parseValue(arg) {
51
+ if (typeof arg === "string" || typeof arg === "number") {
52
+ return arg;
53
+ }
54
+ if (typeof arg !== "object") {
55
+ return "";
56
+ }
57
+ if (Array.isArray(arg)) {
58
+ return classNames.apply(null, arg);
59
+ }
60
+ if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes("[native code]")) {
61
+ return arg.toString();
62
+ }
63
+ var classes = "";
64
+ for (var key in arg) {
65
+ if (hasOwn.call(arg, key) && arg[key]) {
66
+ classes = appendClass(classes, key);
67
+ }
68
+ }
69
+ return classes;
70
+ }
71
+ function appendClass(value, newClass) {
72
+ if (!newClass) {
73
+ return value;
74
+ }
75
+ if (value) {
76
+ return value + " " + newClass;
77
+ }
78
+ return value + newClass;
79
+ }
80
+ if (typeof module2 !== "undefined" && module2.exports) {
81
+ classNames.default = classNames;
82
+ module2.exports = classNames;
83
+ } else if (typeof define === "function" && typeof define.amd === "object" && define.amd) {
84
+ define("classnames", [], function() {
85
+ return classNames;
86
+ });
87
+ } else {
88
+ window.classNames = classNames;
89
+ }
90
+ })();
91
+ }
92
+ });
93
+
94
+ // index.ts
95
+ var index_exports = {};
96
+ __export(index_exports, {
97
+ Select: () => Select
98
+ });
99
+ module.exports = __toCommonJS(index_exports);
100
+
101
+ // select.tsx
102
+ var RadixSelect = __toESM(require("@radix-ui/react-select"), 1);
103
+ var import_react = require("react");
104
+
105
+ // ../../../../packages/registry/class-names/dist/index.js
106
+ var import_classnames = __toESM(require_classnames(), 1);
107
+ var getClassName = ({
108
+ className,
109
+ modifiers = {},
110
+ rootClass,
111
+ styles = {}
112
+ }) => {
113
+ function getModifiers() {
114
+ const updatedModifiers = {};
115
+ const modifierKeys = Object.keys(modifiers);
116
+ if (modifierKeys.length) {
117
+ modifierKeys.forEach((modKey) => {
118
+ const classKey = `${rootClass}--${modKey}`;
119
+ const newClassName = styles[classKey] || classKey;
120
+ updatedModifiers[newClassName] = modifiers[modKey] ?? false;
121
+ });
122
+ }
123
+ return updatedModifiers;
124
+ }
125
+ function getChildClass(childClassName) {
126
+ const classKey = `${rootClass}__${childClassName}`;
127
+ return styles[classKey] || classKey;
128
+ }
129
+ function getRootStyles() {
130
+ return (0, import_classnames.default)(
131
+ className,
132
+ {
133
+ ...getModifiers()
134
+ },
135
+ styles[rootClass] || rootClass
136
+ );
137
+ }
138
+ return [getRootStyles(), getChildClass];
139
+ };
140
+
141
+ // select.module.scss
142
+ var select_module_default = {
143
+ "BHT__Select": "BHT__Select",
144
+ "BHT__Select__chevron": "BHT__Select__chevron",
145
+ "BHT__Select__content": "BHT__Select__content",
146
+ "select-in": "select-in",
147
+ "select-out": "select-out",
148
+ "BHT__Select__viewport": "BHT__Select__viewport",
149
+ "BHT__Select__item": "BHT__Select__item",
150
+ "BHT__Select__item-indicator": "BHT__Select__item-indicator"
151
+ };
152
+
153
+ // select.tsx
154
+ var import_jsx_runtime = require("react/jsx-runtime");
155
+ var Select = ({
156
+ options,
157
+ value,
158
+ defaultValue,
159
+ onValueChange,
160
+ placeholder,
161
+ disabled,
162
+ required,
163
+ className,
164
+ id,
165
+ name,
166
+ ...rest
167
+ }) => {
168
+ const generatedId = (0, import_react.useId)();
169
+ const selectId = id ?? generatedId;
170
+ const [rootClass, getChildClass] = getClassName({
171
+ className,
172
+ rootClass: "BHT__Select",
173
+ styles: select_module_default
174
+ });
175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
176
+ RadixSelect.Root,
177
+ {
178
+ value,
179
+ defaultValue,
180
+ onValueChange,
181
+ disabled,
182
+ required,
183
+ name,
184
+ children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(RadixSelect.Trigger, { ...rest, id: selectId, className: rootClass, children: [
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixSelect.Value, { placeholder }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixSelect.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
188
+ "svg",
189
+ {
190
+ className: getChildClass("chevron"),
191
+ viewBox: "0 0 12 8",
192
+ fill: "none",
193
+ stroke: "currentColor",
194
+ strokeWidth: "2",
195
+ strokeLinecap: "round",
196
+ strokeLinejoin: "round",
197
+ "aria-hidden": "true",
198
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1 1.5L6 6.5L11 1.5" })
199
+ }
200
+ ) })
201
+ ] }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixSelect.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
203
+ RadixSelect.Content,
204
+ {
205
+ className: getChildClass("content"),
206
+ position: "popper",
207
+ sideOffset: 4,
208
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixSelect.Viewport, { className: getChildClass("viewport"), children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
209
+ RadixSelect.Item,
210
+ {
211
+ value: option.value,
212
+ disabled: option.disabled,
213
+ className: getChildClass("item"),
214
+ children: [
215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixSelect.ItemText, { children: option.label }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
217
+ RadixSelect.ItemIndicator,
218
+ {
219
+ className: getChildClass("item-indicator"),
220
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
+ "svg",
222
+ {
223
+ viewBox: "0 0 12 10",
224
+ fill: "none",
225
+ stroke: "currentColor",
226
+ strokeWidth: "2.5",
227
+ strokeLinecap: "round",
228
+ strokeLinejoin: "round",
229
+ "aria-hidden": "true",
230
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1 5L4.5 8.5L11 1" })
231
+ }
232
+ )
233
+ }
234
+ )
235
+ ]
236
+ },
237
+ option.value
238
+ )) })
239
+ }
240
+ ) })
241
+ ]
242
+ }
243
+ );
244
+ };
245
+ // Annotate the CommonJS export names for ESM import in node:
246
+ 0 && (module.exports = {
247
+ Select
248
+ });
249
+ /*! Bundled license information:
250
+
251
+ classnames/index.js:
252
+ (*!
253
+ Copyright (c) 2018 Jed Watson.
254
+ Licensed under the MIT License (MIT), see
255
+ http://jedwatson.github.io/classnames
256
+ *)
257
+ */
package/dist/index.css ADDED
@@ -0,0 +1,164 @@
1
+ /* esbuild-sass-plugin:css-chunkselect.module.scss */
2
+ .BHT__Select {
3
+ position: relative;
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: space-between;
7
+ gap: var(--spacing-2, 0.5rem);
8
+ width: 100%;
9
+ min-height: 2.5rem;
10
+ padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);
11
+ font-family: var(--md-sys-typescale-body-large-font, var(--font-body));
12
+ font-size: var(--md-sys-typescale-body-large-size, 1rem);
13
+ font-weight: var(--md-sys-typescale-body-large-weight, 400);
14
+ line-height: var(--md-sys-typescale-body-large-line-height, 1.5rem);
15
+ letter-spacing: var(--md-sys-typescale-body-large-tracking, 0.0313em);
16
+ color: var(--md-sys-color-on-surface, var(--color-on-surface));
17
+ background: transparent;
18
+ border: 1px solid var(--md-sys-color-outline, var(--color-border-strong));
19
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
20
+ cursor: pointer;
21
+ text-align: left;
22
+ transition: border-color var(--md-sys-motion-duration-short-3, 150ms) var(--md-sys-motion-easing-standard, ease), box-shadow var(--md-sys-motion-duration-short-3, 150ms) var(--md-sys-motion-easing-standard, ease);
23
+ }
24
+ .BHT__Select::before {
25
+ content: "";
26
+ position: absolute;
27
+ left: 0;
28
+ right: 0;
29
+ top: 50%;
30
+ translate: 0 -50%;
31
+ height: 100%;
32
+ min-height: 48px;
33
+ }
34
+ .BHT__Select[data-placeholder] {
35
+ color: var(--md-sys-color-on-surface-variant, var(--color-text-muted));
36
+ }
37
+ .BHT__Select:hover:not([data-disabled], [data-state=open], :focus-visible) {
38
+ border-color: var(--md-sys-color-on-surface, var(--color-on-surface));
39
+ }
40
+ .BHT__Select[data-state=open],
41
+ .BHT__Select:focus-visible {
42
+ outline: none;
43
+ border-color: var(--md-sys-color-primary, var(--color-border-focus));
44
+ box-shadow: inset 0 0 0 1px var(--md-sys-color-primary, var(--color-border-focus));
45
+ }
46
+ .BHT__Select[data-state=open] .BHT__Select__chevron {
47
+ transform: rotate(180deg);
48
+ }
49
+ .BHT__Select[data-disabled] {
50
+ opacity: 0.45;
51
+ cursor: not-allowed;
52
+ }
53
+ .BHT__Select[aria-invalid=true] {
54
+ border-color: var(--md-sys-color-error, var(--color-error));
55
+ }
56
+ .BHT__Select[aria-invalid=true]:hover:not([data-disabled], [data-state=open], :focus-visible) {
57
+ border-color: var(--md-sys-color-on-error-container, var(--color-error));
58
+ }
59
+ .BHT__Select[aria-invalid=true][data-state=open],
60
+ .BHT__Select[aria-invalid=true]:focus-visible {
61
+ border-color: var(--md-sys-color-error, var(--color-error));
62
+ box-shadow: inset 0 0 0 1px var(--md-sys-color-error, var(--color-error));
63
+ }
64
+ .BHT__Select__chevron {
65
+ width: 0.75rem;
66
+ height: 0.75rem;
67
+ flex-shrink: 0;
68
+ color: var(--md-sys-color-on-surface-variant, var(--color-text-muted));
69
+ transition: transform var(--md-sys-motion-duration-short-3, 150ms) var(--md-sys-motion-easing-standard, ease);
70
+ }
71
+ .BHT__Select__content {
72
+ background: var(--md-sys-color-surface-container, var(--color-surface-raised));
73
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
74
+ box-shadow: var(--md-sys-elevation-2, var(--shadow-md));
75
+ overflow: hidden;
76
+ z-index: var(--z-dropdown, 450);
77
+ min-width: var(--radix-select-trigger-width);
78
+ max-height: var(--radix-select-content-available-height);
79
+ }
80
+ .BHT__Select__content[data-state=open] {
81
+ animation: select-in var(--md-sys-motion-duration-short-3, 150ms) var(--md-sys-motion-easing-standard-decelerate, ease-out);
82
+ }
83
+ .BHT__Select__content[data-state=closed] {
84
+ animation: select-out var(--md-sys-motion-duration-short-2, 100ms) var(--md-sys-motion-easing-standard-accelerate, ease-in);
85
+ }
86
+ .BHT__Select__viewport {
87
+ padding: var(--spacing-1, 0.25rem);
88
+ }
89
+ .BHT__Select__item {
90
+ position: relative;
91
+ isolation: isolate;
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: space-between;
95
+ gap: var(--spacing-2, 0.5rem);
96
+ padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);
97
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
98
+ font-family: var(--md-sys-typescale-label-large-font, var(--font-body));
99
+ font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
100
+ font-weight: var(--md-sys-typescale-label-large-weight, 500);
101
+ line-height: var(--md-sys-typescale-label-large-line-height, 1.25rem);
102
+ letter-spacing: var(--md-sys-typescale-label-large-tracking, 0.0071em);
103
+ color: var(--md-sys-color-on-surface, var(--color-on-surface));
104
+ cursor: pointer;
105
+ user-select: none;
106
+ outline: none;
107
+ }
108
+ .BHT__Select__item::after {
109
+ content: "";
110
+ position: absolute;
111
+ inset: 0;
112
+ border-radius: inherit;
113
+ background: currentColor;
114
+ opacity: 0;
115
+ pointer-events: none;
116
+ transition: opacity var(--md-sys-motion-duration-short-2, 100ms) var(--md-sys-motion-easing-standard, ease);
117
+ }
118
+ .BHT__Select__item[data-highlighted]::after {
119
+ opacity: var(--md-sys-state-hover-opacity, 0.08);
120
+ }
121
+ .BHT__Select__item:active::after {
122
+ opacity: var(--md-sys-state-pressed-opacity, 0.1);
123
+ }
124
+ .BHT__Select__item[data-state=checked] {
125
+ background: var(--md-sys-color-secondary-container, var(--color-surface-light));
126
+ color: var(--md-sys-color-on-secondary-container, var(--color-on-surface));
127
+ }
128
+ .BHT__Select__item[data-disabled] {
129
+ opacity: 0.4;
130
+ cursor: not-allowed;
131
+ pointer-events: none;
132
+ }
133
+ .BHT__Select__item-indicator {
134
+ width: 0.75rem;
135
+ height: 0.75rem;
136
+ flex-shrink: 0;
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ }
141
+ .BHT__Select__item-indicator svg {
142
+ width: 100%;
143
+ height: 100%;
144
+ }
145
+ @keyframes select-in {
146
+ from {
147
+ opacity: 0;
148
+ transform: translateY(-0.25rem);
149
+ }
150
+ to {
151
+ opacity: 1;
152
+ transform: translateY(0);
153
+ }
154
+ }
155
+ @keyframes select-out {
156
+ from {
157
+ opacity: 1;
158
+ transform: translateY(0);
159
+ }
160
+ to {
161
+ opacity: 0;
162
+ transform: translateY(-0.25rem);
163
+ }
164
+ }
@@ -0,0 +1,37 @@
1
+ import * as react from 'react';
2
+ import * as RadixSelect from '@radix-ui/react-select';
3
+
4
+ interface SelectOption {
5
+ /** Text shown for this option */
6
+ label: string;
7
+ /** Value submitted/reported when this option is chosen */
8
+ value: string;
9
+ /** Prevents this option from being selected */
10
+ disabled?: boolean;
11
+ }
12
+ interface SelectProps extends Omit<RadixSelect.SelectTriggerProps, "className"> {
13
+ /** Options rendered in the dropdown */
14
+ options: SelectOption[];
15
+ /** Controlled selected value */
16
+ value?: string;
17
+ /** Initial selected value for uncontrolled usage */
18
+ defaultValue?: string;
19
+ /** Called with the new value when the selection changes */
20
+ onValueChange?: (value: string) => void;
21
+ /** Text shown when no value is selected */
22
+ placeholder?: string;
23
+ /** Marks the field as required for form validation */
24
+ required?: boolean;
25
+ /** Additional class names to merge with the component root element */
26
+ className?: string;
27
+ /** Form field name submitted with the value */
28
+ name?: string;
29
+ }
30
+ /**
31
+ * Bare select control — the Radix select trigger button is this component's
32
+ * own root element, no built-in label/hint/error. Wrap it in `FormField` for
33
+ * that chrome: `<FormField label="..." htmlFor="x"><Select id="x" .../></FormField>`.
34
+ */
35
+ declare const Select: ({ options, value, defaultValue, onValueChange, placeholder, disabled, required, className, id, name, ...rest }: SelectProps) => react.JSX.Element;
36
+
37
+ export { Select, type SelectOption, type SelectProps };
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // ../../../../node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.js
28
+ var require_classnames = __commonJS({
29
+ "../../../../node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.js"(exports, module) {
30
+ "use strict";
31
+ (function() {
32
+ "use strict";
33
+ var hasOwn = {}.hasOwnProperty;
34
+ function classNames() {
35
+ var classes = "";
36
+ for (var i = 0; i < arguments.length; i++) {
37
+ var arg = arguments[i];
38
+ if (arg) {
39
+ classes = appendClass(classes, parseValue(arg));
40
+ }
41
+ }
42
+ return classes;
43
+ }
44
+ function parseValue(arg) {
45
+ if (typeof arg === "string" || typeof arg === "number") {
46
+ return arg;
47
+ }
48
+ if (typeof arg !== "object") {
49
+ return "";
50
+ }
51
+ if (Array.isArray(arg)) {
52
+ return classNames.apply(null, arg);
53
+ }
54
+ if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes("[native code]")) {
55
+ return arg.toString();
56
+ }
57
+ var classes = "";
58
+ for (var key in arg) {
59
+ if (hasOwn.call(arg, key) && arg[key]) {
60
+ classes = appendClass(classes, key);
61
+ }
62
+ }
63
+ return classes;
64
+ }
65
+ function appendClass(value, newClass) {
66
+ if (!newClass) {
67
+ return value;
68
+ }
69
+ if (value) {
70
+ return value + " " + newClass;
71
+ }
72
+ return value + newClass;
73
+ }
74
+ if (typeof module !== "undefined" && module.exports) {
75
+ classNames.default = classNames;
76
+ module.exports = classNames;
77
+ } else if (typeof define === "function" && typeof define.amd === "object" && define.amd) {
78
+ define("classnames", [], function() {
79
+ return classNames;
80
+ });
81
+ } else {
82
+ window.classNames = classNames;
83
+ }
84
+ })();
85
+ }
86
+ });
87
+
88
+ // select.tsx
89
+ import * as RadixSelect from "@radix-ui/react-select";
90
+ import { useId } from "react";
91
+
92
+ // ../../../../packages/registry/class-names/dist/index.js
93
+ var import_classnames = __toESM(require_classnames(), 1);
94
+ var getClassName = ({
95
+ className,
96
+ modifiers = {},
97
+ rootClass,
98
+ styles = {}
99
+ }) => {
100
+ function getModifiers() {
101
+ const updatedModifiers = {};
102
+ const modifierKeys = Object.keys(modifiers);
103
+ if (modifierKeys.length) {
104
+ modifierKeys.forEach((modKey) => {
105
+ const classKey = `${rootClass}--${modKey}`;
106
+ const newClassName = styles[classKey] || classKey;
107
+ updatedModifiers[newClassName] = modifiers[modKey] ?? false;
108
+ });
109
+ }
110
+ return updatedModifiers;
111
+ }
112
+ function getChildClass(childClassName) {
113
+ const classKey = `${rootClass}__${childClassName}`;
114
+ return styles[classKey] || classKey;
115
+ }
116
+ function getRootStyles() {
117
+ return (0, import_classnames.default)(
118
+ className,
119
+ {
120
+ ...getModifiers()
121
+ },
122
+ styles[rootClass] || rootClass
123
+ );
124
+ }
125
+ return [getRootStyles(), getChildClass];
126
+ };
127
+
128
+ // select.module.scss
129
+ var select_module_default = {
130
+ "BHT__Select": "BHT__Select",
131
+ "BHT__Select__chevron": "BHT__Select__chevron",
132
+ "BHT__Select__content": "BHT__Select__content",
133
+ "select-in": "select-in",
134
+ "select-out": "select-out",
135
+ "BHT__Select__viewport": "BHT__Select__viewport",
136
+ "BHT__Select__item": "BHT__Select__item",
137
+ "BHT__Select__item-indicator": "BHT__Select__item-indicator"
138
+ };
139
+
140
+ // select.tsx
141
+ import { jsx, jsxs } from "react/jsx-runtime";
142
+ var Select = ({
143
+ options,
144
+ value,
145
+ defaultValue,
146
+ onValueChange,
147
+ placeholder,
148
+ disabled,
149
+ required,
150
+ className,
151
+ id,
152
+ name,
153
+ ...rest
154
+ }) => {
155
+ const generatedId = useId();
156
+ const selectId = id ?? generatedId;
157
+ const [rootClass, getChildClass] = getClassName({
158
+ className,
159
+ rootClass: "BHT__Select",
160
+ styles: select_module_default
161
+ });
162
+ return /* @__PURE__ */ jsxs(
163
+ RadixSelect.Root,
164
+ {
165
+ value,
166
+ defaultValue,
167
+ onValueChange,
168
+ disabled,
169
+ required,
170
+ name,
171
+ children: [
172
+ /* @__PURE__ */ jsxs(RadixSelect.Trigger, { ...rest, id: selectId, className: rootClass, children: [
173
+ /* @__PURE__ */ jsx(RadixSelect.Value, { placeholder }),
174
+ /* @__PURE__ */ jsx(RadixSelect.Icon, { asChild: true, children: /* @__PURE__ */ jsx(
175
+ "svg",
176
+ {
177
+ className: getChildClass("chevron"),
178
+ viewBox: "0 0 12 8",
179
+ fill: "none",
180
+ stroke: "currentColor",
181
+ strokeWidth: "2",
182
+ strokeLinecap: "round",
183
+ strokeLinejoin: "round",
184
+ "aria-hidden": "true",
185
+ children: /* @__PURE__ */ jsx("path", { d: "M1 1.5L6 6.5L11 1.5" })
186
+ }
187
+ ) })
188
+ ] }),
189
+ /* @__PURE__ */ jsx(RadixSelect.Portal, { children: /* @__PURE__ */ jsx(
190
+ RadixSelect.Content,
191
+ {
192
+ className: getChildClass("content"),
193
+ position: "popper",
194
+ sideOffset: 4,
195
+ children: /* @__PURE__ */ jsx(RadixSelect.Viewport, { className: getChildClass("viewport"), children: options.map((option) => /* @__PURE__ */ jsxs(
196
+ RadixSelect.Item,
197
+ {
198
+ value: option.value,
199
+ disabled: option.disabled,
200
+ className: getChildClass("item"),
201
+ children: [
202
+ /* @__PURE__ */ jsx(RadixSelect.ItemText, { children: option.label }),
203
+ /* @__PURE__ */ jsx(
204
+ RadixSelect.ItemIndicator,
205
+ {
206
+ className: getChildClass("item-indicator"),
207
+ children: /* @__PURE__ */ jsx(
208
+ "svg",
209
+ {
210
+ viewBox: "0 0 12 10",
211
+ fill: "none",
212
+ stroke: "currentColor",
213
+ strokeWidth: "2.5",
214
+ strokeLinecap: "round",
215
+ strokeLinejoin: "round",
216
+ "aria-hidden": "true",
217
+ children: /* @__PURE__ */ jsx("path", { d: "M1 5L4.5 8.5L11 1" })
218
+ }
219
+ )
220
+ }
221
+ )
222
+ ]
223
+ },
224
+ option.value
225
+ )) })
226
+ }
227
+ ) })
228
+ ]
229
+ }
230
+ );
231
+ };
232
+ export {
233
+ Select
234
+ };
235
+ /*! Bundled license information:
236
+
237
+ classnames/index.js:
238
+ (*!
239
+ Copyright (c) 2018 Jed Watson.
240
+ Licensed under the MIT License (MIT), see
241
+ http://jedwatson.github.io/classnames
242
+ *)
243
+ */
package/docs.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "$schema": "https://github.com/behivetech/bht-component-registry/docs.schema.json",
3
+ "version": 2,
4
+ "generatedAt": "2026-10-20T05:49:58.892Z",
5
+ "docs": [
6
+ {
7
+ "displayName": "Select",
8
+ "description": "Bare select control — the Radix select trigger button is this component's\nown root element, no built-in label/hint/error. Wrap it in `FormField` for\nthat chrome: `<FormField label=\"...\" htmlFor=\"x\"><Select id=\"x\" .../></FormField>`.",
9
+ "extendsNative": true,
10
+ "props": [
11
+ {
12
+ "name": "options",
13
+ "type": "SelectOption[]",
14
+ "required": true,
15
+ "defaultValue": null,
16
+ "description": "Options rendered in the dropdown"
17
+ },
18
+ {
19
+ "name": "asChild",
20
+ "type": "boolean",
21
+ "required": false,
22
+ "defaultValue": null,
23
+ "description": ""
24
+ },
25
+ {
26
+ "name": "className",
27
+ "type": "string",
28
+ "required": false,
29
+ "defaultValue": null,
30
+ "description": "Additional class names to merge with the component root element"
31
+ },
32
+ {
33
+ "name": "defaultValue",
34
+ "type": "string",
35
+ "required": false,
36
+ "defaultValue": null,
37
+ "description": "Initial selected value for uncontrolled usage"
38
+ },
39
+ {
40
+ "name": "name",
41
+ "type": "string",
42
+ "required": false,
43
+ "defaultValue": null,
44
+ "description": "Form field name submitted with the value"
45
+ },
46
+ {
47
+ "name": "onValueChange",
48
+ "type": "((value: string) => void)",
49
+ "required": false,
50
+ "defaultValue": null,
51
+ "description": "Called with the new value when the selection changes"
52
+ },
53
+ {
54
+ "name": "placeholder",
55
+ "type": "string",
56
+ "required": false,
57
+ "defaultValue": null,
58
+ "description": "Text shown when no value is selected"
59
+ },
60
+ {
61
+ "name": "required",
62
+ "type": "boolean",
63
+ "required": false,
64
+ "defaultValue": null,
65
+ "description": "Marks the field as required for form validation"
66
+ },
67
+ {
68
+ "name": "value",
69
+ "type": "string",
70
+ "required": false,
71
+ "defaultValue": null,
72
+ "description": "Controlled selected value"
73
+ }
74
+ ]
75
+ }
76
+ ],
77
+ "functions": []
78
+ }
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Select } from "./select.js";
2
+ export type { SelectProps, SelectOption } from "./select.js";
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "@behivetech/atoms.select",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.4.2",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "private": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
12
+ },
13
+ "./styles.css": "./dist/index.css"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "index.ts",
19
+ "select.tsx",
20
+ "select.module.scss",
21
+ "*.example.tsx",
22
+ "docs.json"
23
+ ],
24
+ "registry": {
25
+ "status": "stable",
26
+ "tags": []
27
+ },
28
+ "peerDependencies": {
29
+ "@radix-ui/react-select": "^2.1.0",
30
+ "react": "^19.2.7",
31
+ "react-dom": "^19.2.7"
32
+ },
33
+ "devDependencies": {
34
+ "@radix-ui/react-select": "^2.3.7",
35
+ "@types/react": "19.2.17",
36
+ "@types/react-dom": "19.2.3",
37
+ "eslint": "^9.39.5",
38
+ "tsup": "^8.5.1",
39
+ "typescript": "^6.0.3",
40
+ "vitest": "^4.1.10",
41
+ "@behivetech/forms.form-field": "0.3.2",
42
+ "@bht-component-registry/class-names": "0.0.6",
43
+ "@bht-component-registry/env": "0.1.0"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/behivetech/bht-galaxy.git",
48
+ "directory": "registry/behivetech/atoms/select"
49
+ },
50
+ "publishConfig": {
51
+ "registry": "https://registry.npmjs.org",
52
+ "access": "public",
53
+ "provenance": true
54
+ },
55
+ "scripts": {
56
+ "build": "tsup && bht-registry-docgen",
57
+ "dev": "tsup --watch",
58
+ "lint": "eslint . --max-warnings 0",
59
+ "check-types": "tsc --noEmit",
60
+ "test": "vitest run"
61
+ }
6
62
  }
@@ -0,0 +1,62 @@
1
+ import { useState } from "react";
2
+ import { FormField } from "@behivetech/forms.form-field";
3
+ import { Select } from "./select.js";
4
+
5
+ const colorOptions = [
6
+ { label: "Red", value: "red" },
7
+ { label: "Green", value: "green" },
8
+ { label: "Blue", value: "blue" },
9
+ ];
10
+
11
+ const roleOptions = [
12
+ { label: "Admin", value: "admin" },
13
+ { label: "Editor", value: "editor" },
14
+ { label: "Viewer", value: "viewer" },
15
+ { label: "Guest (disabled)", value: "guest", disabled: true },
16
+ ];
17
+
18
+ export const BasicSelect = () => {
19
+ const [value, setValue] = useState("");
20
+ return (
21
+ <Select
22
+ placeholder="Choose a color"
23
+ options={colorOptions}
24
+ value={value}
25
+ onValueChange={setValue}
26
+ />
27
+ );
28
+ };
29
+
30
+ export const SelectWithFormField = () => {
31
+ const [value, setValue] = useState("");
32
+ return (
33
+ <FormField
34
+ label="Role"
35
+ htmlFor="role"
36
+ hint="Determines what the user can access."
37
+ >
38
+ <Select
39
+ id="role"
40
+ placeholder="Select a role"
41
+ options={roleOptions}
42
+ value={value}
43
+ onValueChange={setValue}
44
+ />
45
+ </FormField>
46
+ );
47
+ };
48
+
49
+ export const SelectWithError = () => (
50
+ <FormField label="Status" htmlFor="status" error="Please select a status.">
51
+ <Select
52
+ id="status"
53
+ aria-invalid="true"
54
+ placeholder="Select status"
55
+ options={colorOptions}
56
+ />
57
+ </FormField>
58
+ );
59
+
60
+ export const DisabledSelect = () => (
61
+ <Select disabled options={colorOptions} value="red" />
62
+ );
@@ -0,0 +1,232 @@
1
+ // Material Design 3 outlined select: the trigger is an outlined text field and
2
+ // the popup is a menu.
3
+ //
4
+ // The trigger's 2px focus/open edge is a 1px inset shadow on top of the 1px
5
+ // border rather than a wider border, so the value never shifts.
6
+
7
+ .BHT__Select {
8
+ // Radix Select.Trigger renders as <button>
9
+ position: relative;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ gap: var(--spacing-2, 0.5rem);
14
+ width: 100%;
15
+ min-height: 2.5rem;
16
+ padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);
17
+ font-family: var(--md-sys-typescale-body-large-font, var(--font-body));
18
+ font-size: var(--md-sys-typescale-body-large-size, 1rem);
19
+ font-weight: var(--md-sys-typescale-body-large-weight, 400);
20
+ line-height: var(--md-sys-typescale-body-large-line-height, 1.5rem);
21
+ letter-spacing: var(--md-sys-typescale-body-large-tracking, 0.0313em);
22
+ color: var(--md-sys-color-on-surface, var(--color-on-surface));
23
+ // Outlined fields have no container fill; they take the surface they sit on.
24
+ background: transparent;
25
+ border: 1px solid var(--md-sys-color-outline, var(--color-border-strong));
26
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
27
+ cursor: pointer;
28
+ text-align: left;
29
+ transition:
30
+ border-color var(--md-sys-motion-duration-short-3, 150ms)
31
+ var(--md-sys-motion-easing-standard, ease),
32
+ box-shadow var(--md-sys-motion-duration-short-3, 150ms)
33
+ var(--md-sys-motion-easing-standard, ease);
34
+
35
+ // Touch target: extends the hit area to 48px without changing the drawn
36
+ // 40px height. px on purpose — a finger doesn't scale with font size.
37
+ &::before {
38
+ content: "";
39
+ position: absolute;
40
+ left: 0;
41
+ right: 0;
42
+ top: 50%;
43
+ translate: 0 -50%;
44
+ height: 100%;
45
+ min-height: 48px;
46
+ }
47
+
48
+ &[data-placeholder] {
49
+ color: var(--md-sys-color-on-surface-variant, var(--color-text-muted));
50
+ }
51
+
52
+ &:hover:not([data-disabled], [data-state="open"], :focus-visible) {
53
+ border-color: var(--md-sys-color-on-surface, var(--color-on-surface));
54
+ }
55
+
56
+ &[data-state="open"],
57
+ &:focus-visible {
58
+ outline: none;
59
+ border-color: var(--md-sys-color-primary, var(--color-border-focus));
60
+ box-shadow: inset 0 0 0 1px
61
+ var(--md-sys-color-primary, var(--color-border-focus));
62
+ }
63
+
64
+ &[data-state="open"] {
65
+ .BHT__Select {
66
+ &__chevron {
67
+ transform: rotate(180deg);
68
+ }
69
+ }
70
+ }
71
+
72
+ &[data-disabled] {
73
+ opacity: 0.45;
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ &[aria-invalid="true"] {
78
+ border-color: var(--md-sys-color-error, var(--color-error));
79
+
80
+ &:hover:not([data-disabled], [data-state="open"], :focus-visible) {
81
+ border-color: var(--md-sys-color-on-error-container, var(--color-error));
82
+ }
83
+
84
+ &[data-state="open"],
85
+ &:focus-visible {
86
+ border-color: var(--md-sys-color-error, var(--color-error));
87
+ box-shadow: inset 0 0 0 1px var(--md-sys-color-error, var(--color-error));
88
+ }
89
+ }
90
+
91
+ &__chevron {
92
+ width: 0.75rem;
93
+ height: 0.75rem;
94
+ flex-shrink: 0;
95
+ color: var(--md-sys-color-on-surface-variant, var(--color-text-muted));
96
+ transition: transform var(--md-sys-motion-duration-short-3, 150ms)
97
+ var(--md-sys-motion-easing-standard, ease);
98
+ }
99
+
100
+ // --- Menu ------------------------------------------------------------------
101
+
102
+ &__content {
103
+ // Renders in a portal to document.body, so it is a *sibling* of any Modal
104
+ // or Drawer it was opened from rather than a child — the fallback must stay
105
+ // above --z-modal's (400) or an unstyled consumer gets a popup hidden
106
+ // behind the modal that spawned it.
107
+ //
108
+ // MD3 menus separate from the page by tone and elevation, not a border.
109
+ background: var(
110
+ --md-sys-color-surface-container,
111
+ var(--color-surface-raised)
112
+ );
113
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
114
+ box-shadow: var(--md-sys-elevation-2, var(--shadow-md));
115
+ overflow: hidden;
116
+ z-index: var(--z-dropdown, 450);
117
+ min-width: var(--radix-select-trigger-width);
118
+ max-height: var(--radix-select-content-available-height);
119
+
120
+ &[data-state="open"] {
121
+ animation: select-in var(--md-sys-motion-duration-short-3, 150ms)
122
+ var(--md-sys-motion-easing-standard-decelerate, ease-out);
123
+ }
124
+
125
+ &[data-state="closed"] {
126
+ animation: select-out var(--md-sys-motion-duration-short-2, 100ms)
127
+ var(--md-sys-motion-easing-standard-accelerate, ease-in);
128
+ }
129
+ }
130
+
131
+ &__viewport {
132
+ padding: var(--spacing-1, 0.25rem);
133
+ }
134
+
135
+ &__item {
136
+ position: relative;
137
+ isolation: isolate;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ gap: var(--spacing-2, 0.5rem);
142
+ padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);
143
+ border-radius: var(--md-sys-shape-corner-extra-small, 4px);
144
+ font-family: var(--md-sys-typescale-label-large-font, var(--font-body));
145
+ font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
146
+ font-weight: var(--md-sys-typescale-label-large-weight, 500);
147
+ line-height: var(--md-sys-typescale-label-large-line-height, 1.25rem);
148
+ letter-spacing: var(--md-sys-typescale-label-large-tracking, 0.0071em);
149
+ color: var(--md-sys-color-on-surface, var(--color-on-surface));
150
+ cursor: pointer;
151
+ user-select: none;
152
+ outline: none;
153
+
154
+ // State layer. Radix sets data-highlighted for both pointer hover and
155
+ // keyboard focus, so it stands in for :hover here.
156
+ &::after {
157
+ content: "";
158
+ position: absolute;
159
+ inset: 0;
160
+ border-radius: inherit;
161
+ background: currentColor;
162
+ opacity: 0;
163
+ pointer-events: none;
164
+ transition: opacity var(--md-sys-motion-duration-short-2, 100ms)
165
+ var(--md-sys-motion-easing-standard, ease);
166
+ }
167
+
168
+ &[data-highlighted]::after {
169
+ opacity: var(--md-sys-state-hover-opacity, 0.08);
170
+ }
171
+
172
+ &:active::after {
173
+ opacity: var(--md-sys-state-pressed-opacity, 0.1);
174
+ }
175
+
176
+ // MD3 marks the selected menu item with a tonal container.
177
+ &[data-state="checked"] {
178
+ background: var(
179
+ --md-sys-color-secondary-container,
180
+ var(--color-surface-light)
181
+ );
182
+ color: var(
183
+ --md-sys-color-on-secondary-container,
184
+ var(--color-on-surface)
185
+ );
186
+ }
187
+
188
+ &[data-disabled] {
189
+ opacity: 0.4;
190
+ cursor: not-allowed;
191
+ pointer-events: none;
192
+ }
193
+ }
194
+
195
+ &__item-indicator {
196
+ width: 0.75rem;
197
+ height: 0.75rem;
198
+ flex-shrink: 0;
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+
203
+ svg {
204
+ width: 100%;
205
+ height: 100%;
206
+ }
207
+ }
208
+ }
209
+
210
+ @keyframes select-in {
211
+ from {
212
+ opacity: 0;
213
+ transform: translateY(-0.25rem);
214
+ }
215
+
216
+ to {
217
+ opacity: 1;
218
+ transform: translateY(0);
219
+ }
220
+ }
221
+
222
+ @keyframes select-out {
223
+ from {
224
+ opacity: 1;
225
+ transform: translateY(0);
226
+ }
227
+
228
+ to {
229
+ opacity: 0;
230
+ transform: translateY(-0.25rem);
231
+ }
232
+ }
package/select.tsx ADDED
@@ -0,0 +1,127 @@
1
+ import * as RadixSelect from "@radix-ui/react-select";
2
+ import { useId } from "react";
3
+ import { getClassName } from "@bht-component-registry/class-names";
4
+ import styles from "./select.module.scss";
5
+
6
+ export interface SelectOption {
7
+ /** Text shown for this option */
8
+ label: string;
9
+ /** Value submitted/reported when this option is chosen */
10
+ value: string;
11
+ /** Prevents this option from being selected */
12
+ disabled?: boolean;
13
+ }
14
+
15
+ export interface SelectProps extends Omit<
16
+ RadixSelect.SelectTriggerProps,
17
+ "className"
18
+ > {
19
+ /** Options rendered in the dropdown */
20
+ options: SelectOption[];
21
+ /** Controlled selected value */
22
+ value?: string;
23
+ /** Initial selected value for uncontrolled usage */
24
+ defaultValue?: string;
25
+ /** Called with the new value when the selection changes */
26
+ onValueChange?: (value: string) => void;
27
+ /** Text shown when no value is selected */
28
+ placeholder?: string;
29
+ /** Marks the field as required for form validation */
30
+ required?: boolean;
31
+ /** Additional class names to merge with the component root element */
32
+ className?: string;
33
+ /** Form field name submitted with the value */
34
+ name?: string;
35
+ }
36
+
37
+ /**
38
+ * Bare select control — the Radix select trigger button is this component's
39
+ * own root element, no built-in label/hint/error. Wrap it in `FormField` for
40
+ * that chrome: `<FormField label="..." htmlFor="x"><Select id="x" .../></FormField>`.
41
+ */
42
+ export const Select = ({
43
+ options,
44
+ value,
45
+ defaultValue,
46
+ onValueChange,
47
+ placeholder,
48
+ disabled,
49
+ required,
50
+ className,
51
+ id,
52
+ name,
53
+ ...rest
54
+ }: SelectProps) => {
55
+ const generatedId = useId();
56
+ const selectId = id ?? generatedId;
57
+
58
+ const [rootClass, getChildClass] = getClassName({
59
+ className,
60
+ rootClass: "BHT__Select",
61
+ styles,
62
+ });
63
+
64
+ return (
65
+ <RadixSelect.Root
66
+ value={value}
67
+ defaultValue={defaultValue}
68
+ onValueChange={onValueChange}
69
+ disabled={disabled}
70
+ required={required}
71
+ name={name}
72
+ >
73
+ <RadixSelect.Trigger {...rest} id={selectId} className={rootClass}>
74
+ <RadixSelect.Value placeholder={placeholder} />
75
+ <RadixSelect.Icon asChild>
76
+ <svg
77
+ className={getChildClass("chevron")}
78
+ viewBox="0 0 12 8"
79
+ fill="none"
80
+ stroke="currentColor"
81
+ strokeWidth="2"
82
+ strokeLinecap="round"
83
+ strokeLinejoin="round"
84
+ aria-hidden="true"
85
+ >
86
+ <path d="M1 1.5L6 6.5L11 1.5" />
87
+ </svg>
88
+ </RadixSelect.Icon>
89
+ </RadixSelect.Trigger>
90
+ <RadixSelect.Portal>
91
+ <RadixSelect.Content
92
+ className={getChildClass("content")}
93
+ position="popper"
94
+ sideOffset={4}
95
+ >
96
+ <RadixSelect.Viewport className={getChildClass("viewport")}>
97
+ {options.map((option) => (
98
+ <RadixSelect.Item
99
+ key={option.value}
100
+ value={option.value}
101
+ disabled={option.disabled}
102
+ className={getChildClass("item")}
103
+ >
104
+ <RadixSelect.ItemText>{option.label}</RadixSelect.ItemText>
105
+ <RadixSelect.ItemIndicator
106
+ className={getChildClass("item-indicator")}
107
+ >
108
+ <svg
109
+ viewBox="0 0 12 10"
110
+ fill="none"
111
+ stroke="currentColor"
112
+ strokeWidth="2.5"
113
+ strokeLinecap="round"
114
+ strokeLinejoin="round"
115
+ aria-hidden="true"
116
+ >
117
+ <path d="M1 5L4.5 8.5L11 1" />
118
+ </svg>
119
+ </RadixSelect.ItemIndicator>
120
+ </RadixSelect.Item>
121
+ ))}
122
+ </RadixSelect.Viewport>
123
+ </RadixSelect.Content>
124
+ </RadixSelect.Portal>
125
+ </RadixSelect.Root>
126
+ );
127
+ };