@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.147",
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.147",
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.147",
129
+ "@djangocfg/i18n": "^2.1.148",
130
130
  "@djangocfg/playground": "workspace:*",
131
- "@djangocfg/typescript-config": "^2.1.147",
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 translations
134
- const resolvedPlaceholder = placeholder ?? t('ui.select.placeholder')
135
- const resolvedSearchPlaceholder = searchPlaceholder ?? t('ui.select.search')
136
- const resolvedEmptyText = emptyText ?? t('ui.select.noResults')
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} {t('ui.table.selected')}
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={t('ui.actions.remove', { item: country.name })}
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
- {t('ui.select.moreItems', { count: remaining })}
321
+ {moreItemsLabel(remaining)}
318
322
  </Badge>
319
323
  )}
320
324
  </div>
321
325
  )
322
- }, [selectedCountries, maxDisplay, resolvedPlaceholder, disabled, t, multiple, handleRemove])
326
+ }, [selectedCountries, maxDisplay, resolvedPlaceholder, disabled, multiple, handleRemove, moreItemsLabel])
323
327
 
324
328
  return (
325
329
  <Popover