nitro-web 0.0.66 → 0.0.67
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
|
-
|
|
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,
|
|
@@ -33,6 +33,7 @@ type FiltersProps = {
|
|
|
33
33
|
buttonText?: string
|
|
34
34
|
buttonCounterClassName?: string
|
|
35
35
|
filtersContainerClassName?: string
|
|
36
|
+
menuClassName?: string
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export type FiltersHandleType = {
|
|
@@ -52,6 +53,7 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
52
53
|
dropdownProps,
|
|
53
54
|
elements,
|
|
54
55
|
filtersContainerClassName,
|
|
56
|
+
menuClassName,
|
|
55
57
|
}, ref) => {
|
|
56
58
|
const location = useLocation()
|
|
57
59
|
const navigate = useNavigate()
|
|
@@ -119,18 +121,22 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
119
121
|
navigate(location.pathname + queryStr, { replace: true })
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
// if (!filters) return null
|
|
123
124
|
return (
|
|
124
125
|
<Elements.Dropdown
|
|
125
126
|
dir="bottom-right"
|
|
127
|
+
allowOverflow={true}
|
|
126
128
|
// menuIsOpen={true}
|
|
127
|
-
menuClassName=
|
|
129
|
+
menuClassName={twMerge(`!rounded-lg min-w-[330px] ${menuClassName || ''}`)}
|
|
128
130
|
menuContent={
|
|
129
|
-
<div
|
|
131
|
+
<div>
|
|
130
132
|
<div class="flex justify-between items-center border-b p-4 py-3.5">
|
|
131
133
|
<div class="text-lg font-semibold">Filters</div>
|
|
132
134
|
<Button color="clear" size="sm" onClick={resetAll}>Reset All</Button>
|
|
133
135
|
</div>
|
|
136
|
+
{/* <div class="w-[1330px] bg-red-500 absolute">
|
|
137
|
+
This div shouldnt produce a page scrollbar when the dropdown is closed.
|
|
138
|
+
But should be visibile if allowedOverflow is true.
|
|
139
|
+
</div> */}
|
|
134
140
|
<div class={twMerge(`flex flex-wrap gap-4 px-4 py-4 pb-6 ${filtersContainerClassName || ''}`)}>
|
|
135
141
|
{
|
|
136
142
|
filters?.map(({label, rowClassName, ...filter}, i) => (
|
|
@@ -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
|
|
73
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.0.67",
|
|
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
|
|
1259
|
-
else if (isNaN(end)) mongoQuery
|
|
1260
|
-
else mongoQuery
|
|
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.`)
|