@djangocfg/ui-core 2.1.147 → 2.1.148
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.148",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"playground": "playground dev"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@djangocfg/i18n": "^2.1.
|
|
69
|
+
"@djangocfg/i18n": "^2.1.148",
|
|
70
70
|
"consola": "^3.4.2",
|
|
71
71
|
"lucide-react": "^0.545.0",
|
|
72
72
|
"moment": "^2.30.1",
|
|
@@ -126,9 +126,9 @@
|
|
|
126
126
|
"vaul": "1.1.2"
|
|
127
127
|
},
|
|
128
128
|
"devDependencies": {
|
|
129
|
-
"@djangocfg/i18n": "^2.1.
|
|
129
|
+
"@djangocfg/i18n": "^2.1.148",
|
|
130
130
|
"@djangocfg/playground": "workspace:*",
|
|
131
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
131
|
+
"@djangocfg/typescript-config": "^2.1.148",
|
|
132
132
|
"@types/node": "^24.7.2",
|
|
133
133
|
"@types/react": "^19.1.0",
|
|
134
134
|
"@types/react-dom": "^19.1.0",
|
|
@@ -4,7 +4,6 @@ import { Check, ChevronsUpDown, Search, X } from 'lucide-react';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { countries, getEmojiFlag, type TCountryCode } from 'countries-list';
|
|
6
6
|
|
|
7
|
-
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
8
7
|
import { cn } from '../lib/utils';
|
|
9
8
|
import { Badge } from './badge';
|
|
10
9
|
import { Button } from './button';
|
|
@@ -34,11 +33,11 @@ export interface CountrySelectProps {
|
|
|
34
33
|
multiple?: boolean;
|
|
35
34
|
/** Display variant: dropdown (popover) or inline (scrollable list) */
|
|
36
35
|
variant?: CountrySelectVariant;
|
|
37
|
-
/** Placeholder text */
|
|
36
|
+
/** Placeholder text (default: "Select country...") */
|
|
38
37
|
placeholder?: string;
|
|
39
|
-
/** Search placeholder text */
|
|
38
|
+
/** Search placeholder text (default: "Search...") */
|
|
40
39
|
searchPlaceholder?: string;
|
|
41
|
-
/** Empty results text */
|
|
40
|
+
/** Empty results text (default: "No countries found") */
|
|
42
41
|
emptyText?: string;
|
|
43
42
|
/** Additional CSS class */
|
|
44
43
|
className?: string;
|
|
@@ -58,6 +57,10 @@ export interface CountrySelectProps {
|
|
|
58
57
|
maxHeight?: number;
|
|
59
58
|
/** Show search input */
|
|
60
59
|
showSearch?: boolean;
|
|
60
|
+
/** Custom label for selected count (receives count as param). Example: (count) => `${count} selected` */
|
|
61
|
+
selectedCountLabel?: (count: number) => string;
|
|
62
|
+
/** Custom label for "more items" badge (receives count as param). Example: (count) => `+${count} more` */
|
|
63
|
+
moreItemsLabel?: (count: number) => string;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/**
|
|
@@ -125,15 +128,16 @@ export function CountrySelect({
|
|
|
125
128
|
excludedCountries,
|
|
126
129
|
maxHeight = 300,
|
|
127
130
|
showSearch = true,
|
|
131
|
+
selectedCountLabel = (count: number) => `${count} selected`,
|
|
132
|
+
moreItemsLabel = (count: number) => `+${count} more`,
|
|
128
133
|
}: CountrySelectProps) {
|
|
129
|
-
const t = useTypedT<I18nTranslations>()
|
|
130
134
|
const [open, setOpen] = React.useState(false)
|
|
131
135
|
const [search, setSearch] = React.useState("")
|
|
132
136
|
|
|
133
|
-
// Resolve
|
|
134
|
-
const resolvedPlaceholder = placeholder ??
|
|
135
|
-
const resolvedSearchPlaceholder = searchPlaceholder ??
|
|
136
|
-
const resolvedEmptyText = emptyText ??
|
|
137
|
+
// Resolve defaults
|
|
138
|
+
const resolvedPlaceholder = placeholder ?? 'Select country...'
|
|
139
|
+
const resolvedSearchPlaceholder = searchPlaceholder ?? 'Search...'
|
|
140
|
+
const resolvedEmptyText = emptyText ?? 'No countries found'
|
|
137
141
|
|
|
138
142
|
// Build country options
|
|
139
143
|
const allCountries = React.useMemo<CountryOption[]>(() => {
|
|
@@ -214,12 +218,12 @@ export function CountrySelect({
|
|
|
214
218
|
{/* Selected count */}
|
|
215
219
|
{multiple && selectedCountries.length > 0 && (
|
|
216
220
|
<p className="text-sm text-muted-foreground">
|
|
217
|
-
{selectedCountries.length
|
|
221
|
+
{selectedCountLabel(selectedCountries.length)}
|
|
218
222
|
</p>
|
|
219
223
|
)}
|
|
220
224
|
|
|
221
225
|
{/* Country list */}
|
|
222
|
-
<ScrollArea style={{ maxHeight }} className="rounded-md border">
|
|
226
|
+
<ScrollArea style={{ height: maxHeight }} className="rounded-md border">
|
|
223
227
|
<div className="p-1">
|
|
224
228
|
{filteredCountries.length === 0 ? (
|
|
225
229
|
<p className="text-sm text-muted-foreground text-center py-4">
|
|
@@ -306,7 +310,7 @@ export function CountrySelect({
|
|
|
306
310
|
className="ml-1 rounded-full hover:bg-muted-foreground/20"
|
|
307
311
|
onClick={(e) => handleRemove(country.code, e)}
|
|
308
312
|
disabled={disabled}
|
|
309
|
-
aria-label={
|
|
313
|
+
aria-label={`Remove ${country.name}`}
|
|
310
314
|
>
|
|
311
315
|
<X className="h-3 w-3" />
|
|
312
316
|
</button>
|
|
@@ -314,12 +318,12 @@ export function CountrySelect({
|
|
|
314
318
|
))}
|
|
315
319
|
{remaining > 0 && (
|
|
316
320
|
<Badge variant="outline" className="text-xs">
|
|
317
|
-
{
|
|
321
|
+
{moreItemsLabel(remaining)}
|
|
318
322
|
</Badge>
|
|
319
323
|
)}
|
|
320
324
|
</div>
|
|
321
325
|
)
|
|
322
|
-
}, [selectedCountries, maxDisplay, resolvedPlaceholder, disabled,
|
|
326
|
+
}, [selectedCountries, maxDisplay, resolvedPlaceholder, disabled, multiple, handleRemove, moreItemsLabel])
|
|
323
327
|
|
|
324
328
|
return (
|
|
325
329
|
<Popover
|