@carlonicora/nextjs-jsonapi 1.62.0 → 1.63.0

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 +1 @@
1
- {"version":3,"file":"selector.template.d.ts","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/selector.template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAwK3E"}
1
+ {"version":3,"file":"selector.template.d.ts","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/selector.template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CA4K3E"}
@@ -20,14 +20,11 @@ function generateSelectorTemplate(data) {
20
20
  return `"use client";
21
21
 
22
22
  import {
23
- Button,
24
23
  Command,
25
- CommandEmpty,
26
- CommandGroup,
27
- CommandInput,
28
24
  CommandItem,
29
25
  CommandList,
30
26
  FormFieldWrapper,
27
+ Input,
31
28
  Popover,
32
29
  PopoverContent,
33
30
  PopoverTrigger,
@@ -38,7 +35,7 @@ import { DataListRetriever, useDataListRetriever } from "@carlonicora/nextjs-jso
38
35
  import { useDebounce } from "@carlonicora/nextjs-jsonapi/client";
39
36
  import { Modules } from "@carlonicora/nextjs-jsonapi/core";
40
37
 
41
- import { ChevronsUpDown, Loader2, XIcon } from "lucide-react";
38
+ import { CircleX, RefreshCwIcon, SearchIcon, XIcon } from "lucide-react";
42
39
  import { useTranslations } from "next-intl";
43
40
  import { useCallback, useEffect, useRef, useState } from "react";
44
41
 
@@ -100,12 +97,12 @@ export default function ${names.pascalCase}Selector({
100
97
  const set${names.pascalCase} = (${names.camelCase}?: ${names.pascalCase}Interface) => {
101
98
  if (onChange) onChange(${names.camelCase});
102
99
  if (!${names.camelCase}) {
103
- form.setValue(id, undefined);
100
+ form.setValue(id, undefined, { shouldDirty: true });
104
101
  setOpen(false);
105
102
  return;
106
103
  }
107
104
 
108
- form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} });
105
+ form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} }, { shouldDirty: true });
109
106
  setOpen(false);
110
107
 
111
108
  setTimeout(() => {
@@ -120,55 +117,62 @@ export default function ${names.pascalCase}Selector({
120
117
  <Popover open={open} onOpenChange={setOpen} modal={true}>
121
118
  <div className="flex w-full flex-row items-center justify-between">
122
119
  <PopoverTrigger className="w-full">
123
- <Button
124
- variant="outline"
125
- role="combobox"
126
- aria-expanded={open}
127
- className="h-auto min-h-10 w-full justify-between px-3 py-2"
128
- >
129
- <span className={field.value ? "" : "text-muted-foreground"}>
130
- {field.value?.name ?? placeholder ?? t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
131
- </span>
132
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
133
- </Button>
120
+ <div className="flex w-full flex-row items-center justify-start rounded-md">
121
+ {field.value ? (
122
+ <div className="bg-input/20 dark:bg-input/30 border-input flex h-7 w-full flex-row items-center justify-start rounded-md border px-2 py-0.5 text-sm md:text-xs/relaxed">
123
+ <span>{field.value?.name ?? ""}</span>
124
+ </div>
125
+ ) : (
126
+ <div className="bg-input/20 dark:bg-input/30 border-input text-muted-foreground flex h-7 w-full flex-row items-center justify-start rounded-md border px-2 py-0.5 text-sm md:text-xs/relaxed">
127
+ {placeholder ?? t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
128
+ </div>
129
+ )}
130
+ </div>
134
131
  </PopoverTrigger>
135
132
  {field.value && (
136
- <XIcon
137
- className="text-muted-foreground hover:text-destructive ml-2 h-5 w-5 cursor-pointer"
133
+ <CircleX
134
+ className="text-muted hover:text-destructive ml-2 h-4 w-4 shrink-0 cursor-pointer"
138
135
  onClick={() => set${names.pascalCase}()}
139
136
  />
140
137
  )}
141
138
  </div>
142
- <PopoverContent className="w-(--radix-popover-trigger-width) p-0" align="start">
139
+ <PopoverContent align="start" className="w-(--anchor-width)">
143
140
  <Command shouldFilter={false}>
144
- <CommandInput
145
- placeholder={t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
146
- value={searchTerm}
147
- onValueChange={setSearchTerm}
148
- />
149
- <CommandList>
150
- {isSearching && (
151
- <div className="flex items-center justify-center py-6">
152
- <Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
153
- </div>
154
- )}
155
- {!isSearching && data.data && data.data.length === 0 && (
156
- <CommandEmpty>{t(\`generic.search.no_results\`)}</CommandEmpty>
157
- )}
158
- {!isSearching && data.data && data.data.length > 0 && (
159
- <CommandGroup>
160
- {(data.data as ${names.pascalCase}Interface[]).map((${names.camelCase}: ${names.pascalCase}Interface) => (
161
- <CommandItem
162
- key={${names.camelCase}.id}
163
- value={${names.camelCase}.id}
164
- onSelect={() => set${names.pascalCase}(${names.camelCase})}
165
- className="hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent cursor-pointer"
166
- >
167
- {${names.camelCase}.${displayProp}}
168
- </CommandItem>
169
- ))}
170
- </CommandGroup>
141
+ <div className="relative mb-2 w-full">
142
+ <SearchIcon className="text-muted-foreground absolute top-2.5 left-2.5 h-4 w-4" />
143
+ <Input
144
+ placeholder={t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
145
+ type="text"
146
+ className="w-full pr-8 pl-8"
147
+ onChange={(e) => setSearchTerm(e.target.value)}
148
+ value={searchTerm}
149
+ />
150
+ {isSearching ? (
151
+ <RefreshCwIcon className="text-muted-foreground absolute top-2.5 right-2.5 h-4 w-4 animate-spin" />
152
+ ) : searchTermRef.current ? (
153
+ <XIcon
154
+ className={\`absolute top-2.5 right-2.5 h-4 w-4 \${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}\`}
155
+ onClick={() => {
156
+ setSearchTerm("");
157
+ search("");
158
+ }}
159
+ />
160
+ ) : (
161
+ <></>
171
162
  )}
163
+ </div>
164
+ <CommandList>
165
+ {data.data &&
166
+ data.data.length > 0 &&
167
+ (data.data as ${names.pascalCase}Interface[]).map((${names.camelCase}: ${names.pascalCase}Interface) => (
168
+ <CommandItem
169
+ className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
170
+ key={${names.camelCase}.id}
171
+ onSelect={() => set${names.pascalCase}(${names.camelCase})}
172
+ >
173
+ {${names.camelCase}.${displayProp}}
174
+ </CommandItem>
175
+ ))}
172
176
  </CommandList>
173
177
  </Command>
174
178
  </PopoverContent>
@@ -1 +1 @@
1
- {"version":3,"file":"selector.template.js","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/selector.template.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,4DAwKC;AA9KD;;;;;GAKG;AACH,SAAgB,wBAAwB,CAAC,IAA0B;IACjE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;IAChD,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjD,OAAO;;;;;;;;;;;;;;;WAeE,KAAK,CAAC,UAAU,gCAAgC,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;WAChH,KAAK,CAAC,UAAU,8BAA8B,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;;;;;;;;;OASlH,KAAK,CAAC,UAAU;;;;;gBAKP,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,UAAU;;;;0BAI3B,KAAK,CAAC,UAAU;;;;;;;KAOrC,KAAK,CAAC,UAAU;;;;;;;;;;kCAUa,KAAK,CAAC,UAAU;;eAEnC,KAAK,CAAC,UAAU;;;sBAGT,KAAK,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;aAwBzB,KAAK,CAAC,UAAU,OAAO,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,UAAU;6BAC5C,KAAK,CAAC,SAAS;WACjC,KAAK,CAAC,SAAS;;;;;;8BAMI,KAAK,CAAC,SAAS,cAAc,KAAK,CAAC,SAAS,IAAI,WAAW;;;;;;;;;;;;;;;;;;;;;;kHAsByB,OAAO;;;;;;;;sCAQnF,KAAK,CAAC,UAAU;;;;;;;wFAOkC,OAAO;;;;;;;;;;;;;;;uCAexD,KAAK,CAAC,UAAU,qBAAqB,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,UAAU;;iCAE/E,KAAK,CAAC,SAAS;mCACb,KAAK,CAAC,SAAS;+CACH,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS;;;6BAGrD,KAAK,CAAC,SAAS,IAAI,WAAW;;;;;;;;;;;;;;CAc1D,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"selector.template.js","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/selector.template.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,4DA4KC;AAlLD;;;;;GAKG;AACH,SAAgB,wBAAwB,CAAC,IAA0B;IACjE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;IAChD,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjD,OAAO;;;;;;;;;;;;WAYE,KAAK,CAAC,UAAU,gCAAgC,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;WAChH,KAAK,CAAC,UAAU,8BAA8B,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;;;;;;;;;OASlH,KAAK,CAAC,UAAU;;;;;gBAKP,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,UAAU;;;;0BAI3B,KAAK,CAAC,UAAU;;;;;;;KAOrC,KAAK,CAAC,UAAU;;;;;;;;;;kCAUa,KAAK,CAAC,UAAU;;eAEnC,KAAK,CAAC,UAAU;;;sBAGT,KAAK,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;aAwBzB,KAAK,CAAC,UAAU,OAAO,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,UAAU;6BAC5C,KAAK,CAAC,SAAS;WACjC,KAAK,CAAC,SAAS;;;;;;8BAMI,KAAK,CAAC,SAAS,cAAc,KAAK,CAAC,SAAS,IAAI,WAAW;;;;;;;;;;;;;;;;;;;;;;+FAsBM,OAAO;;;;;;;;sCAQhE,KAAK,CAAC,UAAU;;;;;;;;;0FASoC,OAAO;;;;;;;;;;;;;;;;;;;;;;;oCAuB7D,KAAK,CAAC,UAAU,qBAAqB,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,UAAU;;;+BAG9E,KAAK,CAAC,SAAS;6CACD,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS;;2BAErD,KAAK,CAAC,SAAS,IAAI,WAAW;;;;;;;;;;;;CAYxD,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "1.62.0",
3
+ "version": "1.63.0",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -21,14 +21,11 @@ export function generateSelectorTemplate(data: FrontendTemplateData): string {
21
21
  return `"use client";
22
22
 
23
23
  import {
24
- Button,
25
24
  Command,
26
- CommandEmpty,
27
- CommandGroup,
28
- CommandInput,
29
25
  CommandItem,
30
26
  CommandList,
31
27
  FormFieldWrapper,
28
+ Input,
32
29
  Popover,
33
30
  PopoverContent,
34
31
  PopoverTrigger,
@@ -39,7 +36,7 @@ import { DataListRetriever, useDataListRetriever } from "@carlonicora/nextjs-jso
39
36
  import { useDebounce } from "@carlonicora/nextjs-jsonapi/client";
40
37
  import { Modules } from "@carlonicora/nextjs-jsonapi/core";
41
38
 
42
- import { ChevronsUpDown, Loader2, XIcon } from "lucide-react";
39
+ import { CircleX, RefreshCwIcon, SearchIcon, XIcon } from "lucide-react";
43
40
  import { useTranslations } from "next-intl";
44
41
  import { useCallback, useEffect, useRef, useState } from "react";
45
42
 
@@ -101,12 +98,12 @@ export default function ${names.pascalCase}Selector({
101
98
  const set${names.pascalCase} = (${names.camelCase}?: ${names.pascalCase}Interface) => {
102
99
  if (onChange) onChange(${names.camelCase});
103
100
  if (!${names.camelCase}) {
104
- form.setValue(id, undefined);
101
+ form.setValue(id, undefined, { shouldDirty: true });
105
102
  setOpen(false);
106
103
  return;
107
104
  }
108
105
 
109
- form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} });
106
+ form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} }, { shouldDirty: true });
110
107
  setOpen(false);
