11import React from "react" ;
2- import type { ReactElement } from "react" ;
3- import { Box } from "@chakra-ui/layout" ;
2+ import type { IconProps } from "@chakra-ui/icon" ;
3+ import { Icon } from "@chakra-ui/icon" ;
4+ import { Box , Divider } from "@chakra-ui/layout" ;
5+ import { Spinner } from "@chakra-ui/spinner" ;
46import type { SystemStyleObject } from "@chakra-ui/system" ;
5- import { StylesProvider , useMultiStyleConfig } from "@chakra-ui/system" ;
6- import type { ControlProps , GroupBase } from "react-select" ;
7+ import {
8+ createStylesContext ,
9+ useMultiStyleConfig ,
10+ useStyleConfig ,
11+ } from "@chakra-ui/system" ;
12+ import type {
13+ ClearIndicatorProps ,
14+ ControlProps ,
15+ DropdownIndicatorProps ,
16+ GroupBase ,
17+ IndicatorSeparatorProps ,
18+ LoadingIndicatorProps ,
19+ } from "react-select" ;
720import type { SizeProps } from "../types" ;
821
22+ const [ InputStylesProvider , useInputStyles ] = createStylesContext ( "Input" ) ;
23+
924const Control = <
1025 Option ,
1126 IsMulti extends boolean ,
1227 Group extends GroupBase < Option >
1328> (
1429 props : ControlProps < Option , IsMulti , Group >
15- ) : ReactElement => {
30+ ) => {
1631 const {
1732 className,
1833 cx,
@@ -57,7 +72,7 @@ const Control = <
5772 : initialStyles ;
5873
5974 return (
60- < StylesProvider value = { inputStyles } >
75+ < InputStylesProvider value = { inputStyles } >
6176 < Box
6277 ref = { innerRef }
6378 className = { cx (
@@ -78,7 +93,250 @@ const Control = <
7893 >
7994 { children }
8095 </ Box >
81- </ StylesProvider >
96+ </ InputStylesProvider >
97+ ) ;
98+ } ;
99+
100+ export const IndicatorSeparator = <
101+ Option ,
102+ IsMulti extends boolean ,
103+ Group extends GroupBase < Option >
104+ > (
105+ props : IndicatorSeparatorProps < Option , IsMulti , Group >
106+ ) => {
107+ const {
108+ className,
109+ cx,
110+ selectProps : { chakraStyles, useBasicStyles } ,
111+ } = props ;
112+
113+ const initialStyles : SystemStyleObject = {
114+ opacity : 1 ,
115+ ...( useBasicStyles && { display : "none" } ) ,
116+ } ;
117+
118+ const sx : SystemStyleObject = chakraStyles ?. indicatorSeparator
119+ ? chakraStyles . indicatorSeparator ( initialStyles , props )
120+ : initialStyles ;
121+
122+ return (
123+ < Divider
124+ className = { cx ( { "indicator-separator" : true } , className ) }
125+ sx = { sx }
126+ orientation = "vertical"
127+ />
128+ ) ;
129+ } ;
130+
131+ /**
132+ * Borrowed from the `@chakra-ui/icons` package to prevent needing it as a dependency
133+ *
134+ * @see {@link https://github.com/chakra-ui/chakra-ui/blob/main/packages/icons/src/ChevronDown.tsx }
135+ */
136+ export const DownChevron = ( props : IconProps ) => (
137+ < Icon { ...props } >
138+ < path
139+ fill = "currentColor"
140+ d = "M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
141+ />
142+ </ Icon >
143+ ) ;
144+
145+ export const DropdownIndicator = <
146+ Option ,
147+ IsMulti extends boolean ,
148+ Group extends GroupBase < Option >
149+ > (
150+ props : DropdownIndicatorProps < Option , IsMulti , Group >
151+ ) => {
152+ const {
153+ children,
154+ className,
155+ cx,
156+ innerProps,
157+ selectProps : { size, chakraStyles, useBasicStyles } ,
158+ } = props ;
159+
160+ const { addon } = useInputStyles ( ) ;
161+
162+ const iconSizes : SizeProps = {
163+ sm : "16px" ,
164+ md : "20px" ,
165+ lg : "24px" ,
166+ } ;
167+ const iconSize = iconSizes [ size || "md" ] ;
168+
169+ const initialStyles : SystemStyleObject = {
170+ ...addon ,
171+ display : "flex" ,
172+ alignItems : "center" ,
173+ justifyContent : "center" ,
174+ height : "100%" ,
175+ borderRadius : 0 ,
176+ borderWidth : 0 ,
177+ cursor : "pointer" ,
178+ fontSize : iconSize ,
179+ ...( useBasicStyles && {
180+ background : "transparent" ,
181+ padding : 0 ,
182+ width : 6 ,
183+ marginRight : 2 ,
184+ marginLeft : 1 ,
185+ cursor : "inherit" ,
186+ } ) ,
187+ } ;
188+ const sx : SystemStyleObject = chakraStyles ?. dropdownIndicator
189+ ? chakraStyles . dropdownIndicator ( initialStyles , props )
190+ : initialStyles ;
191+
192+ const initialIconStyles = {
193+ height : "1em" ,
194+ width : "1em" ,
195+ } ;
196+ const iconSx : SystemStyleObject = chakraStyles ?. downChevron
197+ ? chakraStyles . downChevron ( initialIconStyles , props )
198+ : initialIconStyles ;
199+
200+ return (
201+ < Box
202+ { ...innerProps }
203+ className = { cx (
204+ {
205+ indicator : true ,
206+ "dropdown-indicator" : true ,
207+ } ,
208+ className
209+ ) }
210+ sx = { sx }
211+ >
212+ { children || < DownChevron sx = { iconSx } /> }
213+ </ Box >
214+ ) ;
215+ } ;
216+
217+ /**
218+ * Borrowed from Chakra UI source
219+ *
220+ * @see {@link https://github.com/chakra-ui/chakra-ui/blob/13c6d2e08b61e179773be4722bb81173dd599306/packages/close-button/src/close-button.tsx#L14 }
221+ */
222+ export const CrossIcon = ( props : IconProps ) => (
223+ < Icon focusable = "false" aria-hidden { ...props } >
224+ < path
225+ fill = "currentColor"
226+ d = "M.439,21.44a1.5,1.5,0,0,0,2.122,2.121L11.823,14.3a.25.25,0,0,1,.354,0l9.262,9.263a1.5,1.5,0,1,0,2.122-2.121L14.3,12.177a.25.25,0,0,1,0-.354l9.263-9.262A1.5,1.5,0,0,0,21.439.44L12.177,9.7a.25.25,0,0,1-.354,0L2.561.44A1.5,1.5,0,0,0,.439,2.561L9.7,11.823a.25.25,0,0,1,0,.354Z"
227+ />
228+ </ Icon >
229+ ) ;
230+
231+ export const ClearIndicator = <
232+ Option ,
233+ IsMulti extends boolean ,
234+ Group extends GroupBase < Option >
235+ > (
236+ props : ClearIndicatorProps < Option , IsMulti , Group >
237+ ) => {
238+ const {
239+ children,
240+ className,
241+ cx,
242+ innerProps,
243+ selectProps : { size, chakraStyles } ,
244+ } = props ;
245+
246+ const closeButtonStyles = useStyleConfig ( "CloseButton" , {
247+ size,
248+ } ) ;
249+
250+ const initialStyles : SystemStyleObject = {
251+ ...closeButtonStyles ,
252+ marginX : 1 ,
253+ display : "flex" ,
254+ alignItems : "center" ,
255+ justifyContent : "center" ,
256+ flexShrink : 0 ,
257+ cursor : "pointer" ,
258+ } ;
259+ const sx : SystemStyleObject = chakraStyles ?. clearIndicator
260+ ? chakraStyles . clearIndicator ( initialStyles , props )
261+ : initialStyles ;
262+
263+ const initialIconStyles : SystemStyleObject = {
264+ width : "1em" ,
265+ height : "1em" ,
266+ } ;
267+ const iconSx : SystemStyleObject = chakraStyles ?. crossIcon
268+ ? chakraStyles . crossIcon ( initialIconStyles , props )
269+ : initialIconStyles ;
270+
271+ return (
272+ < Box
273+ role = "button"
274+ className = { cx (
275+ {
276+ indicator : true ,
277+ "clear-indicator" : true ,
278+ } ,
279+ className
280+ ) }
281+ sx = { sx }
282+ aria-label = "Clear selected options"
283+ { ...innerProps }
284+ >
285+ { children || < CrossIcon sx = { iconSx } /> }
286+ </ Box >
287+ ) ;
288+ } ;
289+
290+ export const LoadingIndicator = <
291+ Option ,
292+ IsMulti extends boolean ,
293+ Group extends GroupBase < Option >
294+ > (
295+ props : LoadingIndicatorProps < Option , IsMulti , Group >
296+ ) => {
297+ const {
298+ className,
299+ cx,
300+ innerProps,
301+ selectProps : { size, chakraStyles } ,
302+ color,
303+ emptyColor,
304+ speed,
305+ thickness,
306+ spinnerSize : propsSpinnerSize ,
307+ } = props ;
308+
309+ const spinnerSizes : SizeProps < string > = {
310+ sm : "xs" ,
311+ md : "sm" ,
312+ lg : "md" ,
313+ } ;
314+
315+ const spinnerSize = spinnerSizes [ size || "md" ] ;
316+
317+ const initialStyles : SystemStyleObject = { marginRight : 3 } ;
318+
319+ const sx : SystemStyleObject = chakraStyles ?. loadingIndicator
320+ ? chakraStyles . loadingIndicator ( initialStyles , props )
321+ : initialStyles ;
322+
323+ return (
324+ < Spinner
325+ className = { cx (
326+ {
327+ indicator : true ,
328+ "loading-indicator" : true ,
329+ } ,
330+ className
331+ ) }
332+ sx = { sx }
333+ { ...innerProps }
334+ size = { propsSpinnerSize || spinnerSize }
335+ color = { color }
336+ emptyColor = { emptyColor }
337+ speed = { speed }
338+ thickness = { thickness }
339+ />
82340 ) ;
83341} ;
84342
0 commit comments