nitro-web 0.0.116 → 0.0.117

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.
@@ -95,4 +95,16 @@
95
95
  .loading-dots::after {
96
96
  content: "";
97
97
  animation: dots 2s steps(1, end) infinite;
98
+ }
99
+
100
+ /* ---- Scrollbar -------------------- */
101
+
102
+ .sm-scrollbar::-webkit-scrollbar {
103
+ width: 4px;
104
+ }
105
+ .sm-scrollbar::-webkit-scrollbar-thumb {
106
+ background-color: rgba(0,0,0,0.3);
107
+ }
108
+ .sm-scrollbar::-webkit-scrollbar-track {
109
+ background: rgba(0,0,0,0.05);
98
110
  }
package/client/index.ts CHANGED
@@ -45,6 +45,7 @@ export { Field, isFieldCached, type FieldProps } from '../components/partials/fo
45
45
  export { FieldColor, type FieldColorProps } from '../components/partials/form/field-color'
46
46
  export { FieldCurrency, type FieldCurrencyProps } from '../components/partials/form/field-currency'
47
47
  export { FieldDate, type FieldDateProps } from '../components/partials/form/field-date'
48
+ export { FieldTime, type FieldTimeProps } from '../components/partials/form/field-time'
48
49
  export { Location } from '../components/partials/form/location'
49
50
  export { Select, getSelectStyle, type SelectProps } from '../components/partials/form/select'
50
51
 
@@ -16,7 +16,7 @@ export type DayPickerProps = Omit<DayPickerPropsBase,
16
16
 
