react-wheel-select 0.0.1

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.
@@ -0,0 +1,241 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { CSSProperties, ReactNode, KeyboardEvent } from 'react';
3
+
4
+ /**
5
+ * Represents a single option in the select dropdown
6
+ */
7
+ interface WheelSelectOption<T extends string = string> {
8
+ /** Unique value for the option */
9
+ value: T;
10
+ /** Display label for the option */
11
+ label: string;
12
+ /** Optional disabled state */
13
+ disabled?: boolean;
14
+ /** Optional custom data attached to the option */
15
+ data?: Record<string, unknown>;
16
+ }
17
+ /**
18
+ * Theme configuration for the wheel select
19
+ */
20
+ interface WheelSelectTheme {
21
+ /** Color scheme: 'dark' | 'light' | 'auto' */
22
+ colorScheme?: 'dark' | 'light' | 'auto';
23
+ /** Custom colors */
24
+ colors?: {
25
+ /** Text color */
26
+ text?: string;
27
+ /** Text color when inactive/faded */
28
+ textMuted?: string;
29
+ /** Background color for active item pill */
30
+ activeBg?: string;
31
+ /** Background color for hover states */
32
+ hoverBg?: string;
33
+ /** Backdrop overlay color */
34
+ backdropBg?: string;
35
+ /** Focus ring color */
36
+ focusRing?: string;
37
+ };
38
+ /** Border radius for pill shapes */
39
+ borderRadius?: number | string;
40
+ /** Font settings */
41
+ font?: {
42
+ /** Font family */
43
+ family?: string;
44
+ /** Font size for options */
45
+ size?: number | string;
46
+ /** Font weight for options */
47
+ weight?: number | string;
48
+ /** Font size for trigger */
49
+ triggerSize?: number | string;
50
+ };
51
+ /** Animation settings */
52
+ animation?: {
53
+ /** Duration in ms */
54
+ duration?: number;
55
+ /** Easing function */
56
+ easing?: string;
57
+ /** Disable all animations */
58
+ disabled?: boolean;
59
+ };
60
+ /** Spacing settings */
61
+ spacing?: {
62
+ /** Gap between trigger text and icon */
63
+ triggerGap?: number | string;
64
+ /** Padding for trigger button */
65
+ triggerPadding?: string;
66
+ /** Gap between option text and icon */
67
+ optionGap?: number | string;
68
+ /** Padding for options */
69
+ optionPadding?: string;
70
+ };
71
+ }
72
+ /**
73
+ * Sizing configuration for the wheel
74
+ */
75
+ interface WheelSelectSizing {
76
+ /** Height of the wheel viewport */
77
+ wheelHeight?: number | string;
78
+ /** Minimum width of the wheel */
79
+ wheelMinWidth?: number | string;
80
+ /** Height of each option item */
81
+ optionHeight?: number | string;
82
+ /** Icon size */
83
+ iconSize?: number;
84
+ }
85
+ /**
86
+ * Behavior configuration
87
+ */
88
+ interface WheelSelectBehavior {
89
+ /** Close on outside click (default: true) */
90
+ closeOnOutsideClick?: boolean;
91
+ /** Close on Escape key (default: true) */
92
+ closeOnEscape?: boolean;
93
+ /** Close on selection (default: true) */
94
+ closeOnSelect?: boolean;
95
+ /** Scroll debounce delay in ms (default: 50) */
96
+ scrollDebounceMs?: number;
97
+ /** Enable keyboard navigation (default: true) */
98
+ keyboardNavigation?: boolean;
99
+ /** Portal target element (default: document.body) */
100
+ portalTarget?: HTMLElement | null;
101
+ /** Focus trigger after close (default: true) */
102
+ focusTriggerOnClose?: boolean;
103
+ }
104
+ /**
105
+ * Custom icon components
106
+ */
107
+ interface WheelSelectIcons {
108
+ /** Custom chevron icon for closed state */
109
+ chevron?: ReactNode;
110
+ /** Custom arrow icon for active item */
111
+ arrow?: ReactNode;
112
+ /** Hide chevron icon */
113
+ hideChevron?: boolean;
114
+ /** Hide arrow icon on active item */
115
+ hideArrow?: boolean;
116
+ }
117
+ /**
118
+ * Accessibility configuration
119
+ */
120
+ interface WheelSelectA11y {
121
+ /** Aria label for the trigger button */
122
+ triggerLabel?: string;
123
+ /** Aria label for the picker dialog */
124
+ pickerLabel?: string;
125
+ /** Custom aria-describedby id */
126
+ describedBy?: string;
127
+ }
128
+ /**
129
+ * Event callbacks
130
+ */
131
+ interface WheelSelectCallbacks<T extends string = string> {
132
+ /** Called when the picker opens */
133
+ onOpen?: () => void;
134
+ /** Called when the picker closes */
135
+ onClose?: () => void;
136
+ /** Called when value changes */
137
+ onChange?: (value: T, option: WheelSelectOption<T>) => void;
138
+ /** Called when active (highlighted) item changes during scroll */
139
+ onActiveChange?: (index: number, option: WheelSelectOption<T>) => void;
140
+ /** Called on keyboard navigation */
141
+ onKeyDown?: (event: KeyboardEvent) => void;
142
+ }
143
+ /**
144
+ * Ref handle for imperative control
145
+ */
146
+ interface WheelSelectRef {
147
+ /** Open the picker programmatically */
148
+ open: () => void;
149
+ /** Close the picker programmatically */
150
+ close: () => void;
151
+ /** Toggle the picker */
152
+ toggle: () => void;
153
+ /** Focus the trigger */
154
+ focus: () => void;
155
+ /** Get current open state */
156
+ isOpen: () => boolean;
157
+ /** Scroll to a specific option by index */
158
+ scrollToIndex: (index: number) => void;
159
+ /** Get the underlying native select element */
160
+ getNativeSelect: () => HTMLSelectElement | null;
161
+ }
162
+ /**
163
+ * Main component props
164
+ */
165
+ interface WheelSelectProps<T extends string = string> {
166
+ /** Array of options */
167
+ options: WheelSelectOption<T>[];
168
+ /** Currently selected value */
169
+ value: T;
170
+ /** Change handler */
171
+ onChange: (value: T) => void;
172
+ /** Placeholder text when no value selected */
173
+ placeholder?: string;
174
+ /** Disabled state */
175
+ disabled?: boolean;
176
+ /** Required field indicator */
177
+ required?: boolean;
178
+ /** Name attribute for form submission */
179
+ name?: string;
180
+ /** ID attribute */
181
+ id?: string;
182
+ /** Additional CSS class */
183
+ className?: string;
184
+ /** Inline styles */
185
+ style?: CSSProperties;
186
+ /** Theme configuration */
187
+ theme?: WheelSelectTheme;
188
+ /** Sizing configuration */
189
+ sizing?: WheelSelectSizing;
190
+ /** Behavior configuration */
191
+ behavior?: WheelSelectBehavior;
192
+ /** Custom icons */
193
+ icons?: WheelSelectIcons;
194
+ /** Accessibility configuration */
195
+ a11y?: WheelSelectA11y;
196
+ /** Event callbacks */
197
+ callbacks?: WheelSelectCallbacks<T>;
198
+ /** Custom trigger renderer */
199
+ renderTrigger?: (props: {
200
+ value: T;
201
+ label: string;
202
+ isOpen: boolean;
203
+ disabled: boolean;
204
+ onClick: () => void;
205
+ }) => ReactNode;
206
+ /** Custom option renderer */
207
+ renderOption?: (props: {
208
+ option: WheelSelectOption<T>;
209
+ index: number;
210
+ isActive: boolean;
211
+ isSelected: boolean;
212
+ }) => ReactNode;
213
+ /** Z-index for the picker overlay */
214
+ zIndex?: number;
215
+ }
216
+ declare function WheelSelectInner<T extends string = string>(props: WheelSelectProps<T>, ref: React.ForwardedRef<WheelSelectRef>): react_jsx_runtime.JSX.Element;
217
+ declare const WheelSelect: <T extends string = string>(props: WheelSelectProps<T> & {
218
+ ref?: React.ForwardedRef<WheelSelectRef>;
219
+ }) => ReturnType<typeof WheelSelectInner>;
220
+
221
+ interface SelectOption {
222
+ value: string;
223
+ label: string;
224
+ }
225
+ interface BaseSelectCompatProps {
226
+ options: SelectOption[];
227
+ value: string;
228
+ onChange: (value: string) => void;
229
+ name?: string;
230
+ id?: string;
231
+ className?: string;
232
+ }
233
+ /**
234
+ * BaseSelectCompat - A progressively enhanced select component
235
+ *
236
+ * Uses native stylable select (appearance: base-select) when supported,
237
+ * falls back to a custom inline wheel picker for older browsers.
238
+ */
239
+ declare function BaseSelectCompat({ options, value, onChange, name, id, className, }: BaseSelectCompatProps): react_jsx_runtime.JSX.Element;
240
+
241
+ export { BaseSelectCompat, type BaseSelectCompatProps, type SelectOption, WheelSelect, type WheelSelectA11y, type WheelSelectBehavior, type WheelSelectCallbacks, type WheelSelectIcons, type WheelSelectOption, type WheelSelectProps, type WheelSelectRef, type WheelSelectSizing, type WheelSelectTheme, WheelSelect as default };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import {forwardRef,useMemo,useState,useRef,useEffect,useCallback,useImperativeHandle}from'react';import {createPortal}from'react-dom';import {jsxs,jsx}from'react/jsx-runtime';var Ee={colorScheme:"dark",colors:{text:"inherit",textMuted:"inherit",activeBg:"rgba(255, 255, 255, 0.12)",hoverBg:"rgba(255, 255, 255, 0.12)",backdropBg:"rgba(0, 0, 0, 0.5)",focusRing:"rgba(255, 255, 255, 0.5)"},borderRadius:12,font:{family:"inherit",size:28,weight:500,triggerSize:"inherit"},animation:{duration:200,easing:"ease",disabled:false},spacing:{triggerGap:16,triggerPadding:"8px 16px",optionGap:16,optionPadding:"0 20px"}},Oe={wheelHeight:320,wheelMinWidth:220,optionHeight:56,iconSize:20},We={closeOnOutsideClick:true,closeOnEscape:true,closeOnSelect:true,scrollDebounceMs:50,keyboardNavigation:true,portalTarget:null,focusTriggerOnClose:true},Pe=({size:i=20,className:a})=>jsxs("svg",{width:i,height:i,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:a,"aria-hidden":"true",children:[jsx("polyline",{points:"8 9 12 5 16 9"}),jsx("polyline",{points:"8 15 12 19 16 15"})]}),He=({size:i=20,className:a})=>jsxs("svg",{width:i,height:i,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:a,"aria-hidden":"true",children:[jsx("line",{x1:"19",y1:"12",x2:"5",y2:"12"}),jsx("polyline",{points:"12 19 5 12 12 5"})]}),D=(i,a="0")=>i===void 0?a:typeof i=="number"?`${i}px`:i,le=(i,a)=>{let r={...i};for(let c in a)a[c]!==void 0&&(typeof a[c]=="object"&&a[c]!==null&&!Array.isArray(a[c])?r[c]=le(i[c],a[c]):r[c]=a[c]);return r};function Me(i,a){let{options:r,value:c,onChange:q,placeholder:oe="Select...",disabled:V=false,required:R=false,name:te,id:v,className:A="",style:L,theme:j,sizing:H,behavior:M,icons:E,a11y:U,callbacks:k,renderTrigger:ie,renderOption:z,zIndex:re=10001}=i,s=useMemo(()=>le(Ee,j||{}),[j]),u=useMemo(()=>le(Oe,H||{}),[H]),g=useMemo(()=>le(We,M||{}),[M]),[b,x]=useState(false),[Y,O]=useState(()=>Math.max(0,r.findIndex(t=>t.value===c))),[W,ce]=useState(null),B=useRef(null),J=useRef(null),$=useRef(null),y=useRef([]),e=useRef(null),n=useRef(null),d=useRef(false),S=useMemo(()=>r.find(t=>t.value===c),[r,c]),T=S?.label??oe;useEffect(()=>{let t=r.findIndex(o=>o.value===c);t!==-1&&O(t);},[c,r]),useEffect(()=>()=>{n.current&&clearTimeout(n.current);},[]);let Q=useMemo(()=>{let t=typeof u.wheelHeight=="number"?u.wheelHeight:320,o=typeof u.optionHeight=="number"?u.optionHeight:56;return t/2-o/2},[u.wheelHeight,u.optionHeight]),P=useCallback(()=>{if(V)return;B.current&&ce(B.current.getBoundingClientRect());let t=r.findIndex(l=>l.value===c),o=t!==-1?t:0;e.current=o,O(o),x(true),k?.onOpen?.();},[V,r,c,k]),I=useCallback(()=>{x(false),g.focusTriggerOnClose&&B.current?.focus(),k?.onClose?.();},[g.focusTriggerOnClose,k]),X=useCallback(t=>{let o=r.find(l=>l.value===t);!o||o.disabled||(q(t),k?.onChange?.(t,o),J.current&&(J.current.value=t),g.closeOnSelect&&(x(false),g.focusTriggerOnClose&&B.current?.focus(),k?.onClose?.()));},[q,r,g.closeOnSelect,g.focusTriggerOnClose,k]);useEffect(()=>{if(b&&e.current!==null){let t=e.current;e.current=null,requestAnimationFrame(()=>{let o=$.current,l=y.current[t];if(o&&l){let f=o.clientHeight,h=l.clientHeight,Z=l.offsetTop-f/2+h/2;o.scrollTop=Z;}});}},[b]),useEffect(()=>{b&&$.current&&$.current.focus();},[b]);let ne=useCallback(()=>{let t=$.current;if(!t)return;let o=t.getBoundingClientRect(),l=o.top+o.height/2,f=0,h=1/0;y.current.forEach((Z,Te)=>{if(!Z)return;let be=Z.getBoundingClientRect(),Ce=be.top+be.height/2,fe=Math.abs(Ce-l);fe<h&&(h=fe,f=Te);}),O(f);let p=r[f];p&&k?.onActiveChange?.(f,p);},[r,k]),ve=useCallback(()=>{d.current=true,n.current&&clearTimeout(n.current),n.current=setTimeout(()=>{d.current=false,ne();},g.scrollDebounceMs);},[ne,g.scrollDebounceMs]),we=useCallback(t=>{if(g.keyboardNavigation)switch(k?.onKeyDown?.(t),t.key){case "Escape":g.closeOnEscape&&(t.preventDefault(),I());break;case "ArrowUp":t.preventDefault(),O(h=>{let p=h-1;for(;p>=0&&r[p]?.disabled;)p--;return p<0?h:(y.current[p]?.scrollIntoView({block:"center",behavior:"smooth"}),p)});break;case "ArrowDown":t.preventDefault(),O(h=>{let p=h+1;for(;p<r.length&&r[p]?.disabled;)p++;return p>=r.length?h:(y.current[p]?.scrollIntoView({block:"center",behavior:"smooth"}),p)});break;case "Enter":case " ":t.preventDefault();let o=r[Y];o&&!o.disabled&&X(o.value);break;case "Home":t.preventDefault();let l=r.findIndex(h=>!h.disabled);l!==-1&&(O(l),y.current[l]?.scrollIntoView({block:"center",behavior:"smooth"}));break;case "End":t.preventDefault();let f=r.findLastIndex(h=>!h.disabled);f!==-1&&(O(f),y.current[f]?.scrollIntoView({block:"center",behavior:"smooth"}));break}},[g.keyboardNavigation,g.closeOnEscape,r,Y,I,X,k]),ue=useCallback(t=>o=>{o.preventDefault(),o.stopPropagation(),n.current&&(clearTimeout(n.current),n.current=null);let l=r[t];l&&!l.disabled&&X(l.value);},[r,X]),ke=useCallback(t=>{g.closeOnOutsideClick&&t.target===t.currentTarget&&I();},[g.closeOnOutsideClick,I]),xe=useCallback(t=>{q(t.target.value);},[q]),ge=useCallback(t=>{y.current[t]?.scrollIntoView({block:"center",behavior:"smooth"});},[]);useImperativeHandle(a,()=>({open:P,close:I,toggle:()=>b?I():P(),focus:()=>B.current?.focus(),isOpen:()=>b,scrollToIndex:ge,getNativeSelect:()=>J.current}),[b,P,I,ge]);let he=useMemo(()=>({"--ws-color-text":s.colors.text,"--ws-color-text-muted":s.colors.textMuted,"--ws-color-active-bg":s.colors.activeBg,"--ws-color-hover-bg":s.colors.hoverBg,"--ws-color-backdrop-bg":s.colors.backdropBg,"--ws-color-focus-ring":s.colors.focusRing,"--ws-border-radius":D(s.borderRadius),"--ws-font-family":s.font.family,"--ws-font-size":D(s.font.size),"--ws-font-weight":String(s.font.weight),"--ws-font-size-trigger":D(s.font.triggerSize),"--ws-animation-duration":s.animation.disabled?"0ms":`${s.animation.duration}ms`,"--ws-animation-easing":s.animation.easing,"--ws-trigger-gap":D(s.spacing.triggerGap),"--ws-trigger-padding":s.spacing.triggerPadding,"--ws-option-gap":D(s.spacing.optionGap),"--ws-option-padding":s.spacing.optionPadding,"--ws-wheel-height":D(u.wheelHeight),"--ws-wheel-min-width":D(u.wheelMinWidth),"--ws-option-height":D(u.optionHeight),"--ws-icon-size":`${u.iconSize}px`,"--ws-spacer-height":`${Q}px`,"--ws-z-index":String(re)}),[s,u,Q,re]),ye=s.colorScheme==="auto"?"":s.colorScheme==="light"?"ws-light":"ws-dark",Se=()=>{if(!b||!W)return null;let t=g.portalTarget??document.body;return createPortal(jsx("div",{className:"ws-backdrop",onClick:ke,role:"presentation",style:he,children:jsx("div",{className:"ws-picker",style:{left:W.left,top:W.top+W.height/2},role:"dialog","aria-modal":"true","aria-label":U?.pickerLabel??"Select an option",children:jsxs("div",{ref:$,className:"ws-wheel",role:"listbox",tabIndex:0,onKeyDown:we,onScroll:ve,"aria-activedescendant":`ws-option-${v??"default"}-${Y}`,"aria-describedby":U?.describedBy,children:[jsx("div",{className:"ws-spacer","aria-hidden":"true"}),r.map((o,l)=>{let f=l===Y,h=o.value===c;return z?jsx("div",{ref:p=>{y.current[l]=p;},id:`ws-option-${v??"default"}-${l}`,role:"option","aria-selected":h,"aria-disabled":o.disabled,className:`ws-option ${f?"ws-active":""} ${o.disabled?"ws-disabled":""}`,onClick:ue(l),children:z({option:o,index:l,isActive:f,isSelected:h})},o.value):jsxs("div",{ref:p=>{y.current[l]=p;},id:`ws-option-${v??"default"}-${l}`,role:"option","aria-selected":h,"aria-disabled":o.disabled,className:`ws-option ${f?"ws-active":""} ${o.disabled?"ws-disabled":""}`,onClick:ue(l),children:[jsx("span",{className:"ws-option-text",children:o.label}),f&&!E?.hideArrow&&(E?.arrow??jsx(He,{size:u.iconSize,className:"ws-arrow"}))]},o.value)}),jsx("div",{className:"ws-spacer","aria-hidden":"true"})]})})}),t)};return jsxs("span",{className:`ws-root ${ye} ${A}`,style:{...he,...L},children:[jsxs("select",{ref:J,name:te,id:v?`${v}-native`:void 0,value:c,onChange:xe,className:"ws-native-select",tabIndex:-1,"aria-hidden":"true",required:R,disabled:V,children:[!S&&jsx("option",{value:"",children:oe}),r.map(t=>jsx("option",{value:t.value,disabled:t.disabled,children:t.label},t.value))]}),ie?ie({value:c,label:T,isOpen:b,disabled:V,onClick:P}):jsxs("button",{ref:B,type:"button",id:v,className:`ws-trigger ${b?"ws-open":""}`,onClick:P,disabled:V,"aria-haspopup":"listbox","aria-expanded":b,"aria-label":U?.triggerLabel,"aria-describedby":U?.describedBy,style:{visibility:b?"hidden":"visible"},children:[jsx("span",{className:"ws-trigger-text",children:T}),!E?.hideChevron&&(E?.chevron??jsx(Pe,{size:u.iconSize,className:"ws-chevron"}))]}),Se()]})}var me=forwardRef(Me),Be=me;var Ae=()=>jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"base-select-chevron",children:[jsx("polyline",{points:"8 9 12 5 16 9"}),jsx("polyline",{points:"8 15 12 19 16 15"})]}),Le=()=>jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"base-select-arrow",children:[jsx("line",{x1:"19",y1:"12",x2:"5",y2:"12"}),jsx("polyline",{points:"12 19 5 12 12 5"})]});function ze({options:i,value:a,onChange:r,name:c,id:q,className:oe=""}){let[R,te]=useState(false),[v,A]=useState(()=>Math.max(0,i.findIndex(e=>e.value===a))),L=useRef(null),j=useRef(null),H=useRef(null),M=useRef([]),[E,U]=useState(null);useEffect(()=>{let e=i.findIndex(n=>n.value===a);e!==-1&&A(e);},[a,i]);let ie=i.find(e=>e.value===a)?.label??i[0]?.label??"",z=useRef(null),re=useCallback(()=>{L.current&&U(L.current.getBoundingClientRect());let e=i.findIndex(d=>d.value===a),n=e!==-1?e:0;z.current=n,A(n),te(true);},[i,a]),s=useCallback(()=>{te(false),L.current?.focus();},[]),u=useCallback(e=>{r(e),j.current&&(j.current.value=e),te(false),L.current?.focus();},[r]);useEffect(()=>{if(R&&z.current!==null){let e=z.current;z.current=null,requestAnimationFrame(()=>{let n=H.current,d=M.current[e];if(n&&d){let S=n.clientHeight,T=d.clientHeight,P=d.offsetTop-S/2+T/2;n.scrollTop=P;}});}},[R]);let g=useCallback(()=>{let e=H.current;if(!e)return;let n=e.getBoundingClientRect(),d=n.top+n.height/2,S=0,T=1/0;M.current.forEach((Q,P)=>{if(!Q)return;let I=Q.getBoundingClientRect(),X=I.top+I.height/2,ne=Math.abs(X-d);ne<T&&(T=ne,S=P);}),A(S);},[]),b=useRef(false),x=useRef(null),Y=useCallback(()=>{b.current=true,x.current&&clearTimeout(x.current),x.current=setTimeout(()=>{b.current=false,g();},50);},[g]),O=useCallback(e=>{switch(e.key){case "Escape":e.preventDefault(),s();break;case "ArrowUp":e.preventDefault(),A(n=>{let d=Math.max(0,n-1);return M.current[d]?.scrollIntoView({block:"center",behavior:"smooth"}),d});break;case "ArrowDown":e.preventDefault(),A(n=>{let d=Math.min(i.length-1,n+1);return M.current[d]?.scrollIntoView({block:"center",behavior:"smooth"}),d});break;case "Enter":e.preventDefault(),i[v]&&u(i[v].value);break}},[v,i,s,u]),W=useCallback(e=>{i[e]&&u(i[e].value);},[i,u]),ce=useCallback(e=>n=>{n.preventDefault(),n.stopPropagation(),x.current&&(clearTimeout(x.current),x.current=null),W(e);},[W]),B=useCallback(e=>{let d=e.target.closest(".base-select-option");if(d){let S=d.id,T=parseInt(S.replace("option-",""),10);!isNaN(T)&&i[T]&&W(T);}},[i,W]),J=useCallback(e=>{e.target===e.currentTarget&&s();},[s]);useEffect(()=>{R&&H.current&&H.current.focus();},[R]),useEffect(()=>()=>{x.current&&clearTimeout(x.current);},[]);let $=useCallback(e=>{r(e.target.value);},[r]),y=()=>!R||!E?null:createPortal(jsx("div",{className:"base-select-backdrop",onClick:J,role:"presentation",children:jsx("div",{className:"base-select-picker",style:{left:E.left,top:E.top+E.height/2},role:"dialog","aria-modal":"true","aria-label":"Select an option",children:jsxs("div",{ref:H,className:"base-select-wheel",role:"listbox",tabIndex:0,onKeyDown:O,onScroll:Y,onClick:B,"aria-activedescendant":`option-${v}`,children:[jsx("div",{className:"base-select-spacer","aria-hidden":"true"}),i.map((e,n)=>jsxs("div",{ref:d=>{M.current[n]=d;},id:`option-${n}`,role:"option","aria-selected":n===v,className:`base-select-option ${n===v?"active":""}`,onClick:ce(n),children:[jsx("span",{className:"base-select-option-text",children:e.label}),n===v&&jsx(Le,{})]},e.value)),jsx("div",{className:"base-select-spacer","aria-hidden":"true"})]})})}),document.body);return jsxs("span",{className:`base-select-compat fallback ${oe}`,children:[jsx("select",{ref:j,name:c,id:q,value:a,onChange:$,className:"base-select-hidden",tabIndex:-1,"aria-hidden":"true",children:i.map(e=>jsx("option",{value:e.value,children:e.label},e.value))}),jsxs("button",{ref:L,type:"button",className:`base-select-trigger ${R?"open":""}`,onClick:re,"aria-haspopup":"listbox","aria-expanded":R,style:{visibility:R?"hidden":"visible"},children:[jsx("span",{className:"base-select-trigger-text",children:ie}),jsx(Ae,{})]}),y()]})}export{ze as BaseSelectCompat,me as WheelSelect,Be as default};//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/WheelSelect.tsx","../src/components/BaseSelectCompat.tsx"],"names":["defaultTheme","defaultSizing","defaultBehavior","DefaultChevronIcon","size","className","jsxs","jsx","DefaultArrowIcon","toCssValue","value","defaultValue","mergeDeep","target","source","result","key","WheelSelectInner","props","ref","options","onChange","placeholder","disabled","required","name","id","style","themeProp","sizingProp","behaviorProp","icons","a11y","callbacks","renderTrigger","renderOption","zIndex","theme","useMemo","sizing","behavior","isOpen","setIsOpen","useState","activeIndex","setActiveIndex","o","triggerRect","setTriggerRect","triggerRef","useRef","selectRef","wheelRef","itemRefs","initialScrollIndexRef","scrollTimeoutRef","isScrollingRef","selectedOption","displayLabel","useEffect","idx","spacerHeight","wheelH","optionH","openPicker","useCallback","targetIndex","closePicker","commitSelection","newValue","option","wheel","item","wheelHeight","itemHeight","scrollTarget","calculateActiveFromScroll","wheelRect","wheelCenter","closestIndex","closestDistance","index","itemRect","itemCenter","distance","handleScroll","handlePickerKeyDown","e","prev","newIndex","firstEnabled","lastEnabled","handleItemClick","handleBackdropClick","handleNativeChange","scrollToIndex","useImperativeHandle","cssVariables","colorSchemeClass","renderPicker","portalTarget","createPortal","isActive","isSelected","el","WheelSelect","forwardRef","WheelSelect_default","ChevronIcon","ArrowLeftIcon","BaseSelectCompat","handleItemSelect","handleWheelClick","optionEl","optionId","renderFallbackPicker"],"mappings":"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","file":"index.js","sourcesContent":["import {\n useState,\n useRef,\n useEffect,\n useCallback,\n useMemo,\n forwardRef,\n useImperativeHandle,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n type CSSProperties,\n} from 'react'\nimport { createPortal } from 'react-dom'\n\n// ============================================================================\n// Types & Interfaces\n// ============================================================================\n\n/**\n * Represents a single option in the select dropdown\n */\nexport interface WheelSelectOption<T extends string = string> {\n /** Unique value for the option */\n value: T\n /** Display label for the option */\n label: string\n /** Optional disabled state */\n disabled?: boolean\n /** Optional custom data attached to the option */\n data?: Record<string, unknown>\n}\n\n/**\n * Theme configuration for the wheel select\n */\nexport interface WheelSelectTheme {\n /** Color scheme: 'dark' | 'light' | 'auto' */\n colorScheme?: 'dark' | 'light' | 'auto'\n /** Custom colors */\n colors?: {\n /** Text color */\n text?: string\n /** Text color when inactive/faded */\n textMuted?: string\n /** Background color for active item pill */\n activeBg?: string\n /** Background color for hover states */\n hoverBg?: string\n /** Backdrop overlay color */\n backdropBg?: string\n /** Focus ring color */\n focusRing?: string\n }\n /** Border radius for pill shapes */\n borderRadius?: number | string\n /** Font settings */\n font?: {\n /** Font family */\n family?: string\n /** Font size for options */\n size?: number | string\n /** Font weight for options */\n weight?: number | string\n /** Font size for trigger */\n triggerSize?: number | string\n }\n /** Animation settings */\n animation?: {\n /** Duration in ms */\n duration?: number\n /** Easing function */\n easing?: string\n /** Disable all animations */\n disabled?: boolean\n }\n /** Spacing settings */\n spacing?: {\n /** Gap between trigger text and icon */\n triggerGap?: number | string\n /** Padding for trigger button */\n triggerPadding?: string\n /** Gap between option text and icon */\n optionGap?: number | string\n /** Padding for options */\n optionPadding?: string\n }\n}\n\n/**\n * Sizing configuration for the wheel\n */\nexport interface WheelSelectSizing {\n /** Height of the wheel viewport */\n wheelHeight?: number | string\n /** Minimum width of the wheel */\n wheelMinWidth?: number | string\n /** Height of each option item */\n optionHeight?: number | string\n /** Icon size */\n iconSize?: number\n}\n\n/**\n * Behavior configuration\n */\nexport interface WheelSelectBehavior {\n /** Close on outside click (default: true) */\n closeOnOutsideClick?: boolean\n /** Close on Escape key (default: true) */\n closeOnEscape?: boolean\n /** Close on selection (default: true) */\n closeOnSelect?: boolean\n /** Scroll debounce delay in ms (default: 50) */\n scrollDebounceMs?: number\n /** Enable keyboard navigation (default: true) */\n keyboardNavigation?: boolean\n /** Portal target element (default: document.body) */\n portalTarget?: HTMLElement | null\n /** Focus trigger after close (default: true) */\n focusTriggerOnClose?: boolean\n}\n\n/**\n * Custom icon components\n */\nexport interface WheelSelectIcons {\n /** Custom chevron icon for closed state */\n chevron?: ReactNode\n /** Custom arrow icon for active item */\n arrow?: ReactNode\n /** Hide chevron icon */\n hideChevron?: boolean\n /** Hide arrow icon on active item */\n hideArrow?: boolean\n}\n\n/**\n * Accessibility configuration\n */\nexport interface WheelSelectA11y {\n /** Aria label for the trigger button */\n triggerLabel?: string\n /** Aria label for the picker dialog */\n pickerLabel?: string\n /** Custom aria-describedby id */\n describedBy?: string\n}\n\n/**\n * Event callbacks\n */\nexport interface WheelSelectCallbacks<T extends string = string> {\n /** Called when the picker opens */\n onOpen?: () => void\n /** Called when the picker closes */\n onClose?: () => void\n /** Called when value changes */\n onChange?: (value: T, option: WheelSelectOption<T>) => void\n /** Called when active (highlighted) item changes during scroll */\n onActiveChange?: (index: number, option: WheelSelectOption<T>) => void\n /** Called on keyboard navigation */\n onKeyDown?: (event: KeyboardEvent) => void\n}\n\n/**\n * Ref handle for imperative control\n */\nexport interface WheelSelectRef {\n /** Open the picker programmatically */\n open: () => void\n /** Close the picker programmatically */\n close: () => void\n /** Toggle the picker */\n toggle: () => void\n /** Focus the trigger */\n focus: () => void\n /** Get current open state */\n isOpen: () => boolean\n /** Scroll to a specific option by index */\n scrollToIndex: (index: number) => void\n /** Get the underlying native select element */\n getNativeSelect: () => HTMLSelectElement | null\n}\n\n/**\n * Main component props\n */\nexport interface WheelSelectProps<T extends string = string> {\n /** Array of options */\n options: WheelSelectOption<T>[]\n /** Currently selected value */\n value: T\n /** Change handler */\n onChange: (value: T) => void\n /** Placeholder text when no value selected */\n placeholder?: string\n /** Disabled state */\n disabled?: boolean\n /** Required field indicator */\n required?: boolean\n /** Name attribute for form submission */\n name?: string\n /** ID attribute */\n id?: string\n /** Additional CSS class */\n className?: string\n /** Inline styles */\n style?: CSSProperties\n /** Theme configuration */\n theme?: WheelSelectTheme\n /** Sizing configuration */\n sizing?: WheelSelectSizing\n /** Behavior configuration */\n behavior?: WheelSelectBehavior\n /** Custom icons */\n icons?: WheelSelectIcons\n /** Accessibility configuration */\n a11y?: WheelSelectA11y\n /** Event callbacks */\n callbacks?: WheelSelectCallbacks<T>\n /** Custom trigger renderer */\n renderTrigger?: (props: {\n value: T\n label: string\n isOpen: boolean\n disabled: boolean\n onClick: () => void\n }) => ReactNode\n /** Custom option renderer */\n renderOption?: (props: {\n option: WheelSelectOption<T>\n index: number\n isActive: boolean\n isSelected: boolean\n }) => ReactNode\n /** Z-index for the picker overlay */\n zIndex?: number\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\nconst defaultTheme: Required<WheelSelectTheme> = {\n colorScheme: 'dark',\n colors: {\n text: 'inherit',\n textMuted: 'inherit',\n activeBg: 'rgba(255, 255, 255, 0.12)',\n hoverBg: 'rgba(255, 255, 255, 0.12)',\n backdropBg: 'rgba(0, 0, 0, 0.5)',\n focusRing: 'rgba(255, 255, 255, 0.5)',\n },\n borderRadius: 12,\n font: {\n family: 'inherit',\n size: 28,\n weight: 500,\n triggerSize: 'inherit',\n },\n animation: {\n duration: 200,\n easing: 'ease',\n disabled: false,\n },\n spacing: {\n triggerGap: 16,\n triggerPadding: '8px 16px',\n optionGap: 16,\n optionPadding: '0 20px',\n },\n}\n\nconst defaultSizing: Required<WheelSelectSizing> = {\n wheelHeight: 320,\n wheelMinWidth: 220,\n optionHeight: 56,\n iconSize: 20,\n}\n\nconst defaultBehavior: Required<WheelSelectBehavior> = {\n closeOnOutsideClick: true,\n closeOnEscape: true,\n closeOnSelect: true,\n scrollDebounceMs: 50,\n keyboardNavigation: true,\n portalTarget: null,\n focusTriggerOnClose: true,\n}\n\n// ============================================================================\n// Icon Components\n// ============================================================================\n\ninterface IconProps {\n size?: number\n className?: string\n}\n\nconst DefaultChevronIcon = ({ size = 20, className }: IconProps) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <polyline points=\"8 9 12 5 16 9\" />\n <polyline points=\"8 15 12 19 16 15\" />\n </svg>\n)\n\nconst DefaultArrowIcon = ({ size = 20, className }: IconProps) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"19\" y1=\"12\" x2=\"5\" y2=\"12\" />\n <polyline points=\"12 19 5 12 12 5\" />\n </svg>\n)\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\nconst toCssValue = (value: number | string | undefined, defaultValue: string = '0'): string => {\n if (value === undefined) return defaultValue\n return typeof value === 'number' ? `${value}px` : value\n}\n\nconst mergeDeep = <T extends Record<string, unknown>>(target: T, source: Partial<T>): T => {\n const result = { ...target }\n for (const key in source) {\n if (source[key] !== undefined) {\n if (\n typeof source[key] === 'object' &&\n source[key] !== null &&\n !Array.isArray(source[key])\n ) {\n result[key] = mergeDeep(\n target[key] as Record<string, unknown>,\n source[key] as Record<string, unknown>\n ) as T[Extract<keyof T, string>]\n } else {\n result[key] = source[key] as T[Extract<keyof T, string>]\n }\n }\n }\n return result\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nfunction WheelSelectInner<T extends string = string>(\n props: WheelSelectProps<T>,\n ref: React.ForwardedRef<WheelSelectRef>\n) {\n const {\n options,\n value,\n onChange,\n placeholder = 'Select...',\n disabled = false,\n required = false,\n name,\n id,\n className = '',\n style,\n theme: themeProp,\n sizing: sizingProp,\n behavior: behaviorProp,\n icons,\n a11y,\n callbacks,\n renderTrigger,\n renderOption,\n zIndex = 10001,\n } = props\n\n // Merge configurations with defaults\n const theme = useMemo(\n () => mergeDeep(defaultTheme, themeProp || {}),\n [themeProp]\n )\n const sizing = useMemo(\n () => mergeDeep(defaultSizing, sizingProp || {}),\n [sizingProp]\n )\n const behavior = useMemo(\n () => mergeDeep(defaultBehavior, behaviorProp || {}),\n [behaviorProp]\n )\n\n // State\n const [isOpen, setIsOpen] = useState(false)\n const [activeIndex, setActiveIndex] = useState(() =>\n Math.max(0, options.findIndex(o => o.value === value))\n )\n const [triggerRect, setTriggerRect] = useState<DOMRect | null>(null)\n\n // Refs\n const triggerRef = useRef<HTMLButtonElement>(null)\n const selectRef = useRef<HTMLSelectElement>(null)\n const wheelRef = useRef<HTMLDivElement>(null)\n const itemRefs = useRef<(HTMLDivElement | null)[]>([])\n const initialScrollIndexRef = useRef<number | null>(null)\n const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const isScrollingRef = useRef(false)\n\n // Computed values\n const selectedOption = useMemo(\n () => options.find(o => o.value === value),\n [options, value]\n )\n const displayLabel = selectedOption?.label ?? placeholder\n\n // Keep active index in sync when value changes externally\n useEffect(() => {\n const idx = options.findIndex(o => o.value === value)\n if (idx !== -1) {\n setActiveIndex(idx)\n }\n }, [value, options])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n }\n }\n }, [])\n\n // Calculate spacer height based on wheel and option heights\n const spacerHeight = useMemo(() => {\n const wheelH = typeof sizing.wheelHeight === 'number' ? sizing.wheelHeight : 320\n const optionH = typeof sizing.optionHeight === 'number' ? sizing.optionHeight : 56\n return (wheelH / 2) - (optionH / 2)\n }, [sizing.wheelHeight, sizing.optionHeight])\n\n // Open picker\n const openPicker = useCallback(() => {\n if (disabled) return\n if (triggerRef.current) {\n setTriggerRect(triggerRef.current.getBoundingClientRect())\n }\n const idx = options.findIndex(o => o.value === value)\n const targetIndex = idx !== -1 ? idx : 0\n initialScrollIndexRef.current = targetIndex\n setActiveIndex(targetIndex)\n setIsOpen(true)\n callbacks?.onOpen?.()\n }, [disabled, options, value, callbacks])\n\n // Close picker\n const closePicker = useCallback(() => {\n setIsOpen(false)\n if (behavior.focusTriggerOnClose) {\n triggerRef.current?.focus()\n }\n callbacks?.onClose?.()\n }, [behavior.focusTriggerOnClose, callbacks])\n\n // Commit selection\n const commitSelection = useCallback((newValue: T) => {\n const option = options.find(o => o.value === newValue)\n if (!option || option.disabled) return\n\n onChange(newValue)\n callbacks?.onChange?.(newValue, option)\n\n if (selectRef.current) {\n selectRef.current.value = newValue\n }\n\n if (behavior.closeOnSelect) {\n setIsOpen(false)\n if (behavior.focusTriggerOnClose) {\n triggerRef.current?.focus()\n }\n callbacks?.onClose?.()\n }\n }, [onChange, options, behavior.closeOnSelect, behavior.focusTriggerOnClose, callbacks])\n\n // Scroll to selected item when picker opens\n useEffect(() => {\n if (isOpen && initialScrollIndexRef.current !== null) {\n const targetIndex = initialScrollIndexRef.current\n initialScrollIndexRef.current = null\n\n requestAnimationFrame(() => {\n const wheel = wheelRef.current\n const item = itemRefs.current[targetIndex]\n if (wheel && item) {\n const wheelHeight = wheel.clientHeight\n const itemHeight = item.clientHeight\n const itemTop = item.offsetTop\n const scrollTarget = itemTop - (wheelHeight / 2) + (itemHeight / 2)\n wheel.scrollTop = scrollTarget\n }\n })\n }\n }, [isOpen])\n\n // Focus wheel when picker opens\n useEffect(() => {\n if (isOpen && wheelRef.current) {\n wheelRef.current.focus()\n }\n }, [isOpen])\n\n // Calculate active index from scroll position\n const calculateActiveFromScroll = useCallback(() => {\n const wheel = wheelRef.current\n if (!wheel) return\n\n const wheelRect = wheel.getBoundingClientRect()\n const wheelCenter = wheelRect.top + wheelRect.height / 2\n\n let closestIndex = 0\n let closestDistance = Infinity\n\n itemRefs.current.forEach((item, index) => {\n if (!item) return\n const itemRect = item.getBoundingClientRect()\n const itemCenter = itemRect.top + itemRect.height / 2\n const distance = Math.abs(itemCenter - wheelCenter)\n\n if (distance < closestDistance) {\n closestDistance = distance\n closestIndex = index\n }\n })\n\n setActiveIndex(closestIndex)\n const option = options[closestIndex]\n if (option) {\n callbacks?.onActiveChange?.(closestIndex, option)\n }\n }, [options, callbacks])\n\n // Handle scroll with debounce\n const handleScroll = useCallback(() => {\n isScrollingRef.current = true\n\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n }\n\n scrollTimeoutRef.current = setTimeout(() => {\n isScrollingRef.current = false\n calculateActiveFromScroll()\n }, behavior.scrollDebounceMs)\n }, [calculateActiveFromScroll, behavior.scrollDebounceMs])\n\n // Keyboard navigation\n const handlePickerKeyDown = useCallback((e: KeyboardEvent) => {\n if (!behavior.keyboardNavigation) return\n\n callbacks?.onKeyDown?.(e)\n\n switch (e.key) {\n case 'Escape':\n if (behavior.closeOnEscape) {\n e.preventDefault()\n closePicker()\n }\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex(prev => {\n let newIndex = prev - 1\n // Skip disabled options\n while (newIndex >= 0 && options[newIndex]?.disabled) {\n newIndex--\n }\n if (newIndex < 0) return prev\n const item = itemRefs.current[newIndex]\n item?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n return newIndex\n })\n break\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex(prev => {\n let newIndex = prev + 1\n // Skip disabled options\n while (newIndex < options.length && options[newIndex]?.disabled) {\n newIndex++\n }\n if (newIndex >= options.length) return prev\n const item = itemRefs.current[newIndex]\n item?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n return newIndex\n })\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n const option = options[activeIndex]\n if (option && !option.disabled) {\n commitSelection(option.value)\n }\n break\n case 'Home':\n e.preventDefault()\n const firstEnabled = options.findIndex(o => !o.disabled)\n if (firstEnabled !== -1) {\n setActiveIndex(firstEnabled)\n itemRefs.current[firstEnabled]?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }\n break\n case 'End':\n e.preventDefault()\n const lastEnabled = options.findLastIndex(o => !o.disabled)\n if (lastEnabled !== -1) {\n setActiveIndex(lastEnabled)\n itemRefs.current[lastEnabled]?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }\n break\n }\n }, [behavior.keyboardNavigation, behavior.closeOnEscape, options, activeIndex, closePicker, commitSelection, callbacks])\n\n // Handle item click\n const handleItemClick = useCallback((index: number) => (e: MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n scrollTimeoutRef.current = null\n }\n const option = options[index]\n if (option && !option.disabled) {\n commitSelection(option.value)\n }\n }, [options, commitSelection])\n\n // Handle backdrop click\n const handleBackdropClick = useCallback((e: MouseEvent) => {\n if (behavior.closeOnOutsideClick && e.target === e.currentTarget) {\n closePicker()\n }\n }, [behavior.closeOnOutsideClick, closePicker])\n\n // Handle native select change\n const handleNativeChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n onChange(e.target.value as T)\n }, [onChange])\n\n // Scroll to specific index\n const scrollToIndex = useCallback((index: number) => {\n const item = itemRefs.current[index]\n item?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }, [])\n\n // Imperative handle\n useImperativeHandle(ref, () => ({\n open: openPicker,\n close: closePicker,\n toggle: () => isOpen ? closePicker() : openPicker(),\n focus: () => triggerRef.current?.focus(),\n isOpen: () => isOpen,\n scrollToIndex,\n getNativeSelect: () => selectRef.current,\n }), [isOpen, openPicker, closePicker, scrollToIndex])\n\n // Generate CSS custom properties\n const cssVariables = useMemo((): CSSProperties => ({\n '--ws-color-text': theme.colors.text,\n '--ws-color-text-muted': theme.colors.textMuted,\n '--ws-color-active-bg': theme.colors.activeBg,\n '--ws-color-hover-bg': theme.colors.hoverBg,\n '--ws-color-backdrop-bg': theme.colors.backdropBg,\n '--ws-color-focus-ring': theme.colors.focusRing,\n '--ws-border-radius': toCssValue(theme.borderRadius),\n '--ws-font-family': theme.font.family,\n '--ws-font-size': toCssValue(theme.font.size),\n '--ws-font-weight': String(theme.font.weight),\n '--ws-font-size-trigger': toCssValue(theme.font.triggerSize),\n '--ws-animation-duration': theme.animation.disabled ? '0ms' : `${theme.animation.duration}ms`,\n '--ws-animation-easing': theme.animation.easing,\n '--ws-trigger-gap': toCssValue(theme.spacing.triggerGap),\n '--ws-trigger-padding': theme.spacing.triggerPadding,\n '--ws-option-gap': toCssValue(theme.spacing.optionGap),\n '--ws-option-padding': theme.spacing.optionPadding,\n '--ws-wheel-height': toCssValue(sizing.wheelHeight),\n '--ws-wheel-min-width': toCssValue(sizing.wheelMinWidth),\n '--ws-option-height': toCssValue(sizing.optionHeight),\n '--ws-icon-size': `${sizing.iconSize}px`,\n '--ws-spacer-height': `${spacerHeight}px`,\n '--ws-z-index': String(zIndex),\n } as CSSProperties), [theme, sizing, spacerHeight, zIndex])\n\n // Determine color scheme class\n const colorSchemeClass = theme.colorScheme === 'auto'\n ? ''\n : theme.colorScheme === 'light'\n ? 'ws-light'\n : 'ws-dark'\n\n // Render the picker portal\n const renderPicker = () => {\n if (!isOpen || !triggerRect) return null\n\n const portalTarget = behavior.portalTarget ?? document.body\n\n return createPortal(\n <div\n className=\"ws-backdrop\"\n onClick={handleBackdropClick}\n role=\"presentation\"\n style={cssVariables}\n >\n <div\n className=\"ws-picker\"\n style={{\n left: triggerRect.left,\n top: triggerRect.top + triggerRect.height / 2,\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={a11y?.pickerLabel ?? 'Select an option'}\n >\n <div\n ref={wheelRef}\n className=\"ws-wheel\"\n role=\"listbox\"\n tabIndex={0}\n onKeyDown={handlePickerKeyDown}\n onScroll={handleScroll}\n aria-activedescendant={`ws-option-${id ?? 'default'}-${activeIndex}`}\n aria-describedby={a11y?.describedBy}\n >\n <div className=\"ws-spacer\" aria-hidden=\"true\" />\n\n {options.map((option, index) => {\n const isActive = index === activeIndex\n const isSelected = option.value === value\n\n if (renderOption) {\n return (\n <div\n key={option.value}\n ref={el => { itemRefs.current[index] = el }}\n id={`ws-option-${id ?? 'default'}-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n className={`ws-option ${isActive ? 'ws-active' : ''} ${option.disabled ? 'ws-disabled' : ''}`}\n onClick={handleItemClick(index)}\n >\n {renderOption({ option, index, isActive, isSelected })}\n </div>\n )\n }\n\n return (\n <div\n key={option.value}\n ref={el => { itemRefs.current[index] = el }}\n id={`ws-option-${id ?? 'default'}-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n className={`ws-option ${isActive ? 'ws-active' : ''} ${option.disabled ? 'ws-disabled' : ''}`}\n onClick={handleItemClick(index)}\n >\n <span className=\"ws-option-text\">{option.label}</span>\n {isActive && !icons?.hideArrow && (\n icons?.arrow ?? <DefaultArrowIcon size={sizing.iconSize} className=\"ws-arrow\" />\n )}\n </div>\n )\n })}\n\n <div className=\"ws-spacer\" aria-hidden=\"true\" />\n </div>\n </div>\n </div>,\n portalTarget\n )\n }\n\n return (\n <span\n className={`ws-root ${colorSchemeClass} ${className}`}\n style={{ ...cssVariables, ...style }}\n >\n {/* Hidden native select for form submission */}\n <select\n ref={selectRef}\n name={name}\n id={id ? `${id}-native` : undefined}\n value={value}\n onChange={handleNativeChange}\n className=\"ws-native-select\"\n tabIndex={-1}\n aria-hidden=\"true\"\n required={required}\n disabled={disabled}\n >\n {!selectedOption && <option value=\"\">{placeholder}</option>}\n {options.map(option => (\n <option\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n >\n {option.label}\n </option>\n ))}\n </select>\n\n {/* Trigger button */}\n {renderTrigger ? (\n renderTrigger({\n value,\n label: displayLabel,\n isOpen,\n disabled,\n onClick: openPicker,\n })\n ) : (\n <button\n ref={triggerRef}\n type=\"button\"\n id={id}\n className={`ws-trigger ${isOpen ? 'ws-open' : ''}`}\n onClick={openPicker}\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-label={a11y?.triggerLabel}\n aria-describedby={a11y?.describedBy}\n style={{ visibility: isOpen ? 'hidden' : 'visible' }}\n >\n <span className=\"ws-trigger-text\">{displayLabel}</span>\n {!icons?.hideChevron && (\n icons?.chevron ?? <DefaultChevronIcon size={sizing.iconSize} className=\"ws-chevron\" />\n )}\n </button>\n )}\n\n {/* Picker portal */}\n {renderPicker()}\n </span>\n )\n}\n\n// Forward ref with generic support\nexport const WheelSelect = forwardRef(WheelSelectInner) as <T extends string = string>(\n props: WheelSelectProps<T> & { ref?: React.ForwardedRef<WheelSelectRef> }\n) => ReturnType<typeof WheelSelectInner>\n\nexport default WheelSelect\n","import {\n useState,\n useRef,\n useEffect,\n useCallback, \n type KeyboardEvent,\n type MouseEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport './BaseSelectCompat.css'\n\nexport interface SelectOption {\n value: string\n label: string\n}\n\nexport interface BaseSelectCompatProps {\n options: SelectOption[]\n value: string\n onChange: (value: string) => void\n name?: string\n id?: string\n className?: string\n}\n\n// Chevron up/down icon for closed state (diamond/double chevron style)\nconst ChevronIcon = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"base-select-chevron\"\n >\n <polyline points=\"8 9 12 5 16 9\" />\n <polyline points=\"8 15 12 19 16 15\" />\n </svg>\n)\n\n// Left arrow icon for active item in open state\nconst ArrowLeftIcon = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"base-select-arrow\"\n >\n <line x1=\"19\" y1=\"12\" x2=\"5\" y2=\"12\" />\n <polyline points=\"12 19 5 12 12 5\" />\n </svg>\n)\n\n/**\n * BaseSelectCompat - A progressively enhanced select component\n *\n * Uses native stylable select (appearance: base-select) when supported,\n * falls back to a custom inline wheel picker for older browsers.\n */\nexport function BaseSelectCompat({\n options,\n value,\n onChange,\n name,\n id,\n className = '',\n}: BaseSelectCompatProps) {\n // Always use fallback mode for consistent custom styling across browsers\n // The native base-select doesn't match our design requirements\n const useNative = false\n const [isOpen, setIsOpen] = useState(false)\n const [activeIndex, setActiveIndex] = useState(() =>\n Math.max(0, options.findIndex(o => o.value === value))\n )\n\n const triggerRef = useRef<HTMLButtonElement>(null)\n const selectRef = useRef<HTMLSelectElement>(null)\n const wheelRef = useRef<HTMLDivElement>(null)\n const itemRefs = useRef<(HTMLDivElement | null)[]>([])\n\n\n // Track trigger position for inline picker placement\n const [triggerRect, setTriggerRect] = useState<DOMRect | null>(null)\n\n // Keep active index in sync when value changes externally\n useEffect(() => {\n const idx = options.findIndex(o => o.value === value)\n if (idx !== -1) {\n setActiveIndex(idx)\n }\n }, [value, options])\n\n // Get the current selected label\n const selectedOption = options.find(o => o.value === value)\n const displayLabel = selectedOption?.label ?? options[0]?.label ?? ''\n\n // Track the initial index to scroll to when opening\n const initialScrollIndexRef = useRef<number | null>(null)\n\n // Handle opening the picker\n const openPicker = useCallback(() => {\n if (triggerRef.current) {\n setTriggerRect(triggerRef.current.getBoundingClientRect())\n }\n // Calculate and store the index to scroll to\n const idx = options.findIndex(o => o.value === value)\n const targetIndex = idx !== -1 ? idx : 0\n initialScrollIndexRef.current = targetIndex\n setActiveIndex(targetIndex)\n setIsOpen(true)\n }, [options, value])\n\n // Handle closing without selection\n const closePicker = useCallback(() => {\n setIsOpen(false)\n triggerRef.current?.focus()\n }, [])\n\n // Handle selection commit\n const commitSelection = useCallback((newValue: string) => {\n onChange(newValue)\n // Sync with native select\n if (selectRef.current) {\n selectRef.current.value = newValue\n }\n setIsOpen(false)\n triggerRef.current?.focus()\n }, [onChange])\n\n // Scroll to the selected item when picker opens (only once)\n useEffect(() => {\n if (isOpen && initialScrollIndexRef.current !== null) {\n const targetIndex = initialScrollIndexRef.current\n // Clear the ref so we don't scroll again\n initialScrollIndexRef.current = null\n\n // Use requestAnimationFrame to ensure DOM is ready\n requestAnimationFrame(() => {\n const wheel = wheelRef.current\n const item = itemRefs.current[targetIndex]\n if (wheel && item) {\n // Center the item in the wheel (instant scroll, no smooth)\n const wheelHeight = wheel.clientHeight\n const itemHeight = item.clientHeight\n const itemTop = item.offsetTop\n const scrollTarget = itemTop - (wheelHeight / 2) + (itemHeight / 2)\n wheel.scrollTop = scrollTarget\n }\n })\n }\n }, [isOpen])\n\n /**\n * Calculate which item is closest to the center of the scroll container.\n *\n * Math explanation:\n * 1. Find the vertical center of the scroll container viewport\n * 2. For each item, calculate its center position relative to scroll\n * 3. Find the item whose center is closest to the viewport center\n */\n const calculateActiveFromScroll = useCallback(() => {\n const wheel = wheelRef.current\n if (!wheel) return\n\n const wheelRect = wheel.getBoundingClientRect()\n const wheelCenter = wheelRect.top + wheelRect.height / 2\n\n let closestIndex = 0\n let closestDistance = Infinity\n\n itemRefs.current.forEach((item, index) => {\n if (!item) return\n const itemRect = item.getBoundingClientRect()\n const itemCenter = itemRect.top + itemRect.height / 2\n const distance = Math.abs(itemCenter - wheelCenter)\n\n if (distance < closestDistance) {\n closestDistance = distance\n closestIndex = index\n }\n })\n\n setActiveIndex(closestIndex)\n }, [])\n\n // Track scroll state to avoid interference with clicks\n const isScrollingRef = useRef(false)\n const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n /**\n * Handle scroll events - only update visual highlight, don't auto-commit.\n * User must click an item or press Enter to commit selection.\n * Uses a timeout to detect scroll end and avoid interfering with clicks.\n */\n const handleScroll = useCallback(() => {\n isScrollingRef.current = true\n\n // Clear any existing timeout\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n }\n\n // Update active index after a short delay (scroll end detection)\n scrollTimeoutRef.current = setTimeout(() => {\n isScrollingRef.current = false\n calculateActiveFromScroll()\n }, 50)\n }, [calculateActiveFromScroll])\n\n\n // Handle keyboard navigation in picker\n const handlePickerKeyDown = useCallback((e: KeyboardEvent) => {\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n closePicker()\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex(prev => {\n const newIndex = Math.max(0, prev - 1)\n // Scroll the new active item into view\n const item = itemRefs.current[newIndex]\n item?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n return newIndex\n })\n break\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex(prev => {\n const newIndex = Math.min(options.length - 1, prev + 1)\n const item = itemRefs.current[newIndex]\n item?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n return newIndex\n })\n break\n case 'Enter':\n e.preventDefault()\n if (options[activeIndex]) {\n commitSelection(options[activeIndex].value)\n }\n break\n }\n }, [activeIndex, options, closePicker, commitSelection])\n\n // Handle clicking on an item\n const handleItemSelect = useCallback((index: number) => {\n if (options[index]) {\n commitSelection(options[index].value)\n }\n }, [options, commitSelection])\n\n // Use onClick for click detection - more reliable than pointer events\n const handleItemClick = useCallback((index: number) => (e: React.MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n // Clear any pending scroll timeout to prevent interference\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n scrollTimeoutRef.current = null\n }\n handleItemSelect(index)\n }, [handleItemSelect])\n\n // Fallback: handle click on wheel using event delegation\n const handleWheelClick = useCallback((e: MouseEvent) => {\n const target = e.target as HTMLElement\n const optionEl = target.closest('.base-select-option') as HTMLElement\n if (optionEl) {\n const optionId = optionEl.id // e.g., \"option-3\"\n const index = parseInt(optionId.replace('option-', ''), 10)\n if (!isNaN(index) && options[index]) {\n handleItemSelect(index)\n }\n }\n }, [options, handleItemSelect])\n\n // Handle backdrop click\n const handleBackdropClick = useCallback((e: MouseEvent) => {\n if (e.target === e.currentTarget) {\n closePicker()\n }\n }, [closePicker])\n\n // Focus trap - focus the wheel when picker opens\n useEffect(() => {\n if (isOpen && wheelRef.current) {\n wheelRef.current.focus()\n }\n }, [isOpen])\n\n // Cleanup scroll timeout on unmount or when picker closes\n useEffect(() => {\n return () => {\n if (scrollTimeoutRef.current) {\n clearTimeout(scrollTimeoutRef.current)\n }\n }\n }, [])\n\n // Handle native select change (for native mode)\n const handleNativeChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n onChange(e.target.value)\n }, [onChange])\n\n // Render the fallback inline wheel picker\n const renderFallbackPicker = () => {\n if (!isOpen || !triggerRect) return null\n\n return createPortal(\n <div\n className=\"base-select-backdrop\"\n onClick={handleBackdropClick}\n role=\"presentation\"\n >\n <div\n className=\"base-select-picker\"\n style={{\n // Position the picker so the active item aligns with the trigger\n left: triggerRect.left,\n top: triggerRect.top + triggerRect.height / 2,\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Select an option\"\n >\n <div\n ref={wheelRef}\n className=\"base-select-wheel\"\n role=\"listbox\"\n tabIndex={0}\n onKeyDown={handlePickerKeyDown}\n onScroll={handleScroll}\n onClick={handleWheelClick}\n aria-activedescendant={`option-${activeIndex}`}\n >\n {/* Spacer to allow first item to be centered */}\n <div className=\"base-select-spacer\" aria-hidden=\"true\" />\n\n {options.map((option, index) => (\n <div\n key={option.value}\n ref={el => { itemRefs.current[index] = el }}\n id={`option-${index}`}\n role=\"option\"\n aria-selected={index === activeIndex}\n className={`base-select-option ${index === activeIndex ? 'active' : ''}`}\n onClick={handleItemClick(index)}\n >\n <span className=\"base-select-option-text\">{option.label}</span>\n {index === activeIndex && <ArrowLeftIcon />}\n </div>\n ))}\n\n {/* Spacer to allow last item to be centered */}\n <div className=\"base-select-spacer\" aria-hidden=\"true\" />\n </div>\n </div>\n </div>,\n document.body\n )\n }\n\n // Native mode with appearance: base-select\n if (useNative) {\n return (\n <span className={`base-select-compat native ${className}`}>\n <select\n ref={selectRef}\n name={name}\n id={id}\n value={value}\n onChange={handleNativeChange}\n className=\"base-select-native\"\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </span>\n )\n }\n\n // Fallback mode with custom inline wheel picker\n return (\n <span className={`base-select-compat fallback ${className}`}>\n {/* Hidden native select for form submission and semantics */}\n <select\n ref={selectRef}\n name={name}\n id={id}\n value={value}\n onChange={handleNativeChange}\n className=\"base-select-hidden\"\n tabIndex={-1}\n aria-hidden=\"true\"\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n\n {/* Text trigger with pill background on hover - hidden when picker is open */}\n <button\n ref={triggerRef}\n type=\"button\"\n className={`base-select-trigger ${isOpen ? 'open' : ''}`}\n onClick={openPicker}\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n style={{ visibility: isOpen ? 'hidden' : 'visible' }}\n >\n <span className=\"base-select-trigger-text\">{displayLabel}</span>\n <ChevronIcon />\n </button>\n\n {/* Fallback inline picker */}\n {renderFallbackPicker()}\n </span>\n )\n}\n\nexport default BaseSelectCompat\n"]}