nitro-web 0.0.15 → 0.0.17

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.
@@ -1,56 +1,56 @@
1
- // @ts-nocheck
2
- // todo: finish tailwind conversion
3
- import { css } from 'twin.macro'
4
- import { DayPicker } from 'react-day-picker'
5
1
  import { format, isValid, parse } from 'date-fns'
6
- import { getCurrencyPrefixWidth } from 'nitro-web/util'
7
- import { Dropdown } from 'nitro-web'
8
- import 'react-day-picker/dist/style.css'
2
+ import { getPrefixWidth } from 'nitro-web/util'
3
+ import { Calendar, Dropdown } from 'nitro-web'
9
4
 
10
- export function InputDate({ className, prefix, id, onChange, mode='single', value, ...props }) {
11
- /**
12
- * @param {string} mode - 'single'|'range'|'multiple' - an array is returned for non-single modes
13
- */
5
+ type Mode = 'single' | 'multiple' | 'range'
6
+ type DropdownRef = {
7
+ setIsActive: (value: boolean) => void
8
+ }
9
+ export type FieldDateProps = React.InputHTMLAttributes<HTMLInputElement> & {
10
+ name: string
11
+ id?: string
12
+ mode?: Mode
13
+ // an array is returned for non-single modes
14
+ onChange?: (e: { target: { id: string, value: null|number|(null|number)[] } }) => void
15
+ prefix?: string
16
+ value?: null|number|string|(null|number|string)[]
17
+ numberOfMonths?: number
18
+ Icon?: React.ReactNode
19
+ }
20
+
21
+ export function FieldDate({ mode='single', onChange, prefix='', value, numberOfMonths, Icon, ...props }: FieldDateProps) {
14
22
  const localePattern = 'd MMM yyyy'
15
- const isInvalid = className?.includes('is-invalid') ? 'is-invalid' : ''
16
- const [prefixWidth, setPrefixWidth] = useState()
17
- const ref = useRef(null)
23
+ const [prefixWidth, setPrefixWidth] = useState(0)
24
+ const dropdownRef = useRef<DropdownRef>(null)
25
+ const id = props.id || props.name
26
+ const [month, setMonth] = useState<number|undefined>()
18
27
 
28
+ // Convert the value to an array of valid* dates
19
29
  const dates = useMemo(() => {
20
- // Convert the value to an array of valid* dates
21
30
  const _dates = Array.isArray(value) ? value : [value]
22
- return _dates.map(date => isValid(date) ? new Date(date) : undefined)
31
+ return _dates.map(date => isValid(date) ? new Date(date as number) : null) /// change to null
23
32
  }, [value])
24
33
 
25
- // Hold the month in state to control the calendar when the input changes
26
- const [month, setMonth] = useState(dates[0])
27
-
28
34
  // Hold the input value in state
29
35
  const [inputValue, setInputValue] = useState(() => getInputValue(dates))
30
36
 
37
+ // Get the prefix content width
31
38
  useEffect(() => {
32
- // Get the prefix content width
33
- setPrefixWidth(getCurrencyPrefixWidth(prefix, 4))
39
+ setPrefixWidth(getPrefixWidth(prefix, 4))
34
40
  }, [prefix])
35
41
 
36
- function handleDayPickerSelect(newDate) {
37
- if (mode == 'single') {
38
- ref.current.setIsActive(false) // close the dropdown
39
- callOnChange(newDate?.getTime() || null)
40
- setInputValue(getInputValue([newDate]))
41
-
42
- } else if (mode == 'range') {
43
- const {from, to} = newDate || {} // may not exist
44
- callOnChange(from ? [from?.getTime() || null, to?.getTime() || null] : null)
45
- setInputValue(getInputValue(from ? [from, to] : []))
42
+ function onCalendarChange(mode: Mode, value: null|number|(null|number)[]) {
43
+ if (mode == 'single') dropdownRef.current?.setIsActive(false) // Close the dropdown
44
+ setInputValue(getInputValue(value))
45
+ if (onChange) onChange({ target: { id: id, value: value }})
46
+ }
46
47
 
47
- } else {
48
- callOnChange(newDate.filter(o => o).map(d => d.getTime()))
49
- setInputValue(getInputValue(newDate.filter(o => o)))
50
- }
48
+ function getInputValue(dates: Date|number|null|(Date|number|null)[]) {
49
+ const _dates = Array.isArray(dates) ? dates : [dates]
50
+ return _dates.map(o => o ? format(o, localePattern) : '').join(mode == 'range' ? ' - ' : ', ')
51
51
  }
52
52
 
53
- function handleInputChange(e) {
53
+ function onInputChange(e: React.ChangeEvent<HTMLInputElement>) {
54
54
  setInputValue(e.target.value) // keep the input value in sync
55
55
 
56
56
  let split = e.target.value.split(/-|,/).map(o => {
@@ -63,162 +63,50 @@ export function InputDate({ className, prefix, id, onChange, mode='single', valu
63
63
  else if (mode == 'multiple') split = split.filter(o => o) // remove invalid dates
64
64
 
65
65
  // Swap dates if needed
66
- if (mode == 'range' && split[0] > split[1]) split = [split[0], split[0]]
66
+ if (mode == 'range' && (split[0] || 0) > (split[1] || 0)) split = [split[0], split[0]]
67
67
 
68
68
  // Set month
69
69
  for (let i=split.length; i--;) {
70
- if (split[i]) setMonth(split[i])
70
+ if (split[i]) setMonth((split[i] as Date).getTime())
71
71
  break
72
72
  }
73
73
 
74
- // Set dates
75
- callOnChange(mode == 'single' ? split[0] : split)
76
- }
77
-
78
- function getInputValue(dates) {
79
- return dates.map(o => o ? format(o, localePattern) : '').join(mode == 'range' ? ' - ' : ', ')
80
- }
81
-
82
- function callOnChange(value) {
83
- if (onChange) onChange({ target: { id: id, value: value }}) // timestamp|[timestamp]
74
+ // Update
75
+ const value = mode == 'single' ? split[0]?.getTime() ?? null : split.map(d => d?.getTime() ?? null)
76
+ if (onChange) onChange({ target: { id, value }})
84
77
  }
85
78
 
86
79
  return (
87
80
  <Dropdown
88
- ref={ref}
89
- css={style}
81
+ ref={dropdownRef}
90
82
  menuToggles={false}
91
83
  animate={false}
92
84
  // menuIsOpen={true}
85
+ minWidth={0}
93
86
  menuChildren={
94
- <DayPicker
95
- mode={mode}
96
- month={month}
97
- onMonthChange={setMonth}
98
- numberOfMonths={mode == 'range' ? 2 : 1}
99
- selected={mode === 'single' ? dates[0] : mode == 'range' ? { from: dates[0], to: dates[1] } : dates}
100
- onSelect={handleDayPickerSelect}
101
- />
87
+ <Calendar {...{ mode, value, numberOfMonths, month }} onChange={onCalendarChange} className="px-3 pt-1 pb-2" />
102
88
  }
103
89
  >
104
- <div>
105
- {prefix && <span class={`input-prefix ${inputValue ? 'has-value' : ''}`}>{prefix}</span>}
90
+ <div className="grid grid-cols-1">
91
+ {Icon}
92
+ {
93
+ prefix &&
94
+ // Similar classNames to the input.tsx:IconWrapper()
95
+ <span className="relative col-start-1 row-start-1 self-center select-none z-[1] justify-self-start text-sm ml-3">{prefix}</span>
96
+ }
106
97
  <input
107
98
  {...props}
108
- key={'k'+prefixWidth}
99
+ key={'k' + prefixWidth}
109
100
  id={id}
110
101
  autoComplete="off"
111
- className={
112
- className + ' ' + isInvalid
113
- }
102
+ className={(props.className||'')}// + props.className?.includes('is-invalid') ? ' is-invalid' : ''}
114
103
  value={inputValue}
115
- onChange={handleInputChange}
104
+ onChange={onInputChange}
116
105
  onBlur={() => setInputValue(getInputValue(dates))}
117
106
  style={{ textIndent: prefixWidth + 'px' }}
107
+ type="text"
118
108
  />
119
109
  </div>
120
110
  </Dropdown>
121
111
  )
122
112
  }
123
-
124
- const style = css`
125
- .rdp {
126
- --rdp-cell-size: 34px;
127
- --rdp-caption-font-size: 12px;
128
- --rdp-accent-color: blue; /* theme('colors.primary') */
129
- font-size: 13px;
130
- margin: 0 12px 11px;
131
- svg {
132
- width: 13px;
133
- height: 13px;
134
- }
135
- .rdp-caption_label {
136
- height: var(--rdp-cell-size);
137
- }
138
- .rdp-head_cell {
139
- text-align: center !important;
140
- }
141
- tr {
142
- display: flex;
143
- justify-content: space-around;
144
- align-items: center;
145
- th,
146
- td {
147
- display: flex;
148
- align-items: center;
149
- margin-left: -1px;
150
- margin-top: -1px;
151
- .rdp-day {
152
- border: 0 !important;
153
- position: relative;
154
- border-radius: 0 !important;
155
- color: inherit;
156
- background-color: transparent !important;
157
- &:before {
158
- content: '';
159
- position: absolute;
160
- display: block;
161
- left: 0px;
162
- top: 0px;
163
- bottom: 0px;
164
- right: 0px;
165
- z-index: -1;
166
- }
167
- }
168
- .rdp-day:focus,
169
- .rdp-day:hover,
170
- .rdp-day:active {
171
- &:not([disabled]):not(.rdp-day_selected) {
172
- &:before {
173
- left: 1px;
174
- top: 1px;
175
- bottom: 1px;
176
- right: 1px;
177
- border-radius: 50%;
178
- background-color: #e7edff;
179
- }
180
- &:active {
181
- color: white;
182
- &:before {
183
- background-color: blue; /* theme('colors.primary') */
184
- }
185
- }
186
- }
187
- }
188
- .rdp-day_selected {
189
- color: white;
190
- :before {
191
- border-radius: 50%;
192
- background-color: blue; /* theme('colors.primary') */
193
- }
194
- }
195
- .rdp-day_range_middle {
196
- color: black; /* theme('colors.dark') */
197
- :before {
198
- border-radius: 0;
199
- border: 1px solid rgb(151 133 185);
200
- background-color: blue; /* theme('colors.primary-light') */
201
- }
202
- }
203
- .rdp-day_range_start,
204
- .rdp-day_range_end {
205
- position: relative;
206
- z-index: 1;
207
- &.rdp-day_range_start:before {
208
- border-top-right-radius: 0px;
209
- border-bottom-right-radius: 0px;
210
- }
211
- &.rdp-day_range_end:before {
212
- border-top-left-radius: 0px;
213
- border-bottom-left-radius: 0px;
214
- }
215
- &.rdp-day_range_start.rdp-day_range_end:before {
216
- border-radius: 50%;
217
- }
218
- }
219
- }
220
- }
221
- }
222
- `
223
-
224
-
@@ -1,134 +1,165 @@
1
- // @ts-nocheck
2
1
  import { css } from 'twin.macro'
3
- import { InputColor, InputCurrency, util } from 'nitro-web' // InputDate
4
-
2
+ import { twMerge } from 'tailwind-merge'
3
+ import { util, FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps } from 'nitro-web'
4
+ import { Errors, type Error } from 'types'
5
5
  import {
6
6
  EnvelopeIcon,
7
- // CalendarIcon,
7
+ CalendarIcon,
8
8
  FunnelIcon,
9
9
  MagnifyingGlassIcon,
10
10
  EyeIcon,
11
11
  EyeSlashIcon,
12
12
  } from '@heroicons/react/20/solid'
13
13
 
14
- interface InputProps {
14
+ type InputProps = React.InputHTMLAttributes<HTMLInputElement>
15
+ type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
16
+ type FieldExtraProps = {
17
+ // field name or path on state (used to match errors), e.g. 'date', 'company.email'
15
18
  name: string
16
- state?: any
17
19
  id?: string
18
- type?: string
19
- [key: string]: any
20
+ // state object to get the value, and check errors against
21
+ state?: { errors: Errors, [key: string]: unknown }
22
+ type?: 'text' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color'
23
+ icon?: React.ReactNode
24
+ iconPos?: 'left' | 'right'
25
+ }
26
+ type IconWrapperProps = {
27
+ iconPos: string
28
+ icon?: React.ReactNode
29
+ [key: string]: unknown
20
30
  }
31
+ // Discriminated union (https://stackoverflow.com/a/77351290/1900648)
32
+ export type FieldProps = (
33
+ | ({ type?: 'text' | 'password' | 'email' | 'filter' | 'search' } & InputProps & FieldExtraProps)
34
+ | ({ type: 'textarea' } & TextareaProps & FieldExtraProps)
35
+ | ({ type: 'currency' } & FieldCurrencyProps & FieldExtraProps)
36
+ | ({ type: 'color' } & FieldColorProps & FieldExtraProps)
37
+ | ({ type: 'date' } & FieldDateProps & FieldExtraProps)
38
+ )
39
+
40
+ export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
41
+ // type must be kept as props.type for TS to be happy and follow the conditions below
42
+ let error!: Error
43
+ let value!: string
44
+ let Icon!: React.ReactNode
45
+ const type = props.type
46
+ const iconPos = ip == 'left' || (type == 'color' && !ip) ? 'left' : 'right'
21
47
 
22
- export function Input({ name='', state, id, type='text', ...props }: InputProps) {
23
- /**
24
- * Input
25
- * @param {string} name - field name or path on state (used to match errors), e.g. 'date', 'company.email'
26
- * @param {object} state - State object to get the value, and check errors against
27
- * @param {string} [id] - not required, name used if not provided
28
- * @param {('password'|'email'|'text'|'date'|'filter'|'search'|'color'|'textarea'|'currency')} [type='text']
29
- */
30
- let IconSvg: React.ReactNode
31
- let onClick: () => void
32
- let iconDir = 'right'
33
- let InputEl = 'input'
48
+ if (!props.name) {
49
+ throw new Error('Input component requires a `name` prop')
50
+ }
51
+
52
+ // Input type
34
53
  const [inputType, setInputType] = useState(() => { // eslint-disable-line
35
- return type == 'password' ? 'password' : (type == 'textarea' ? type : 'text')
54
+ return type == 'password' ? 'password' : (type == 'textarea' ? 'textarea' : 'text')
36
55
  })
37
-
38
- if (!name) throw new Error('Input component requires a `name` prop')
39
56
 
40
- // Input is always controlled if state is passed in
41
- if (props.value) {
42
- var value = props.value
43
- } else if (typeof state == 'object') {
44
- value = util.deepFind(state, name)
45
- if (typeof value == 'undefined') value = ''
46
- }
57
+ // Value: Input is always controlled if state is passed in
58
+ if (props.value) value = props.value as string
59
+ else if (typeof state == 'object') value = util.deepFind(state, props.name) ?? ''
47
60
 
48
- // Find any errors that match this input path
61
+ // Errors: find any that match this input path
49
62
  for (const item of (state?.errors || [])) {
50
- if (util.isRegex(name) && (item.title||'').match(name)) var error = item
51
- else if (item.title == name) error = item
63
+ if (util.isRegex(props.name) && (item.title || '').match(props.name)) error = item
64
+ else if (item.title == props.name) error = item
52
65
  }
53
66
 
54
- // Special input types
67
+ // Icon
55
68
  if (type == 'password') {
56
- onClick = () => setInputType(o => o == 'password' ? 'text' : 'password')
57
- IconSvg = inputType == 'password' ? <EyeSlashIcon /> : <EyeIcon />
58
- } else if (type == 'email') {
59
- IconSvg = <EnvelopeIcon />
60
- // } else if (type == 'date') {
61
- // IconSvg = <CalendarIcon />
62
- // InputEl = InputDate
63
- } else if (type == 'filter') {
64
- IconSvg = <FunnelIcon />
65
- } else if (type == 'search') {
66
- IconSvg = <MagnifyingGlassIcon />
69
+ Icon = <IconWrapper
70
+ iconPos={iconPos}
71
+ icon={icon || inputType == 'password' ? <EyeSlashIcon /> : <EyeIcon />}
72
+ onClick={() => setInputType(o => o == 'password' ? 'text' : 'password')}
73
+ className="pointer-events-auto"
74
+ />
75
+ } else if (type == 'email') {
76
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <EnvelopeIcon />} />
77
+ } else if (type == 'filter') {
78
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <FunnelIcon />} className="size-3" />
79
+ } else if (type == 'search') {
80
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <MagnifyingGlassIcon />} className="size-4" />
67
81
  } else if (type == 'color') {
68
- iconDir = 'left'
69
- IconSvg = <ColorIcon hex={value}/>
70
- InputEl = InputColor
71
- } else if (type == 'textarea') {
72
- InputEl = 'textarea'
73
- } else if (type == 'currency') {
74
- if (!props.config) throw new Error('Input: `config` is required when type=currency')
75
- InputEl = InputCurrency
82
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <ColorSvg hex={value}/>} className="size-[17px]" />
83
+ } else if (type == 'date') {
84
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <CalendarIcon />} className="size-4" />
85
+ } else {
86
+ Icon = <IconWrapper iconPos={iconPos} icon={icon} />
76
87
  }
77
88
 
78
- // Icon
79
- const iconEl = <IconEl iconDir={iconDir} IconSvg={IconSvg} onClick={onClick} type={type} />
89
+ // Classname
90
+ const inputClassName = getInputClasses({ error, Icon, iconPos, type })
91
+ const commonProps = { id: props.name || props.id, value: value, className: inputClassName }
80
92
 
81
- // Create base props object
82
- const inputProps = {
83
- ...props,
84
- // autoComplete: props.autoComplete || 'off',
85
- id: id || name,
86
- type: inputType,
87
- value: value,
88
- iconEl: iconEl,
89
- className:
90
- 'col-start-1 row-start-1 block w-full rounded-md bg-white py-2 text-sm outline outline-1 -outline-offset-1 ' +
91
- 'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 sm:text-sm/6 ' +
92
- (iconDir == 'right' && IconSvg ? 'sm:pr-9 pl-3 pr-10 ' : IconSvg ? 'sm:pl-9 pl-10 pr-3 ' : 'px-3 ') +
93
- (error ? 'text-red-900 outline-danger focus:outline-danger ' : 'text-input outline-input-border focus:outline-primary ') +
94
- (iconDir == 'right' ? 'justify-self-start ' : 'justify-self-end '),
93
+ // Type has to be referenced as props.type for TS to be happy
94
+ if (!type || type == 'text' || type == 'password' || type == 'email' || type == 'filter' || type == 'search') {
95
+ return (
96
+ <FieldContainer error={error} className={props.className}>
97
+ {Icon}<input {...props} {...commonProps} type={inputType} />
98
+ </FieldContainer>
99
+ )
100
+ } else if (type == 'textarea') {
101
+ return (
102
+ <FieldContainer error={error} className={props.className}>
103
+ {Icon}<textarea {...props} {...commonProps} />
104
+ </FieldContainer>
105
+ )
106
+ } else if (type == 'currency') {
107
+ return (
108
+ <FieldContainer error={error} className={props.className}>
109
+ {Icon}<FieldCurrency {...props} {...commonProps} />
110
+ </FieldContainer>
111
+ )
112
+ } else if (type == 'color') {
113
+ return (
114
+ <FieldContainer error={error} className={props.className}>
115
+ <FieldColor {...props} {...commonProps} Icon={Icon} />
116
+ </FieldContainer>
117
+ )
118
+ } else if (type == 'date') {
119
+ return (
120
+ <FieldContainer error={error} className={props.className}>
121
+ <FieldDate {...props} {...commonProps} Icon={Icon} />
122
+ </FieldContainer>
123
+ )
95
124
  }
125
+ }
96
126
 
97
- // Only add iconEl prop for custom components
98
- if (!['color', 'date'].includes(type)) delete inputProps.iconEl
99
-
127
+ function FieldContainer({ children, className, error }: { children: React.ReactNode, className?: string, error?: Error }) {
100
128
  return (
101
- // https://tailwindui.com/components/application-ui/forms/input-groups#component-474bd025b849b44eb3c46df09a496b7a
102
- <div css={style} className={`mt-input-before mb-input-after grid grid-cols-1 ${props?.className || ''}`}>
103
- { !inputProps.iconEl && iconEl }
104
- <InputEl {...inputProps} />
129
+ <div css={style} className={`mt-input-before mb-input-after grid grid-cols-1 ${className || ''}`}>
130
+ {children}
105
131
  {error && <div class="mt-1.5 text-xs text-danger">{error.detail}</div>}
106
132
  </div>
107
133
  )
108
134
  }
109
135
 
110
- type IconElProps = {
111
- iconDir: string
112
- IconSvg: React.ReactNode
113
- onClick: () => void
114
- type: string
136
+ function getInputClasses({ error, Icon, iconPos, type }: { error: Error, Icon?: React.ReactNode, iconPos: string, type?: string }) {
137
+ const paddingLeft = type == 'color' ? 'sm:pl-9 pl-9' : 'sm:pl-8 pl-8'
138
+ const paddingRight = type == 'color' ? 'sm:pr-9 pr-9' : 'sm:pr-8 pr-8'
139
+ return (
140
+ 'col-start-1 row-start-1 block w-full rounded-md bg-white py-2 text-sm leading-[1.65] outline outline-1 -outline-offset-1 ' +
141
+ 'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 ' +
142
+ (iconPos == 'right' && Icon ? `${paddingRight} pl-3 ` : (Icon ? `${paddingLeft} pr-3 ` : 'px-3 ')) +
143
+ (error ? 'text-red-900 outline-danger focus:outline-danger ' : 'text-input outline-input-border focus:outline-primary ') +
144
+ (iconPos == 'right' ? 'justify-self-start ' : 'justify-self-end ')
145
+ )
115
146
  }
116
147
 
117
- function IconEl({ iconDir, IconSvg, onClick, type }: IconElProps) {
118
- const iconSize = type == 'color' ? 'size-[18px]' : 'size-4'
148
+ function IconWrapper({ icon, iconPos, ...props }: IconWrapperProps) {
119
149
  return (
120
- !!IconSvg &&
121
- <div
122
- className={`col-start-1 row-start-1 ${iconSize} self-center text-[#c6c8ce] select-none relative z-[1] ` +
123
- `pointer-events-${type == 'password' ? 'auto' : 'none'} ` +
124
- (iconDir == 'right' ? 'justify-self-end mr-3' : 'justify-self-start ml-3')
125
- }
126
- onClick={onClick}
127
- >{IconSvg}</div>
150
+ !!icon &&
151
+ <div
152
+ {...props}
153
+ className={twMerge(
154
+ 'relative size-[14px] col-start-1 row-start-1 self-center text-[#c6c8ce] select-none z-[1] ' +
155
+ (iconPos == 'right' ? 'justify-self-end mr-3 ' : 'justify-self-start ml-3 ') +
156
+ props.className || ''
157
+ )}
158
+ >{icon}</div>
128
159
  )
129
160
  }
130
161
 
131
- function ColorIcon({ hex }: { hex: string }) {
162
+ function ColorSvg({ hex }: { hex?: string }) {
132
163
  return (
133
164
  <span class="block size-full rounded-md" style={{ backgroundColor: hex ? hex : '#f1f1f1' }}></span>
134
165
  )
@@ -2,7 +2,7 @@ import { css } from 'twin.macro'
2
2
  import { twMerge } from 'tailwind-merge'
3
3
  import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps } from 'react-select'
4
4
  import { ClearIndicatorProps, DropdownIndicatorProps, MultiValueRemoveProps } from 'react-select'
5
- import { ChevronDownIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
5
+ import { ChevronUpDownIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
6
6
  import { util } from 'nitro-web'
7
7
  import { Errors } from 'types'
8
8
 
@@ -203,7 +203,7 @@ function Option(props: OptionProps) {
203
203
  const DropdownIndicator = (props: DropdownIndicatorProps) => {
204
204
  return (
205
205
  <components.DropdownIndicator {...props}>
206
- <ChevronDownIcon className="size-6 -my-0.5 -mx-1" />
206
+ <ChevronUpDownIcon className="text-gray-400 size-[17px] -my-0.5 -mx-0.5" />
207
207
  </components.DropdownIndicator>
208
208
  )
209
209
  }
@@ -237,7 +237,7 @@ const selectStyles = {
237
237
  // Based off https://www.jussivirtanen.fi/writing/styling-react-select-with-tailwind
238
238
  // Input container
239
239
  control: {
240
- base: 'rounded-md bg-white hover:cursor-pointer text-sm sm:text-sm/6 outline outline-1 -outline-offset-1 outline-input-border',
240
+ base: 'rounded-md bg-white hover:cursor-pointer text-sm leading-[1.65] outline outline-1 -outline-offset-1 outline-input-border',
241
241
  focus: 'outline-2 -outline-offset-2 outline-primary',
242
242
  error: 'outline-danger',
243
243
  },
@@ -249,7 +249,7 @@ const selectStyles = {
249
249
  },
250
250
  multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
251
251
  multiValueLabel: '',
252
- multiValueRemove: 'border border-primary-dark bg-white rounded-md text-dark hover:bg-red-50',
252
+ multiValueRemove: 'border border-black/10 bg-clip-content bg-white rounded-md text-dark hover:bg-red-50',
253
253
  placeholder: 'text-input-placeholder',
254
254
  singleValue: {
255
255
  base: 'text-input',