111
108
 
112
109
  setTimeout(() => {
@@ -121,55 +118,62 @@ export default function ${names.pascalCase}Selector({
121
118
  <Popover open={open} onOpenChange={setOpen} modal={true}>
122
119
  <div className="flex w-full flex-row items-center justify-between">
123
120
  <PopoverTrigger className="w-full">
124
- <Button
125
- variant="outline"
126
- role="combobox"
127
- aria-expanded={open}
128
- className="h-auto min-h-10 w-full justify-between px-3 py-2"
129
- >
130
- <span className={field.value ? "" : "text-muted-foreground"}>
131
- {field.value?.name ?? placeholder ?? t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
132
- </span>
133
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
134
- </Button>
121
+ <div className="flex w-full flex-row items-center justify-start rounded-md">
122
+ {field.value ? (
123
+ <div className="bg-input/20 dark:bg-input/30 border-input flex h-7 w-full flex-row items-center justify-start rounded-md border px-2 py-0.5 text-sm md:text-xs/relaxed">
124
+ <span>{field.value?.name ?? ""}</span>
125
+ </div>
126
+ ) : (
127
+ <div className="bg-input/20 dark:bg-input/30 border-input text-muted-foreground flex h-7 w-full flex-row items-center justify-start rounded-md border px-2 py-0.5 text-sm md:text-xs/relaxed">
128
+ {placeholder ?? t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
129
+ </div>
130
+ )}
131
+ </div>
135
132
  </PopoverTrigger>
136
133
  {field.value && (
137
- <XIcon
138
- className="text-muted-foreground hover:text-destructive ml-2 h-5 w-5 cursor-pointer"
134
+ <CircleX
135
+ className="text-muted hover:text-destructive ml-2 h-4 w-4 shrink-0 cursor-pointer"
139
136
  onClick={() => set${names.pascalCase}()}
140
137
  />
141
138
  )}
142
139
  </div>
143
- <PopoverContent className="w-(--radix-popover-trigger-width) p-0" align="start">
140
+ <PopoverContent align="start" className="w-(--anchor-width)">
144
141
  <Command shouldFilter={false}>
145
- <CommandInput
146
- placeholder={t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
147
- value={searchTerm}
148
- onValueChange={setSearchTerm}
149
- />
150
- <CommandList>
151
- {isSearching && (
152
- <div className="flex items-center justify-center py-6">
153
- <Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
154
- </div>
155
- )}
156
- {!isSearching && data.data && data.data.length === 0 && (
157
- <CommandEmpty>{t(\`generic.search.no_results\`)}</CommandEmpty>
158
- )}
159
- {!isSearching && data.data && data.data.length > 0 && (
160
- <CommandGroup>
161
- {(data.data as ${names.pascalCase}Interface[]).map((${names.camelCase}: ${names.pascalCase}Interface) => (
162
- <CommandItem
163
- key={${names.camelCase}.id}
164
- value={${names.camelCase}.id}
165
- onSelect={() => set${names.pascalCase}(${names.camelCase})}
166
- className="hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent cursor-pointer"
167
- >
168
- {${names.camelCase}.${displayProp}}
169
- </CommandItem>
170
- ))}
171
- </CommandGroup>
142
+ <div className="relative mb-2 w-full">
143
+ <SearchIcon className="text-muted-foreground absolute top-2.5 left-2.5 h-4 w-4" />
144
+ <Input
145
+ placeholder={t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
146
+ type="text"
147
+ className="w-full pr-8 pl-8"
148
+ onChange={(e) => setSearchTerm(e.target.value)}
149
+ value={searchTerm}
150
+ />
151
+ {isSearching ? (
152
+ <RefreshCwIcon className="text-muted-foreground absolute top-2.5 right-2.5 h-4 w-4 animate-spin" />
153
+ ) : searchTermRef.current ? (
154
+ <XIcon
155
+ className={\`absolute top-2.5 right-2.5 h-4 w-4 \${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}\`}
156
+ onClick={() => {
157
+ setSearchTerm("");
158
+ search("");
159
+ }}
160
+ />
161
+ ) : (
162
+ <></>
172
163
  )}
164
+ </div>
165
+ <CommandList>
166
+ {data.data &&
167
+ data.data.length > 0 &&
168
+ (data.data as ${names.pascalCase}Interface[]).map((${names.camelCase}: ${names.pascalCase}Interface) => (
169
+ <CommandItem
170
+ className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
171
+ key={${names.camelCase}.id}
172
+ onSelect={() => set${names.pascalCase}(${names.camelCase})}
173
+ >
174
+ {${names.camelCase}.${displayProp}}
175
+ </CommandItem>
176
+ ))}
173
177
  </CommandList>
174
178
  </Command>
175
179
  </PopoverContent>