17
17
  export type CalendarProps = DayPickerProps & {
18
18
  mode?: Mode
19
- onChange?: (mode: Mode, value: null|number|(null|number)[]) => void
19
+ onChange?: (value: null|number|(null|number)[]) => void
20
20
  value?: null|number|string|(null|number|string)[]
21
21
  numberOfMonths?: number
22
22
  month?: number // the value may be updated from an outside source, thus the month may have changed
@@ -48,17 +48,17 @@ export function Calendar({ mode='single', onChange, value, numberOfMonths, month
48
48
  case 'single': {
49
49
  const date = newDate as ModeSelection<'single'>
50
50
  preserveTimeFn(date)
51
- onChange?.(mode, date?.getTime() ?? null)
51
+ onChange?.(date?.getTime() ?? null)
52
52
  break
53
53
  }
54
54
  case 'range': {
55
55
  const { from, to } = (newDate ?? {}) as ModeSelection<'range'>
56
- onChange?.(mode, from ? [from.getTime() || null, to?.getTime() || null] : null)
56
+ onChange?.(from ? [from.getTime() || null, to?.getTime() || null] : null)
57
57
  break
58
58
  }
59
59
  case 'multiple': {
60
60
  const dates = (newDate as ModeSelection<'multiple'>)?.filter(Boolean) ?? []
61
- onChange?.(mode, dates.map((d) => d.getTime()))
61
+ onChange?.(dates.map((d) => d.getTime()))
62
62
  break
63
63
  }
64
64
  }
@@ -2,7 +2,8 @@
2
2
  import { format, isValid, parse } from 'date-fns'
3
3
  import { getPrefixWidth } from 'nitro-web/util'
4
4
  import { Calendar, Dropdown } from 'nitro-web'
5
- import { dayButtonClassName, DayPickerProps } from '../element/calendar'
5
+ import { DayPickerProps } from '../element/calendar'
6
+ import { TimePicker } from './field-time'
6
7
 
7
8
  type Mode = 'single' | 'multiple' | 'range'
8
9
  type DropdownRef = {
@@ -42,11 +43,6 @@ export type FieldDateProps = (
42
43
  })
43
44
  )
44
45
 
45
- type TimePickerProps = {
46
- date: Date|null
47
- onChange: (mode: Mode, value: number|null) => void
48
- }
49
-
50
46
  export function FieldDate({
51
47
  dir = 'bottom-left',
52
48
  Icon,
@@ -59,6 +55,8 @@ export function FieldDate({
59
55
  DayPickerProps,
60
56
  ...props
61
57
  }: FieldDateProps) {
58
+ // Currently this displays the dates in local timezone and saves in utc. We should allow the user to display the dates in a
59
+ // different timezone.
62
60
  const localePattern = `d MMM yyyy${showTime && mode == 'single' ? ' hh:mmaa' : ''}`
63
61
  const [prefixWidth, setPrefixWidth] = useState(0)
64
62
  const dropdownRef = useRef<DropdownRef>(null)
@@ -72,7 +70,7 @@ export function FieldDate({
72
70
  const onChange = onChangeProp ?? ((e: { target: { name: string, value: any } }) => setInternalValue(e.target.value))
73
71
 
74
72
  // Convert the value to an array of valid* dates
75
- const dates = useMemo(() => {
73
+ const validDates = useMemo(() => {
76
74
  const arrOfNumbers = typeof value === 'string'
77
75
  ? value.split(/\s*,\s*/g).map(o => parseFloat(o))
78
76
  : Array.isArray(value) ? value : [value]
@@ -81,19 +79,19 @@ export function FieldDate({
81
79
  }, [value])
82
80
 
83
81
  // Hold the input value in state
84
- const [inputValue, setInputValue] = useState(() => getInputValue(dates))
82
+ const [inputValue, setInputValue] = useState(() => getInputValue(validDates))
85
83
 
86
84
  // Update the date's inputValue (text) when the value changes outside of the component
87
85
  useEffect(() => {
88
- if (new Date().getTime() > lastUpdated + 100) setInputValue(getInputValue(dates))
89
- }, [dates])
86
+ if (new Date().getTime() > lastUpdated + 100) setInputValue(getInputValue(validDates))
87
+ }, [validDates])
90
88
 
91
89
  // Get the prefix content width
92
90
  useEffect(() => {
93
91
  setPrefixWidth(getPrefixWidth(prefix, 4))
94
92
  }, [prefix])
95
93
 
96
- function onCalendarChange(mode: Mode, value: null|number|(null|number)[]) {
94
+ function onCalendarChange(value: null|number|(null|number)[]) {
97
95
  if (mode == 'single' && !showTime) dropdownRef.current?.setIsActive(false) // Close the dropdown
98
96
  setInputValue(getInputValue(value))
99
97
  // Update the value
@@ -102,6 +100,7 @@ export function FieldDate({
102
100
  }
103
101
 
104
102
  function onInputChange(e: React.ChangeEvent<HTMLInputElement>) {
103
+ // Calls onChange (should update state, thus updating the value) with "raw" values
105
104
  setInputValue(e.target.value) // keep the input value in sync
106
105
 
107
106
  let split = e.target.value.split(/-|,/).map(o => {
@@ -132,7 +131,7 @@ export function FieldDate({
132
131
  const _dates = Array.isArray(value) ? value : [value]
133
132
  return _dates.map(o => o ? format(o, localePattern) : '').join(mode == 'range' ? ' - ' : ', ')
134
133
  }
135
-
134
+
136
135
  function getOutputValue(value: Date|number|null|(Date|number|null)[]): any {
137
136
  // console.log(value)
138
137
  return value
@@ -149,13 +148,13 @@ export function FieldDate({
149
148
  <div className="flex">
150
149
  <Calendar
151
150
  // Calendar actually accepts an array of dates, but the type is not typed correctly
152
- {...{ mode: mode, value: dates as any, numberOfMonths: numberOfMonths, month: month }}
151
+ {...{ mode: mode, value: validDates as any, numberOfMonths: numberOfMonths, month: month }}
153
152
  {...DayPickerProps}
154
153
  preserveTime={!!showTime}
155
154
  onChange={onCalendarChange}
156
155
  className="pt-1 pb-2 px-3"
157
156
  />
158
- {!!showTime && mode == 'single' && <TimePicker date={dates?.[0]} onChange={onCalendarChange} />}
157
+ {!!showTime && mode == 'single' && <TimePicker date={validDates?.[0] ?? undefined} onChange={onCalendarChange} />}
159
158
  </div>
160
159
  }
161
160
  dir={dir}
@@ -175,7 +174,7 @@ export function FieldDate({
175
174
  id={id}
176
175
  autoComplete="off"
177
176
  className={(props.className||'')}// + props.className?.includes('is-invalid') ? ' is-invalid' : ''}
178
- onBlur={() => setInputValue(getInputValue(dates))}
177
+ onBlur={() => setInputValue(getInputValue(validDates))} // onChange should of updated the value -> validValue by this point
179
178
  onChange={onInputChange}
180
179
  style={{ textIndent: prefixWidth + 'px' }}
181
180
  type="text"
@@ -185,72 +184,3 @@ export function FieldDate({
185
184
  </Dropdown>
186
185
  )
187
186
  }
188
-
189
- function TimePicker({ date, onChange }: TimePickerProps) {
190
- const lists = [
191
- [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // hours
192
- [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55], // minutes
193
- ['AM', 'PM'], // AM/PM
194
- ]
195
-
196
- // Get current values from date or use defaults
197
- const hour = date ? parseInt(format(date, 'h')) : undefined
198
- const minute = date ? parseInt(format(date, 'm')) : undefined
199
- const period = date ? format(date, 'a') : undefined
200
-
201
- const handleTimeChange = (type: 'hour' | 'minute' | 'period', value: string | number) => {
202
- // Create a new date object from the current date or current time
203
- const newDate = new Date(date || new Date())
204
-
205
- if (type === 'hour') {
206
- // Parse the time with the new hour value
207
- const timeString = `${value}:${format(newDate, 'mm')} ${format(newDate, 'a')}`
208
- const updatedDate = parse(timeString, 'h:mm a', newDate)
209
- newDate.setHours(updatedDate.getHours(), updatedDate.getMinutes())
210
- } else if (type === 'minute') {
211
- // Parse the time with the new minute value
212
- const timeString = `${format(newDate, 'h')}:${value} ${format(newDate, 'a')}`
213
- const updatedDate = parse(timeString, 'h:mm a', newDate)
214
- newDate.setMinutes(updatedDate.getMinutes())
215
- } else if (type === 'period') {
216
- // Parse the time with the new period value
217
- const timeString = `${format(newDate, 'h')}:${format(newDate, 'mm')} ${value}`
218
- const updatedDate = parse(timeString, 'h:mm a', newDate)
219
- newDate.setHours(updatedDate.getHours())
220
- }
221
-
222
- onChange('single', newDate.getTime())
223
- }
224
-
225
- return (
226
- lists.map((list, i) => {
227
- const type = i === 0 ? 'hour' : i === 1 ? 'minute' : 'period'
228
- const currentValue = i === 0 ? hour : i === 1 ? minute : period
229
-
230
- return (
231
- <div key={i} className="w-[60px] py-1 relative overflow-hidden hover:overflow-y-auto border-l border-gray-100">
232
- <div className="w-[60px] absolute flex flex-col items-center">
233
- {list.map(item => (
234
- <div
235
- className="py-1 flex group cursor-pointer"
236
- key={item}
237
- onClick={() => handleTimeChange(type, item)}
238
- >
239
- <button
240
- key={item}
241
- className={
242
- `${dayButtonClassName} rounded-full flex justify-center items-center group-hover:bg-gray-100 `
243
- + (item === currentValue ? '!bg-input-border-focus text-white' : '')
244
- }
245
- onClick={() => handleTimeChange(type, item)}
246
- >
247
- {item.toString().padStart(2, '0').toLowerCase()}
248
- </button>
249
- </div>
250
- ))}
251
- </div>
252
- </div>
253
- )
254
- })
255
- )
256
- }
@@ -0,0 +1,197 @@
1
+ import { format, parse } from 'date-fns'
2
+ import { Button, Dropdown } from 'nitro-web'
3
+ import { dayButtonClassName } from '../element/calendar'
4
+
5
+ type Timestamp = number // timestamp on epoch day
6
+ type DropdownRef = {
7
+ setIsActive: (value: boolean) => void
8
+ }
9
+ export type FieldTimeProps = React.InputHTMLAttributes<HTMLInputElement> & {
10
+ name: string
11
+ id?: string
12
+ onChange?: (e: { target: { name: string, value: null|number } }) => void
13
+ value?: string | Timestamp;
14
+ Icon?: React.ReactNode
15
+ dir?: 'bottom-left'|'bottom-right'|'top-left'|'top-right'
16
+ // tz?: string
17
+ }
18
+ type TimePickerProps = {
19
+ date?: Date
20
+ onChange: (value: Timestamp) => void
21
+ // tz?: string
22
+ }
23
+
24
+ export function FieldTime({ onChange, value, Icon, dir = 'bottom-left', ...props }: FieldTimeProps) {
25
+ // time is viewed and set in local timezone, and saved as timestamp on epoch day.
26
+ // Note: timestamp is better than saving seconds so we can easily view this in a particular timezone
27
+ const localePattern = 'hh:mmaa'
28
+ const dropdownRef = useRef<DropdownRef>(null)
29
+ const id = props.id || props.name
30
+
31
+ // Convert the value to a valid time value
32
+ const validValue = useMemo(() => {
33
+ const num = typeof value === 'string' ? parseInt(value) : value
34
+ console.log(11, num)
35
+ return typeof num === 'number' && !isNaN(num) ? num : new Date(0).getTime()
36
+ }, [value])
37
+
38
+ // Hold the input value in state
39
+ const [inputValue, setInputValue] = useState(() => getInputValue(validValue))
40
+
41
+ function onTimePickerChange(value: Timestamp) {
42
+ setInputValue(getInputValue(value))
43
+ if (onChange) onChange({ target: { name: props.name, value: value }})
44
+ }
45
+
46
+ function getInputValue(timestamp: Timestamp) {
47
+ // Get the input-value in local timezone
48
+ return typeof timestamp === 'number' ? format(new Date(timestamp), localePattern) : ''
49
+ }
50
+
51
+ function onInputChange(e: React.ChangeEvent<HTMLInputElement>) {
52
+ // Assume the string is in local timezone, and calls onChange with "raw" values (should update state, thus updating the value).
53
+ setInputValue(e.target.value) // keep the input value in sync
54
+ const [, _hour, _minute, _second, _period] = e.target.value.match(/(\d{1,2}):(\d{2})(:\d{2})?\s*(am|pm)/i) || []
55
+ if (!_hour || !_minute) return
56
+ const hour24 = parseInt(_hour) < 12 && _period.match(/pm/i) ? parseInt(_hour) + 12 : parseInt(_hour)
57
+ const minute = parseInt(_minute)
58
+
59
+ // Assume the time string is in the local timezone, and convert to UTC date from epoch
60
+ const localDate = new Date(0)
61
+ localDate.setHours(hour24, minute, _second ? parseInt(_second) : 0, 0)
62
+ const value = localDate.getTime()
63
+ if (onChange) onChange({ target: { name: props.name, value: value }})
64
+ }
65
+
66
+ function onNowClick() {
67
+ const epochDay = new Date(0)
68
+ const now = new Date()
69
+ // now set hours, minutes, seconds to now but on epoch day
70
+ epochDay.setHours(now.getHours(), now.getMinutes(), now.getSeconds(), 0)
71
+ onTimePickerChange(epochDay.getTime())
72
+ }
73
+
74
+ return (
75
+ <Dropdown
76
+ ref={dropdownRef}
77
+ menuToggles={false}
78
+ // animate={false}
79
+ // menuIsOpen={true}
80
+ minWidth={0}
81
+ dir={dir}
82
+ menuContent={
83
+ <div>
84
+ <div className="flex justify-center h-[250px]">
85
+ <TimePicker date={new Date(validValue)} onChange={onTimePickerChange} />
86
+ </div>
87
+ <div className="flex justify-between p-2 border-t border-gray-100">
88
+ <Button color="secondary" size="xs" onClick={() => onNowClick()}>Now</Button>
89
+ <Button color="primary" size="xs" onClick={() => dropdownRef.current?.setIsActive(false)}>Done</Button>
90
+ </div>
91
+ </div>
92
+ }
93
+ >
94
+ <div className="grid grid-cols-1">
95
+ {Icon}
96
+ <input
97
+ {...props}
98
+ id={id}
99
+ autoComplete="off"
100
+ className={(props.className||'')}// + props.className?.includes('is-invalid') ? ' is-invalid' : ''}
101
+ value={inputValue}
102
+ onChange={onInputChange}
103
+ onBlur={() => setInputValue(getInputValue(validValue))} // onChange should of updated the value -> validValue by this point
104
+ type="text"
105
+ />
106
+ </div>
107
+ </Dropdown>
108
+ )
109
+ }
110
+
111
+ export function TimePicker({ date, onChange }: TimePickerProps) {
112
+ const lists = [
113
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // hours
114
+ [
115
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
116
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
117
+ 51, 52, 53, 54, 55, 56, 57, 58, 59,
118
+ ], // minutes
119
+ ['AM', 'PM'], // AM/PM
120
+ ]
121
+
122
+ // Get current values from date or use defaults
123
+ const hour = date ? parseInt(format(date, 'h')) : undefined
124
+ const minute = date ? parseInt(format(date, 'm')) : undefined
125
+ const period = date ? format(date, 'a') : undefined
126
+
127
+ const handleTimeChange = (type: 'hour' | 'minute' | 'period', value: string | number) => {
128
+ // Creates a new date object in the local timezone, and calls onChange with the timestamp
129
+ const newDate = new Date(date || new Date())
130
+
131
+ if (type === 'hour') {
132
+ // Parse the time with the new hour value
133
+ const timeString = `${value}:${format(newDate, 'mm')} ${format(newDate, 'a')}`
134
+ const updatedDate = parse(timeString, 'h:mm a', newDate)
135
+ newDate.setHours(updatedDate.getHours(), updatedDate.getMinutes())
136
+ } else if (type === 'minute') {
137
+ // Parse the time with the new minute value
138
+ const timeString = `${format(newDate, 'h')}:${value} ${format(newDate, 'a')}`
139
+ const updatedDate = parse(timeString, 'h:mm a', newDate)
140
+ newDate.setMinutes(updatedDate.getMinutes())
141
+ } else if (type === 'period') {
142
+ // Parse the time with the new period value
143
+ const timeString = `${format(newDate, 'h')}:${format(newDate, 'mm')} ${value}`
144
+ const updatedDate = parse(timeString, 'h:mm a', newDate)
145
+ newDate.setHours(updatedDate.getHours())
146
+ }
147
+
148
+ onChange(newDate.getTime())
149
+ }
150
+
151
+ function scrollIntoView(type: 'hour' | 'minute' | 'period', value: string | number, element: HTMLElement) {
152
+ const container = element?.parentElement?.parentElement
153
+ if (element && container) {
154
+ const topContainerPadding = 0
155
+ const scrollTop = element.offsetTop - container.offsetTop - topContainerPadding
156
+ container.scrollTo({ top: scrollTop, behavior: 'smooth' })
157
+ }
158
+ }
159
+
160
+ return (
161
+ lists.map((list, i) => {
162
+ const type = i === 0 ? 'hour' : i === 1 ? 'minute' : 'period'
163
+ const currentValue = i === 0 ? hour : i === 1 ? minute : period
164
+
165
+ return (
166
+ <div
167
+ key={i}
168
+ className="w-[60px] py-2 relative overflow-hidden hover:overflow-y-auto border-l border-gray-100 sm-scrollbar first:border-l-0"
169
+ >
170
+ <div className="w-[60px] absolute flex flex-col items-center">
171
+ {/* using absolute since the scrollbar takes up space */}
172
+ {list.map(item => (
173
+ <div
174
+ className="py-[1px] flex group cursor-pointer"
175
+ key={item}
176
+ onClick={(e) => {
177
+ handleTimeChange(type, item)
178
+ scrollIntoView(type, item, e.currentTarget)
179
+ }}
180
+ >
181
+ <button
182
+ key={item}
183
+ className={
184
+ `${dayButtonClassName} rounded-full flex justify-center items-center group-hover:bg-gray-100 `
185
+ + (item === currentValue ? '!bg-input-border-focus text-white' : '')
186
+ }
187
+ >
188
+ {item.toString().padStart(2, '0').toLowerCase()}
189
+ </button>
190
+ </div>
191
+ ))}
192
+ </div>
193
+ </div>
194
+ )
195
+ })
196
+ )
197
+ }
@@ -1,13 +1,14 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  // fill-current tw class for lucide icons (https://github.com/lucide-icons/lucide/discussions/458)
3
3
  import { css } from 'twin.macro'
4
- import { FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps } from 'nitro-web'
4
+ import { FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps, FieldTime,
5
+ FieldTimeProps } from 'nitro-web'
5
6
  import { twMerge, getErrorFromState, deepFind } from 'nitro-web/util'
6
7
  import { Errors, type Error } from 'nitro-web/types'
7
- import { MailIcon, CalendarIcon, FunnelIcon, SearchIcon, EyeIcon, EyeOffIcon } from 'lucide-react'
8
+ import { MailIcon, CalendarIcon, FunnelIcon, SearchIcon, EyeIcon, EyeOffIcon, ClockIcon } from 'lucide-react'
8
9
  import { memo } from 'react'
9
10
 
10
- type FieldType = 'text' | 'number' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color'
11
+ type FieldType = 'text' | 'number' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color' | 'time'
11
12
  type InputProps = React.InputHTMLAttributes<HTMLInputElement>
12
13
  type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
13
14
  type FieldExtraProps = {
@@ -40,6 +41,8 @@ export type FieldProps = (
40
41
  | ({ type: 'currency' } & FieldCurrencyProps & FieldExtraProps)
41
42
  | ({ type: 'color' } & FieldColorProps & FieldExtraProps)
42
43
  | ({ type: 'date' } & FieldDateProps & FieldExtraProps)
44
+ | ({ type: 'time' } & FieldTimeProps & FieldExtraProps)
45
+ // | ({ type: 'time2' } & FieldTimeProps2 & FieldExtraProps)
43
46
  )
44
47
  type IsFieldCachedProps = {
45
48
  name: string
@@ -95,6 +98,8 @@ function FieldBase({ state, icon, iconPos: ip, errorTitle, ...props }: FieldProp
95
98
  Icon = <IconWrapper iconPos={iconPos} icon={icon || <ColorSvg hex={value}/>} className="size-[17px]" />
96
99
  } else if (type == 'date') {
97
100
  Icon = <IconWrapper iconPos={iconPos} icon={icon || <CalendarIcon />} className="size-[14px] size-input-icon" />
101
+ } else if (type == 'time') {
102
+ Icon = <IconWrapper iconPos={iconPos} icon={icon || <ClockIcon />} className="size-[14px] size-input-icon" />
98
103
  } else if (icon) {
99
104
  Icon = <IconWrapper iconPos={iconPos} icon={icon} className="size-[14px] size-input-icon" />
100
105
  }
@@ -134,7 +139,20 @@ function FieldBase({ state, icon, iconPos: ip, errorTitle, ...props }: FieldProp
134
139
  <FieldDate {...props} {...commonProps} Icon={Icon} />
135
140
  </FieldContainer>
136
141
  )
137
- }
142
+ } else if (type == 'time') {
143
+ return (
144
+ <FieldContainer error={error} className={props.className}>
145
+ <FieldTime {...props} {...commonProps} Icon={Icon} />
146
+ </FieldContainer>
147
+ )
148
+ }
149
+ // else if (type == 'time2') {
150
+ // return (
151
+ // <FieldContainer error={error} className={props.className}>
152
+ // <FieldTime2 {...props} {...commonProps} Icon={Icon} />
153
+ // </FieldContainer>
154
+ // )
155
+ // }
138
156
  }
139
157
 
140
158
  function FieldContainer({ children, className, error }: { children: React.ReactNode, className?: string, error?: Error }) {
@@ -224,7 +224,7 @@ function Option(props: OptionProps) {
224
224
  const DropdownIndicator = (props: DropdownIndicatorProps) => {
225
225
  return (
226
226
  <components.DropdownIndicator {...props}>
227
- <ChevronsUpDownIcon size={15} className="text-gray-400 -my-0.5 -mx-[1px]" />
227
+ <ChevronsUpDownIcon size={15} className="text-input-icon -my-0.5 -mx-[1px]" />
228
228
  </components.DropdownIndicator>
229
229
  )
230
230
  }
@@ -45,6 +45,7 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
45
45
  date: Date.now(),
46
46
  'date-range': [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 33],
47
47
  'date-time': Date.now(),
48
+ time: null,
48
49
  calendar: [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 8],
49
50
  firstName: 'Bruce',
50
51
  tableFilter: '',
@@ -471,6 +472,10 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
471
472
  <label for="date">Date multi-select (right aligned)</label>
472
473
  <Field name="date" type="date" mode="multiple" state={state} onChange={(e) => onChange(setState, e)} dir="bottom-right" />
473
474
  </div>
475
+ <div>
476
+ <label for="time">Time</label>
477
+ <Field name="time" type="time" state={state} onChange={(e) => onChange(setState, e)} />
478
+ </div>
474
479
  </div>
475
480
 
476
481
  <h2 class="h3">File Inputs & Calendar</h2>
@@ -482,7 +487,7 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
482
487
  <div>
483
488
  <label for="calendar">Calendar</label>
484
489
  <Calendar mode="range" value={state.calendar} numberOfMonths={1}
485
- onChange={(mode, value) => {
490
+ onChange={(value) => {
486
491
  onChange(setState, { target: { name: 'calendar', value: value } })
487
492
  }}
488
493
  />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.116",
3
+ "version": "0.0.117",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
6
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",