nitro-web 0.0.122 → 0.0.124
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.
- package/components/partials/element/filters.tsx +18 -6
- package/package.json +1 -1
- package/types/util.d.ts +39 -11
- package/types/util.d.ts.map +1 -1
- package/util.js +64 -16
|
@@ -5,6 +5,7 @@ import { ListFilterIcon } from 'lucide-react'
|
|
|
5
5
|
|
|
6
6
|
type CommonProps = {
|
|
7
7
|
label?: string
|
|
8
|
+
size?: 'full' | 'half' | 'third' | 'quarter' | 'fifth'
|
|
8
9
|
rowClassName?: string
|
|
9
10
|
}
|
|
10
11
|
export type FilterType = (
|
|
@@ -108,7 +109,7 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
108
109
|
|
|
109
110
|
async function onInputChange(e: {target: {name: string, value: unknown}}) {
|
|
110
111
|
// console.log('onInputChange', e.target.name, e.target.value)
|
|
111
|
-
//
|
|
112
|
+
// the state is flattened for the query string, so here we se full paths as the key names e.g. 'job.location': '10')
|
|
112
113
|
setState((s) => ({ ...s, [e.target.name]: e.target.value as string }))
|
|
113
114
|
onAfterChange()
|
|
114
115
|
}
|
|
@@ -123,6 +124,15 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
123
124
|
const queryStr = queryString(omit(stateRef.current, includePagination ? [] : ['page']))
|
|
124
125
|
navigate(locationRef.current.pathname + queryStr, { replace: true })
|
|
125
126
|
}
|
|
127
|
+
|
|
128
|
+
function getBasisWidth(size: 'full' | 'half' | 'third' | 'quarter' | 'fifth') {
|
|
129
|
+
// Need to splay out the classnames for tailwind to work
|
|
130
|
+
if (size == 'full') return 'w-full'
|
|
131
|
+
else if (size == 'half') return 'shrink basis-[calc(50%-8px)]'
|
|
132
|
+
else if (size == 'third') return 'shrink basis-[calc(33.33%-8px)]'
|
|
133
|
+
else if (size == 'quarter') return 'shrink basis-[calc(25%-8px)]'
|
|
134
|
+
else if (size == 'fifth') return 'shrink basis-[calc(20%-8px)]'
|
|
135
|
+
}
|
|
126
136
|
|
|
127
137
|
return (
|
|
128
138
|
<Elements.Dropdown
|
|
@@ -141,10 +151,10 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
141
151
|
This div shouldnt produce a page scrollbar when the dropdown is closed.
|
|
142
152
|
But should be visibile if allowedOverflow is true.
|
|
143
153
|
</div> */}
|
|
144
|
-
<div class={twMerge(`flex flex-wrap gap-
|
|
154
|
+
<div class={twMerge(`flex flex-wrap gap-[16px] p-[16px] pb-6 ${dropdownFiltersClassName || ''}`)}>
|
|
145
155
|
{
|
|
146
|
-
filters?.map(({label, rowClassName, ...filter}, i) => (
|
|
147
|
-
<div key={i} class={twMerge(
|
|
156
|
+
filters?.map(({label, size='full', rowClassName, ...filter}, i) => (
|
|
157
|
+
<div key={i} class={twMerge(getBasisWidth(size), rowClassName || '')}>
|
|
148
158
|
<div class="flex justify-between">
|
|
149
159
|
<label for={filter.id || filter.name}>{label || camelCaseToTitle(filter.name)}</label>
|
|
150
160
|
<a href="#" class="label font-normal text-secondary underline" onClick={(e) => reset(e, filter)}>Reset</a>
|
|
@@ -154,7 +164,8 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
154
164
|
<Elements.Select
|
|
155
165
|
{...filter}
|
|
156
166
|
class="!mb-0"
|
|
157
|
-
|
|
167
|
+
// `filter.name` is a full path e.g. 'job.location', not just the key `location`
|
|
168
|
+
value={typeof state[filter.name] === 'undefined' ? '' : state[filter.name]}
|
|
158
169
|
onChange={onInputChange}
|
|
159
170
|
type={undefined}
|
|
160
171
|
/>
|
|
@@ -164,7 +175,8 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
164
175
|
<Elements.Field
|
|
165
176
|
{...filter}
|
|
166
177
|
class="!mb-0"
|
|
167
|
-
|
|
178
|
+
// `filter.name` is a full path e.g. 'job.location', not just the key `location`
|
|
179
|
+
value={typeof state[filter.name] === 'undefined' ? '' : state[filter.name] as string}
|
|
168
180
|
onChange={onInputChange}
|
|
169
181
|
/>
|
|
170
182
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.124",
|
|
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/types/util.d.ts
CHANGED
|
@@ -528,36 +528,58 @@ export function pad(num?: number, padLeft?: number, fixedRight?: number): string
|
|
|
528
528
|
* Validates req.query "filters" against a config object, and returns a MongoDB-compatible query object.
|
|
529
529
|
* @param {{ [key: string]: string }} query - req.query
|
|
530
530
|
* E.g. {
|
|
531
|
-
* createdAt: '1749038400000,1749729600000',
|
|
532
531
|
* location: '10-RS',
|
|
532
|
+
* age: '33',
|
|
533
|
+
* isDeleted: 'false',
|
|
534
|
+
* search: 'John Doe',
|
|
535
|
+
* createdAt: '1749038400000,1749729600000',
|
|
533
536
|
* status: 'incomplete',
|
|
534
|
-
*
|
|
537
|
+
* bookingDate: '14'
|
|
538
|
+
* isActive: 'true',
|
|
539
|
+
* customer.0: '1234567890', // splayed array items
|
|
535
540
|
* }
|
|
536
|
-
* @param {{
|
|
541
|
+
* @param {{
|
|
542
|
+
* [key: string]: 'string'|'number'|'boolean'|'search'|'dateRange'|EnumArray|{ rule: 'ids', parseId: parseId }
|
|
543
|
+
* }} config - allowed filters and their rules
|
|
537
544
|
* E.g. {
|
|
538
|
-
* createdAt: 'dateRange',
|
|
539
545
|
* location: 'string',
|
|
540
|
-
*
|
|
541
|
-
*
|
|
546
|
+
* age: 'number',
|
|
547
|
+
* isDeleted: 'boolean',
|
|
548
|
+
* search: 'search',
|
|
549
|
+
* createdAt: 'dateRange',
|
|
550
|
+
* status: ['incomplete', 'complete'], // EnumArray
|
|
551
|
+
* bookingDate: [11, 14, 33], // EnumArray
|
|
552
|
+
* isActive: [true, false], // EnumArray
|
|
553
|
+
* customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
|
|
542
554
|
* }
|
|
543
555
|
* @example returned object (using the examples above):
|
|
544
556
|
* E.g. {
|
|
545
|
-
* date: { $gte: 1749038400000, $lte: 1749729600000 },
|
|
546
557
|
* location: '10-RS',
|
|
558
|
+
* age: 33,
|
|
559
|
+
* isDeleted: false,
|
|
560
|
+
* search: { $search: 'John' },
|
|
561
|
+
* createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
|
|
547
562
|
* status: 'incomplete',
|
|
548
|
-
*
|
|
563
|
+
* bookingDate: 14,
|
|
564
|
+
* isActive: true,
|
|
565
|
+
* customer: { $in: [new ObjectId('1234567890')] },
|
|
549
566
|
* }
|
|
550
567
|
*/
|
|
551
568
|
export function parseFilters(query: {
|
|
552
569
|
[key: string]: string;
|
|
553
570
|
}, config: {
|
|
554
|
-
[key: string]: "string" | "number" | "search" | "dateRange" |
|
|
571
|
+
[key: string]: "string" | "number" | "boolean" | "search" | "dateRange" | EnumArray | {
|
|
572
|
+
rule: "ids";
|
|
573
|
+
parseId: parseId;
|
|
574
|
+
};
|
|
555
575
|
}): {
|
|
556
|
-
[key: string]: string | number |
|
|
576
|
+
[key: string]: string | number | boolean | {
|
|
577
|
+
$search: string;
|
|
578
|
+
} | {
|
|
557
579
|
$gte: number;
|
|
558
580
|
$lte?: number;
|
|
559
581
|
} | {
|
|
560
|
-
$
|
|
582
|
+
$in: ObjectId[];
|
|
561
583
|
};
|
|
562
584
|
};
|
|
563
585
|
/**
|
|
@@ -838,4 +860,10 @@ export type AxiosRequestConfigWithRetry = AxiosRequestConfig & {
|
|
|
838
860
|
export type AxiosInstanceWithRetry = Omit<AxiosInstance, "get"> & {
|
|
839
861
|
get<T = any, R = AxiosResponse, D = any>(url: string, config?: AxiosRequestConfigWithRetry): Promise<R>;
|
|
840
862
|
};
|
|
863
|
+
export type ObjectId = object;
|
|
864
|
+
export type parseId = (value: string) => ObjectId;
|
|
865
|
+
/**
|
|
866
|
+
* - an array of strings, numbers or booleans
|
|
867
|
+
*/
|
|
868
|
+
export type EnumArray = (string | number | boolean)[];
|
|
841
869
|
//# sourceMappingURL=util.d.ts.map
|
package/types/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
|
package/util.js
CHANGED
|
@@ -22,6 +22,10 @@ import { twMerge as _twMerge } from 'tailwind-merge'
|
|
|
22
22
|
* }} AxiosInstanceWithRetry
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
/** @typedef {object} ObjectId */
|
|
26
|
+
/** @typedef {(value: string) => ObjectId} parseId */
|
|
27
|
+
/** @typedef {(string|number|boolean)[]} EnumArray - an array of strings, numbers or booleans */
|
|
28
|
+
|
|
25
29
|
/** @type {{[key: string]: {[key: string]: string|true}}} */
|
|
26
30
|
let queryObjectCache = {}
|
|
27
31
|
|
|
@@ -1232,31 +1236,51 @@ export function pad (num=0, padLeft=0, fixedRight) {
|
|
|
1232
1236
|
* Validates req.query "filters" against a config object, and returns a MongoDB-compatible query object.
|
|
1233
1237
|
* @param {{ [key: string]: string }} query - req.query
|
|
1234
1238
|
* E.g. {
|
|
1235
|
-
* createdAt: '1749038400000,1749729600000',
|
|
1236
1239
|
* location: '10-RS',
|
|
1240
|
+
* age: '33',
|
|
1241
|
+
* isDeleted: 'false',
|
|
1242
|
+
* search: 'John Doe',
|
|
1243
|
+
* createdAt: '1749038400000,1749729600000',
|
|
1237
1244
|
* status: 'incomplete',
|
|
1238
|
-
*
|
|
1245
|
+
* bookingDate: '14'
|
|
1246
|
+
* isActive: 'true',
|
|
1247
|
+
* customer.0: '1234567890', // splayed array items
|
|
1239
1248
|
* }
|
|
1240
|
-
* @param {{
|
|
1249
|
+
* @param {{
|
|
1250
|
+
* [key: string]: 'string'|'number'|'boolean'|'search'|'dateRange'|EnumArray|{ rule: 'ids', parseId: parseId }
|
|
1251
|
+
* }} config - allowed filters and their rules
|
|
1241
1252
|
* E.g. {
|
|
1242
|
-
* createdAt: 'dateRange',
|
|
1243
1253
|
* location: 'string',
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1254
|
+
* age: 'number',
|
|
1255
|
+
* isDeleted: 'boolean',
|
|
1256
|
+
* search: 'search',
|
|
1257
|
+
* createdAt: 'dateRange',
|
|
1258
|
+
* status: ['incomplete', 'complete'], // EnumArray
|
|
1259
|
+
* bookingDate: [11, 14, 33], // EnumArray
|
|
1260
|
+
* isActive: [true, false], // EnumArray
|
|
1261
|
+
* customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
|
|
1246
1262
|
* }
|
|
1247
1263
|
* @example returned object (using the examples above):
|
|
1248
1264
|
* E.g. {
|
|
1249
|
-
* date: { $gte: 1749038400000, $lte: 1749729600000 },
|
|
1250
1265
|
* location: '10-RS',
|
|
1266
|
+
* age: 33,
|
|
1267
|
+
* isDeleted: false,
|
|
1268
|
+
* search: { $search: 'John' },
|
|
1269
|
+
* createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
|
|
1251
1270
|
* status: 'incomplete',
|
|
1252
|
-
*
|
|
1271
|
+
* bookingDate: 14,
|
|
1272
|
+
* isActive: true,
|
|
1273
|
+
* customer: { $in: [new ObjectId('1234567890')] },
|
|
1253
1274
|
* }
|
|
1254
1275
|
*/
|
|
1255
1276
|
export function parseFilters(query, config) {
|
|
1256
|
-
/**
|
|
1277
|
+
/**
|
|
1278
|
+
* Should match the example returned object above
|
|
1279
|
+
* @type {{
|
|
1280
|
+
* [key: string]: string|number|boolean|{ $search: string }|{ $gte: number; $lte?: number; }|{ $in: ObjectId[] } }} */
|
|
1257
1281
|
const mongoQuery = {}
|
|
1258
1282
|
|
|
1259
|
-
// Convert splayed array items into a unified array objects
|
|
1283
|
+
// Convert splayed array items into a unified array objects, e.g. 'customer.0' = '1' and 'customer.1' = '2' -> 'customer' = '1,2'
|
|
1260
1284
|
for (const key in query) {
|
|
1261
1285
|
if (key.match(/\.\d+$/)) {
|
|
1262
1286
|
const baseKey = key.replace(/\.\d+$/, '')
|
|
@@ -1278,18 +1302,42 @@ export function parseFilters(query, config) {
|
|
|
1278
1302
|
mongoQuery[key] = val
|
|
1279
1303
|
|
|
1280
1304
|
} else if (rule === 'number') {
|
|
1281
|
-
|
|
1282
|
-
|
|
1305
|
+
const num = parseFloat(val)
|
|
1306
|
+
if (isNaN(num)) throw new Error(`The "${key}" filter should be a number, but received "${val}".`)
|
|
1307
|
+
mongoQuery[key] = num
|
|
1308
|
+
|
|
1309
|
+
} else if (rule === 'boolean') {
|
|
1310
|
+
const bool = val === 'true' ? true : val === 'false' ? false : undefined
|
|
1311
|
+
if (bool === undefined) throw new Error(`The "${key}" filter should be a boolean, but received "${val}".`)
|
|
1312
|
+
mongoQuery[key] = bool
|
|
1283
1313
|
|
|
1284
1314
|
} else if (rule === 'search') {
|
|
1285
1315
|
if (typeof val !== 'string') throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
|
|
1286
1316
|
mongoQuery['$text'] = { $search: '"' + val + '"' }
|
|
1287
1317
|
|
|
1318
|
+
// Enums
|
|
1288
1319
|
} else if (Array.isArray(rule)) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1320
|
+
// Detetect the entire array's type from the first item
|
|
1321
|
+
const type = typeof rule[0]
|
|
1322
|
+
if (!['string', 'number', 'boolean'].includes(type)) {
|
|
1323
|
+
throw new Error(`The rule for "${key}" should only contain strings, numbers or booleans, but received "${type}".`)
|
|
1291
1324
|
}
|
|
1292
|
-
|
|
1325
|
+
// Parse the value to the correct type and compare it to the rule item
|
|
1326
|
+
for (const ruleItem of rule) {
|
|
1327
|
+
let valParsed = /** @type {string|number|boolean|undefined} */(val)
|
|
1328
|
+
if (type === 'number') valParsed = parseFloat(val)
|
|
1329
|
+
else if (type === 'boolean') valParsed = val === 'true' ? true : val === 'false' ? false : undefined
|
|
1330
|
+
if (valParsed === ruleItem) mongoQuery[key] = valParsed
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// Ids
|
|
1334
|
+
} else if (typeof rule === 'object' && 'rule' in rule && rule.rule === 'ids') {
|
|
1335
|
+
const ids = val.split(',').map(id => {
|
|
1336
|
+
if (!isHex24(id)) throw new Error(`Invalid id "${id}" passed to the "${key}" filter.`)
|
|
1337
|
+
else return rule.parseId(id)
|
|
1338
|
+
})
|
|
1339
|
+
if (!ids.length) throw new Error(`Please pass at least one id to the "${key}" filter.`)
|
|
1340
|
+
mongoQuery[key] = { $in: ids }
|
|
1293
1341
|
|
|
1294
1342
|
} else if (rule === 'dateRange') {
|
|
1295
1343
|
const [start, end] = val.split(',').map(Number)
|
|
@@ -1423,7 +1471,7 @@ export function queryString (obj, _path='', _output) {
|
|
|
1423
1471
|
|
|
1424
1472
|
for (let key in obj) {
|
|
1425
1473
|
if (obj.hasOwnProperty(key)) {
|
|
1426
|
-
if (typeof obj[key] == 'undefined' ||
|
|
1474
|
+
if (typeof obj[key] == 'undefined' || obj[key] === '') continue
|
|
1427
1475
|
else if (typeof obj[key] == 'object') queryString(/** @type {{[key: string]: unknown}} */(obj[key]), _path + key + '.', output)
|
|
1428
1476
|
else output[_path + key] = obj[key] + ''
|
|
1429
1477
|
}
|