nitro-web 0.0.66 → 0.0.68

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,9 +1,10 @@
1
1
  import { css } from 'twin.macro'
2
2
  import { forwardRef, cloneElement } from 'react'
3
- import { getSelectStyle } from 'nitro-web'
3
+ import { getSelectStyle, twMerge } from 'nitro-web'
4
4
  import { CheckCircleIcon } from '@heroicons/react/24/solid'
5
5
 
6
6
  type DropdownProps = {
7
+ allowOverflow?: boolean
7
8
  animate?: boolean
8
9
  children?: React.ReactNode
9
10
  className?: string
@@ -18,20 +19,23 @@ type DropdownProps = {
18
19
  /** The content to render inside the top of the dropdown **/
19
20
  menuContent?: React.ReactNode
20
21
  menuClassName?: string
22
+ menuOptionClassName?: string
21
23
  menuIsOpen?: boolean
22
24
  menuToggles?: boolean
23
25
  toggleCallback?: (isActive: boolean) => void
24
26
  }
25
27
 
26
28
  export const Dropdown = forwardRef(function Dropdown({
29
+ allowOverflow=false,
27
30
  animate=true,
28
31
  children,
29
32
  className,
30
33
  dir,
31
34
  options,
32
35
  isHoverable,
33
- minWidth,
36
+ minWidth, // remove in favour of menuClassName
34
37
  menuClassName,
38
+ menuOptionClassName,
35
39
  menuContent,
36
40
  menuIsOpen,
37
41
  menuToggles=true,
@@ -92,6 +96,7 @@ export const Dropdown = forwardRef(function Dropdown({
92
96
  (isHoverable ? ' is-hoverable' : '') +
93
97
  (isActive ? ' is-active' : '') +
94
98
  (!animate ? ' no-animation' : '') +
99
+ (allowOverflow ? ' is-allowOverflow' : '') +
95
100
  ' nitro-dropdown' +
96
101
  (className ? ` ${className}` : '')
97
102
  }
@@ -108,7 +113,7 @@ export const Dropdown = forwardRef(function Dropdown({
108
113
  }
109
114
  <ul
110
115
  style={{ minWidth }}
111
- class={`${menuStyle} absolute invisible opacity-0 select-none min-w-full z-[1] ${menuClassName}`}
116
+ class={twMerge(`${menuStyle} absolute invisible opacity-0 select-none min-w-full z-[1] ${menuClassName}`)}
112
117
  >
113
118
  {menuContent}
114
119
  {
@@ -117,7 +122,7 @@ export const Dropdown = forwardRef(function Dropdown({
117
122
  return (
118
123
  <li
119
124
  key={i}
120
- className={`${optionStyle} ${option.className}`}
125
+ className={twMerge(`${optionStyle} ${option.className} ${menuOptionClassName}`)}
121
126
  onClick={(e: React.MouseEvent) => onClick(option, e)}
122
127
  >
123
128
  <span class="flex-auto">{option.label}</span>
@@ -137,8 +142,7 @@ const style = css`
137
142
  transition: transform 0.15s ease, opacity 0.15s ease, visibility 0s 0.15s ease, max-width 0s 0.15s ease, max-height 0s 0.15s ease;
138
143
  max-width: 0; // handy if the dropdown ul exceeds the viewport width
139
144
  max-height: 0; // handy if the dropdown ul exceeds the viewport height
140
- /* overflow: visible !important; // override menustyle */
141
- pointer-events: none;
145
+ pointer-events: none;
142
146
  }
143
147
  &.is-bottom-right,
144
148
  &.is-top-right {
@@ -175,6 +179,8 @@ const style = css`
175
179
  max-width: 1000px;
176
180
  max-height: 1000px;
177
181
  pointer-events: auto;
182
+ }
183
+ &.is-allowOverflow > ul {
178
184
  overflow: visible;
179
185
  }
180
186
  &.is-bottom-left > ul,
@@ -27,12 +27,11 @@ type FiltersProps = {
27
27
  Select?: typeof Select
28
28
  FilterIcon?: typeof ListFilterIcon
29
29
  }
30
- dropdownProps?: Partial<React.ComponentProps<typeof Dropdown>>
31
30
  buttonProps?: Partial<React.ComponentProps<typeof Button>>
32
- buttonClassName?: string
33
31
  buttonText?: string
34
32
  buttonCounterClassName?: string
35
- filtersContainerClassName?: string
33
+ dropdownProps?: Partial<React.ComponentProps<typeof Dropdown>>
34
+ dropdownFiltersClassName?: string
36
35
  }
37
36
 
38
37
  export type FiltersHandleType = {
@@ -45,13 +44,12 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
45
44
  filters,
46
45
  setState: setState2,
47
46
  state: state2,
48
- buttonClassName,
47
+ buttonProps,
49
48
  buttonCounterClassName,
50
- buttonProps,
51
49
  buttonText,
52
50
  dropdownProps,
53
- elements,
54
- filtersContainerClassName,
51
+ dropdownFiltersClassName,
52
+ elements,
55
53
  }, ref) => {
56
54
  const location = useLocation()
57
55
  const navigate = useNavigate()
@@ -119,19 +117,24 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
119
117
  navigate(location.pathname + queryStr, { replace: true })
120
118
  }
121
119
 
122
- // if (!filters) return null
123
120
  return (
124
121
  <Elements.Dropdown
125
122
  dir="bottom-right"
123
+ allowOverflow={true}
126
124
  // menuIsOpen={true}
127
- menuClassName="!rounded-lg"
125
+ {...dropdownProps}
126
+ menuClassName={twMerge(`!rounded-lg min-w-[330px] ${dropdownProps?.menuClassName || ''}`)}
128
127
  menuContent={
129
- <div class="w-[330px]">
128
+ <div>
130
129
  <div class="flex justify-between items-center border-b p-4 py-3.5">
131
130
  <div class="text-lg font-semibold">Filters</div>
132
131
  <Button color="clear" size="sm" onClick={resetAll}>Reset All</Button>
133
132
  </div>
134
- <div class={twMerge(`flex flex-wrap gap-4 px-4 py-4 pb-6 ${filtersContainerClassName || ''}`)}>
133
+ {/* <div class="w-[1330px] bg-red-500 absolute">
134
+ This div shouldnt produce a page scrollbar when the dropdown is closed.
135
+ But should be visibile if allowedOverflow is true.
136
+ </div> */}
137
+ <div class={twMerge(`flex flex-wrap gap-4 px-4 py-4 pb-6 ${dropdownFiltersClassName || ''}`)}>
135
138
  {
136
139
  filters?.map(({label, rowClassName, ...filter}, i) => (
137
140
  <div key={i} class={twMerge(`w-full ${rowClassName||''}`)}>
@@ -164,13 +167,12 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
164
167
  </div>
165
168
  </div>
166
169
  }
167
- {...dropdownProps}
168
170
  >
169
171
  <Elements.Button
170
172
  color="white"
171
173
  IconLeft={<Elements.FilterIcon size={16} />}
172
- className={twMerge(`flex gap-x-2.5 ${buttonClassName || ''}`)}
173
174
  {...buttonProps}
175
+ className={twMerge(`flex gap-x-2.5 ${buttonProps?.className || ''}`)}
174
176
  >
175
177
  <span class="flex items-center gap-x-2.5">
176
178
  { buttonText || 'Filter By' }
@@ -69,8 +69,11 @@ export function FieldDate({
69
69
 
70
70
  // Convert the value to an array of valid* dates
71
71
  const dates = useMemo(() => {
72
- const _dates = Array.isArray(value) ? value : [value]
73
- return _dates.map(date => isValid(date) ? new Date(date as number) : null) /// change to null
72
+ const arrOfNumbers = typeof value === 'string'
73
+ ? value.split(/\s*,\s*/g).map(o => parseFloat(o))
74
+ : Array.isArray(value) ? value : [value]
75
+ const out = arrOfNumbers.map(date => isValid(date) ? new Date(date as number) : null) /// changed to null
76
+ return out
74
77
  }, [value])
75
78
 
76
79
  // Hold the input value in state
@@ -79,7 +82,7 @@ export function FieldDate({
79
82
  // Update the date's inputValue (text) when the value changes outside of the component
80
83
  useEffect(() => {
81
84
  if (new Date().getTime() > lastUpdated + 100) setInputValue(getInputValue(dates))
82
- }, [value])
85
+ }, [dates])
83
86
 
84
87
  // Get the prefix content width
85
88
  useEffect(() => {
@@ -90,15 +93,10 @@ export function FieldDate({
90
93
  if (mode == 'single' && !showTime) dropdownRef.current?.setIsActive(false) // Close the dropdown
91
94
  setInputValue(getInputValue(value))
92
95
  // Update the value
93
- onChange({ target: { name: props.name, value: value as any } })
96
+ onChange({ target: { name: props.name, value: getOutputValue(value) } })
94
97
  setLastUpdated(new Date().getTime())
95
98
  }
96
99
 
97
- function getInputValue(dates: Date|number|null|(Date|number|null)[]) {
98
- const _dates = Array.isArray(dates) ? dates : [dates]
99
- return _dates.map(o => o ? format(o, localePattern) : '').join(mode == 'range' ? ' - ' : ', ')
100
- }
101
-
102
100
  function onInputChange(e: React.ChangeEvent<HTMLInputElement>) {
103
101
  setInputValue(e.target.value) // keep the input value in sync
104
102
 
@@ -122,10 +120,20 @@ export function FieldDate({
122
120
 
123
121
  // Update the value
124
122
  const value = mode == 'single' ? split[0]?.getTime() ?? null : split.map(d => d?.getTime() ?? null)
125
- onChange({ target: { name: props.name, value: value as any }})
123
+ onChange({ target: { name: props.name, value: getOutputValue(value) }})
126
124
  setLastUpdated(new Date().getTime())
127
125
  }
128
126
 
127
+ function getInputValue(value: Date|number|null|(Date|number|null)[]) {
128
+ const _dates = Array.isArray(value) ? value : [value]
129
+ return _dates.map(o => o ? format(o, localePattern) : '').join(mode == 'range' ? ' - ' : ', ')
130
+ }
131
+
132
+ function getOutputValue(value: Date|number|null|(Date|number|null)[]): any {
133
+ // console.log(value)
134
+ return value
135
+ }
136
+
129
137
  return (
130
138
  <Dropdown
131
139
  ref={dropdownRef}
@@ -136,7 +144,8 @@ export function FieldDate({
136
144
  menuContent={
137
145
  <div className="flex">
138
146
  <Calendar
139
- {...{ mode, value, numberOfMonths, month }}
147
+ // Calendar actually accepts an array of dates, but the type is not typed correctly
148
+ {...{ mode: mode, value: dates as any, numberOfMonths: numberOfMonths, month: month }}
140
149
  preserveTime={!!showTime}
141
150
  onChange={onCalendarChange}
142
151
  className="pt-1 pb-2 px-3"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
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 🚀",
package/util.js CHANGED
@@ -1255,9 +1255,9 @@ export function parseFilters(query, config) {
1255
1255
  } else if (rule === 'dateRange') {
1256
1256
  const [start, end] = val.split(',').map(Number)
1257
1257
  if (isNaN(start) && isNaN(end)) throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a date range.`)
1258
- else if (isNaN(start)) mongoQuery.date = { $gte: 0, $lte: end }
1259
- else if (isNaN(end)) mongoQuery.date = { $gte: start }
1260
- else mongoQuery.date = { $gte: start, $lte: end }
1258
+ else if (isNaN(start)) mongoQuery[key] = { $gte: 0, $lte: end }
1259
+ else if (isNaN(end)) mongoQuery[key] = { $gte: start }
1260
+ else mongoQuery[key] = { $gte: start, $lte: end }
1261
1261
 
1262
1262
  } else {
1263
1263
  throw new Error(`Unknown filter type "${rule}" in the config.`)