@djangocfg/ui-core 2.1.381 → 2.1.383

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.
Files changed (78) hide show
  1. package/README.md +85 -21
  2. package/package.json +5 -12
  3. package/src/components/boundary/Boundary.tsx +204 -33
  4. package/src/components/boundary/README.md +249 -0
  5. package/src/components/boundary/index.ts +9 -2
  6. package/src/components/index.ts +9 -2
  7. package/src/components/select/combobox.tsx +47 -19
  8. package/src/hooks/audio/createSoundBus.ts +172 -0
  9. package/src/hooks/audio/index.ts +21 -0
  10. package/src/hooks/audio/useAudioPrefs.ts +91 -0
  11. package/src/hooks/audio/useNotificationSounds.ts +271 -0
  12. package/src/hooks/audio/useSoundEffect.ts +78 -0
  13. package/src/hooks/hotkey/formatHotkey.ts +96 -0
  14. package/src/hooks/hotkey/index.ts +10 -0
  15. package/src/hooks/hotkey/useHotkey.ts +106 -34
  16. package/src/hooks/hotkey/useHotkeyChord.ts +96 -0
  17. package/src/hooks/hotkey/useHotkeyHelp.ts +68 -0
  18. package/src/hooks/index.ts +1 -0
  19. package/src/components/boundary/boundary.story.tsx +0 -109
  20. package/src/components/data/avatar/avatar.story.tsx +0 -115
  21. package/src/components/data/badge/badge.story.tsx +0 -56
  22. package/src/components/data/calendar/calendar.story.tsx +0 -127
  23. package/src/components/data/carousel/carousel.story.tsx +0 -122
  24. package/src/components/data/progress/progress.story.tsx +0 -97
  25. package/src/components/data/table/table.story.tsx +0 -148
  26. package/src/components/data/toggle/toggle.story.tsx +0 -104
  27. package/src/components/data/toggle-group/toggle-group.story.tsx +0 -118
  28. package/src/components/feedback/alert/alert.story.tsx +0 -77
  29. package/src/components/feedback/empty/empty.story.tsx +0 -115
  30. package/src/components/feedback/preloader/preloader.story.tsx +0 -86
  31. package/src/components/feedback/spinner/spinner.story.tsx +0 -66
  32. package/src/components/forms/button/button.story.tsx +0 -116
  33. package/src/components/forms/button-download/button-download.story.tsx +0 -112
  34. package/src/components/forms/button-group/button-group.story.tsx +0 -79
  35. package/src/components/forms/checkbox/checkbox.story.tsx +0 -89
  36. package/src/components/forms/input/input.story.tsx +0 -77
  37. package/src/components/forms/input-group/input-group.story.tsx +0 -119
  38. package/src/components/forms/input-otp/input-otp.story.tsx +0 -105
  39. package/src/components/forms/label/label.story.tsx +0 -52
  40. package/src/components/forms/radio-group/radio-group.story.tsx +0 -113
  41. package/src/components/forms/slider/slider.story.tsx +0 -134
  42. package/src/components/forms/switch/switch.story.tsx +0 -98
  43. package/src/components/forms/textarea/textarea.story.tsx +0 -94
  44. package/src/components/layout/aspect-ratio/aspect-ratio.story.tsx +0 -94
  45. package/src/components/layout/card/card.story.tsx +0 -105
  46. package/src/components/layout/resizable/resizable.story.tsx +0 -119
  47. package/src/components/layout/scroll-area/scroll-area.story.tsx +0 -172
  48. package/src/components/layout/separator/separator.story.tsx +0 -69
  49. package/src/components/layout/skeleton/skeleton.story.tsx +0 -101
  50. package/src/components/navigation/accordion/accordion.story.tsx +0 -110
  51. package/src/components/navigation/collapsible/collapsible.story.tsx +0 -133
  52. package/src/components/navigation/command/command.story.tsx +0 -121
  53. package/src/components/navigation/context-menu/context-menu.story.tsx +0 -125
  54. package/src/components/navigation/dropdown-menu/dropdown-menu.story.tsx +0 -208
  55. package/src/components/navigation/menubar/menubar.story.tsx +0 -152
  56. package/src/components/navigation/navigation-menu/navigation-menu.story.tsx +0 -154
  57. package/src/components/navigation/tabs/tabs.story.tsx +0 -98
  58. package/src/components/overlay/alert-dialog/alert-dialog.story.tsx +0 -104
  59. package/src/components/overlay/dialog/dialog.story.tsx +0 -212
  60. package/src/components/overlay/drawer/drawer.story.tsx +0 -359
  61. package/src/components/overlay/hover-card/hover-card.story.tsx +0 -102
  62. package/src/components/overlay/popover/popover.story.tsx +0 -127
  63. package/src/components/overlay/responsive-sheet/responsive-sheet.story.tsx +0 -117
  64. package/src/components/overlay/sheet/sheet.story.tsx +0 -148
  65. package/src/components/overlay/tooltip/tooltip.story.tsx +0 -139
  66. package/src/components/select/combobox-async.story.tsx +0 -215
  67. package/src/components/select/combobox.story.tsx +0 -226
  68. package/src/components/select/country-select.story.tsx +0 -261
  69. package/src/components/select/language-select.story.tsx +0 -264
  70. package/src/components/select/multi-select.story.tsx +0 -122
  71. package/src/components/select/select.story.tsx +0 -112
  72. package/src/components/specialized/copy/copy.story.tsx +0 -77
  73. package/src/components/specialized/flag/flag.story.tsx +0 -82
  74. package/src/components/specialized/image-with-fallback/image-with-fallback.story.tsx +0 -105
  75. package/src/components/specialized/kbd/kbd.story.tsx +0 -113
  76. package/src/lib/dialog-service/dialog-service.story.tsx +0 -263
  77. package/src/stories/index.ts +0 -28
  78. package/src/styles/theme/theme-tokens.story.tsx +0 -157
@@ -1,215 +0,0 @@
1
- import { useEffect, useMemo, useState } from 'react';
2
-
3
- import { defineStory, useBoolean } from '@djangocfg/playground';
4
-
5
- import { ComboboxAsync, type ComboboxAsyncOption } from '.';
6
- import { Label } from '../forms/label';
7
-
8
- export default defineStory({
9
- title: 'Core/ComboboxAsync',
10
- component: ComboboxAsync,
11
- description:
12
- 'Single-select combobox where the parent owns search + loading. Pairs with `MultiSelectProAsync` for the "pick one" case against a server-side typeahead endpoint.',
13
- });
14
-
15
- // ─── Fake "server" ──────────────────────────────────────────────────
16
- //
17
- // In real usage, ``options`` would come from an SWR / RQ hook keyed off
18
- // the debounced search value. For the playground we simulate latency
19
- // with a setTimeout so the loading affordance and seedOptions story
20
- // can be exercised without a real backend.
21
-
22
- interface Person {
23
- id: string;
24
- name: string;
25
- email: string;
26
- team: string;
27
- }
28
-
29
- const PEOPLE: Person[] = [
30
- { id: 'p-1', name: 'Alice Lee', email: 'alice@x.co', team: 'Platform' },
31
- { id: 'p-2', name: 'Bob Schmidt', email: 'bob@x.co', team: 'Platform' },
32
- { id: 'p-3', name: 'Carla Romero', email: 'carla@x.co', team: 'Growth' },
33
- { id: 'p-4', name: 'Dmitri Volkov', email: 'dmitri@x.co', team: 'Growth' },
34
- { id: 'p-5', name: 'Evelyn Park', email: 'evelyn@x.co', team: 'Design' },
35
- { id: 'p-6', name: 'Farah Patel', email: 'farah@x.co', team: 'Design' },
36
- { id: 'p-7', name: 'Greta Hahn', email: 'greta@x.co', team: 'Engineering' },
37
- { id: 'p-8', name: 'Hiro Tanaka', email: 'hiro@x.co', team: 'Engineering' },
38
- ];
39
-
40
- function fakeSearch(query: string): Promise<Person[]> {
41
- return new Promise((resolve) => {
42
- const q = query.trim().toLowerCase();
43
- const matches = q
44
- ? PEOPLE.filter(
45
- (p) =>
46
- p.name.toLowerCase().includes(q) ||
47
- p.email.toLowerCase().includes(q) ||
48
- p.team.toLowerCase().includes(q),
49
- )
50
- : PEOPLE.slice(0, 6);
51
- setTimeout(() => resolve(matches), 350);
52
- });
53
- }
54
-
55
- function toOption(p: Person): ComboboxAsyncOption {
56
- return {
57
- value: p.id,
58
- label: p.name,
59
- description: `${p.email} · ${p.team}`,
60
- };
61
- }
62
-
63
- // ─── Stories ────────────────────────────────────────────────────────
64
-
65
- export const Interactive = () => {
66
- const [value, setValue] = useState<string | null>(null);
67
- const [search, setSearch] = useState('');
68
- const [options, setOptions] = useState<ComboboxAsyncOption[]>([]);
69
- const [isLoading, setIsLoading] = useState(false);
70
- const [disabled] = useBoolean('disabled', {
71
- defaultValue: false,
72
- label: 'Disabled',
73
- description: 'Disable picker',
74
- });
75
- const [clearable] = useBoolean('clearable', {
76
- defaultValue: true,
77
- label: 'Clearable',
78
- description: 'Show inline × to reset selection',
79
- });
80
-
81
- useEffect(() => {
82
- let cancelled = false;
83
- setIsLoading(true);
84
- void fakeSearch(search).then((rows) => {
85
- if (cancelled) return;
86
- setOptions(rows.map(toOption));
87
- setIsLoading(false);
88
- });
89
- return () => {
90
- cancelled = true;
91
- };
92
- }, [search]);
93
-
94
- return (
95
- <div className="max-w-sm space-y-2">
96
- <Label>Assignee</Label>
97
- <ComboboxAsync
98
- options={options}
99
- value={value}
100
- onValueChange={setValue}
101
- searchValue={search}
102
- onSearchChange={setSearch}
103
- isLoading={isLoading}
104
- placeholder="Pick a teammate…"
105
- searchPlaceholder="Search by name, email, team…"
106
- emptyText="No teammates found"
107
- loadingText="Searching…"
108
- disabled={disabled}
109
- clearable={clearable}
110
- />
111
- <p className="text-xs text-muted-foreground">
112
- Selected: {value ?? '∅'}
113
- </p>
114
- </div>
115
- );
116
- };
117
-
118
- export const Default = Interactive;
119
-
120
- export const Loading = () => {
121
- // Force ``isLoading`` true and an empty options list to show the
122
- // initial spinner state — the same row that renders before the
123
- // first server response lands.
124
- const [value, setValue] = useState<string | null>(null);
125
- const [search, setSearch] = useState('');
126
-
127
- return (
128
- <div className="max-w-sm space-y-2">
129
- <Label>Assignee</Label>
130
- <ComboboxAsync
131
- options={[]}
132
- value={value}
133
- onValueChange={setValue}
134
- searchValue={search}
135
- onSearchChange={setSearch}
136
- isLoading
137
- placeholder="Pick a teammate…"
138
- />
139
- </div>
140
- );
141
- };
142
-
143
- export const Empty = () => {
144
- // Loaded but no matches — distinct visual from the loading row above.
145
- const [value, setValue] = useState<string | null>(null);
146
-
147
- return (
148
- <div className="max-w-sm space-y-2">
149
- <Label>Assignee</Label>
150
- <ComboboxAsync
151
- options={[]}
152
- value={value}
153
- onValueChange={setValue}
154
- searchValue="zzznoresult"
155
- onSearchChange={() => {}}
156
- isLoading={false}
157
- emptyText="No teammates found"
158
- />
159
- </div>
160
- );
161
- };
162
-
163
- export const SeedOption = () => {
164
- // The dialog opens with a pre-existing value whose row isn't part of
165
- // the live results. ``seedOptions`` keeps the trigger from rendering
166
- // a raw id while the user starts typing.
167
- const [value, setValue] = useState<string | null>('p-stale');
168
- const [search, setSearch] = useState('');
169
- const [options, setOptions] = useState<ComboboxAsyncOption[]>([]);
170
- const [isLoading, setIsLoading] = useState(false);
171
-
172
- useEffect(() => {
173
- let cancelled = false;
174
- setIsLoading(true);
175
- void fakeSearch(search).then((rows) => {
176
- if (cancelled) return;
177
- setOptions(rows.map(toOption));
178
- setIsLoading(false);
179
- });
180
- return () => {
181
- cancelled = true;
182
- };
183
- }, [search]);
184
-
185
- const seedOptions = useMemo<ComboboxAsyncOption[]>(
186
- () => [
187
- {
188
- value: 'p-stale',
189
- label: 'Pre-selected (offline)',
190
- description: 'Fetched once, kept around as a seed',
191
- },
192
- ],
193
- [],
194
- );
195
-
196
- return (
197
- <div className="max-w-sm space-y-2">
198
- <Label>Assignee</Label>
199
- <ComboboxAsync
200
- options={options}
201
- value={value}
202
- onValueChange={setValue}
203
- searchValue={search}
204
- onSearchChange={setSearch}
205
- isLoading={isLoading}
206
- seedOptions={seedOptions}
207
- placeholder="Pick a teammate…"
208
- />
209
- <p className="text-xs text-muted-foreground">
210
- Selected id: <code>{value ?? '∅'}</code> — note the trigger shows the
211
- seed label even though <code>p-stale</code> isn't in the search results.
212
- </p>
213
- </div>
214
- );
215
- };
@@ -1,226 +0,0 @@
1
- import { useState } from 'react';
2
- import { defineStory, useBoolean } from '@djangocfg/playground';
3
- import { Combobox, type ComboboxOption } from '.';
4
- import { Label } from '../forms/label';
5
-
6
- export default defineStory({
7
- title: 'Core/Combobox',
8
- component: Combobox,
9
- description: 'Searchable select with autocomplete.',
10
- });
11
-
12
- const frameworks: ComboboxOption[] = [
13
- { value: 'next.js', label: 'Next.js' },
14
- { value: 'sveltekit', label: 'SvelteKit' },
15
- { value: 'nuxt.js', label: 'Nuxt.js' },
16
- { value: 'remix', label: 'Remix' },
17
- { value: 'astro', label: 'Astro' },
18
- ];
19
-
20
- const countries: ComboboxOption[] = [
21
- { value: 'us', label: 'United States' },
22
- { value: 'uk', label: 'United Kingdom' },
23
- { value: 'de', label: 'Germany' },
24
- { value: 'fr', label: 'France' },
25
- { value: 'jp', label: 'Japan' },
26
- { value: 'kr', label: 'South Korea' },
27
- { value: 'cn', label: 'China' },
28
- { value: 'au', label: 'Australia' },
29
- { value: 'ca', label: 'Canada' },
30
- { value: 'br', label: 'Brazil' },
31
- ];
32
-
33
- export const Interactive = () => {
34
- const [value, setValue] = useState('');
35
- const [disabled] = useBoolean('disabled', {
36
- defaultValue: false,
37
- label: 'Disabled',
38
- description: 'Disable combobox',
39
- });
40
-
41
- return (
42
- <div className="max-w-xs space-y-2">
43
- <Label>Framework</Label>
44
- <Combobox
45
- options={frameworks}
46
- value={value}
47
- onValueChange={setValue}
48
- placeholder="Select framework..."
49
- searchPlaceholder="Search framework..."
50
- emptyText="No framework found."
51
- disabled={disabled}
52
- />
53
- </div>
54
- );
55
- };
56
-
57
- export const Default = () => {
58
- const [value, setValue] = useState('');
59
-
60
- return (
61
- <div className="max-w-xs">
62
- <Combobox
63
- options={frameworks}
64
- value={value}
65
- onValueChange={setValue}
66
- placeholder="Select framework..."
67
- searchPlaceholder="Search framework..."
68
- emptyText="No framework found."
69
- />
70
- </div>
71
- );
72
- };
73
-
74
- export const WithDefaultValue = () => {
75
- const [value, setValue] = useState('next.js');
76
-
77
- return (
78
- <div className="max-w-xs">
79
- <Combobox
80
- options={frameworks}
81
- value={value}
82
- onValueChange={setValue}
83
- placeholder="Select framework..."
84
- />
85
- </div>
86
- );
87
- };
88
-
89
- export const Countries = () => {
90
- const [value, setValue] = useState('');
91
-
92
- return (
93
- <div className="max-w-xs space-y-2">
94
- <Label>Country</Label>
95
- <Combobox
96
- options={countries}
97
- value={value}
98
- onValueChange={setValue}
99
- placeholder="Select country..."
100
- searchPlaceholder="Search countries..."
101
- emptyText="No country found."
102
- />
103
- </div>
104
- );
105
- };
106
-
107
- export const Disabled = () => (
108
- <div className="max-w-xs">
109
- <Combobox
110
- options={frameworks}
111
- value=""
112
- onValueChange={() => {}}
113
- placeholder="Select framework..."
114
- disabled
115
- />
116
- </div>
117
- );
118
-
119
- export const Form = () => {
120
- const [framework, setFramework] = useState('');
121
- const [country, setCountry] = useState('');
122
-
123
- return (
124
- <div className="max-w-xs space-y-4">
125
- <div className="space-y-2">
126
- <Label>Framework</Label>
127
- <Combobox
128
- options={frameworks}
129
- value={framework}
130
- onValueChange={setFramework}
131
- placeholder="Select framework..."
132
- />
133
- </div>
134
- <div className="space-y-2">
135
- <Label>Country</Label>
136
- <Combobox
137
- options={countries}
138
- value={country}
139
- onValueChange={setCountry}
140
- placeholder="Select country..."
141
- />
142
- </div>
143
- </div>
144
- );
145
- };
146
-
147
- // Trading symbols for custom filter demo
148
- const symbols: ComboboxOption[] = [
149
- { value: 'BTCUSDT', label: 'BTCUSDT', description: 'BTC/USDT' },
150
- { value: 'ETHUSDT', label: 'ETHUSDT', description: 'ETH/USDT' },
151
- { value: 'ETHBTC', label: 'ETHBTC', description: 'ETH/BTC' },
152
- { value: 'BNBUSDT', label: 'BNBUSDT', description: 'BNB/USDT' },
153
- { value: 'BNBBTC', label: 'BNBBTC', description: 'BNB/BTC' },
154
- { value: 'SOLUSDT', label: 'SOLUSDT', description: 'SOL/USDT' },
155
- { value: 'SOLBTC', label: 'SOLBTC', description: 'SOL/BTC' },
156
- { value: 'ADAUSDT', label: 'ADAUSDT', description: 'ADA/USDT' },
157
- { value: 'DOGEUSDT', label: 'DOGEUSDT', description: 'DOGE/USDT' },
158
- { value: 'XRPUSDT', label: 'XRPUSDT', description: 'XRP/USDT' },
159
- ];
160
-
161
- /**
162
- * Multi-word filter: all search terms must match.
163
- * "btc eth" matches ETHBTC (contains both "btc" and "eth")
164
- */
165
- function multiWordFilter(option: ComboboxOption, search: string): boolean {
166
- const terms = search.toLowerCase().trim().split(/\s+/).filter(Boolean);
167
- if (terms.length === 0) return true;
168
-
169
- const text = [option.label, option.value, option.description || '']
170
- .join(' ')
171
- .toLowerCase();
172
-
173
- return terms.every((term) => text.includes(term));
174
- }
175
-
176
- export const CustomFilter = () => {
177
- const [value, setValue] = useState('');
178
-
179
- return (
180
- <div className="max-w-xs space-y-2">
181
- <Label>Symbol (try "btc eth" or "usdt sol")</Label>
182
- <Combobox
183
- options={symbols}
184
- value={value}
185
- onValueChange={setValue}
186
- placeholder="Select symbol..."
187
- searchPlaceholder="Search... (e.g. btc usdt)"
188
- emptyText="No symbols found."
189
- filterFunction={multiWordFilter}
190
- renderOption={(option) => (
191
- <div className="flex items-center justify-between w-full">
192
- <span className="font-medium">{option.label}</span>
193
- <span className="text-xs text-muted-foreground">{option.description}</span>
194
- </div>
195
- )}
196
- />
197
- <p className="text-xs text-muted-foreground">
198
- Custom filter: space-separated terms (all must match)
199
- </p>
200
- </div>
201
- );
202
- };
203
-
204
- export const WithDescriptions = () => {
205
- const [value, setValue] = useState('');
206
-
207
- return (
208
- <div className="max-w-xs space-y-2">
209
- <Label>Trading Pair</Label>
210
- <Combobox
211
- options={symbols}
212
- value={value}
213
- onValueChange={setValue}
214
- placeholder="Select trading pair..."
215
- searchPlaceholder="Search pairs..."
216
- emptyText="No pairs found."
217
- renderOption={(option) => (
218
- <div className="flex items-center justify-between w-full">
219
- <span className="font-medium">{option.label}</span>
220
- <span className="text-xs text-muted-foreground">{option.description}</span>
221
- </div>
222
- )}
223
- />
224
- </div>
225
- );
226
- };
@@ -1,261 +0,0 @@
1
- import { useState } from 'react';
2
- import { defineStory } from '@djangocfg/playground';
3
- import { CountrySelect, type TCountryCode } from './country-select';
4
- import { Label } from '../forms/label';
5
-
6
- export default defineStory({
7
- title: 'Core/CountrySelect',
8
- component: CountrySelect,
9
- description: 'Country selector with emoji flags. Supports dropdown and inline variants, single and multiple selection.',
10
- });
11
-
12
- // ============================================================================
13
- // Dropdown Variants
14
- // ============================================================================
15
-
16
- export const SingleDropdown = () => {
17
- const [value, setValue] = useState<string[]>([]);
18
-
19
- return (
20
- <div className="max-w-sm space-y-2">
21
- <Label>Select country</Label>
22
- <CountrySelect
23
- value={value}
24
- onChange={setValue}
25
- placeholder="Choose a country..."
26
- />
27
- {value.length > 0 && (
28
- <p className="text-sm text-muted-foreground">Selected: {value[0]}</p>
29
- )}
30
- </div>
31
- );
32
- };
33
-
34
- export const MultipleDropdown = () => {
35
- const [value, setValue] = useState<string[]>([]);
36
-
37
- return (
38
- <div className="max-w-sm space-y-2">
39
- <Label>Select countries</Label>
40
- <CountrySelect
41
- multiple
42
- value={value}
43
- onChange={setValue}
44
- placeholder="Choose countries..."
45
- />
46
- {value.length > 0 && (
47
- <p className="text-sm text-muted-foreground">Selected: {value.join(', ')}</p>
48
- )}
49
- </div>
50
- );
51
- };
52
-
53
- export const WithDefaultValue = () => {
54
- const [value, setValue] = useState<string[]>(['US', 'GB', 'DE']);
55
-
56
- return (
57
- <div className="max-w-sm space-y-2">
58
- <Label>Preselected countries</Label>
59
- <CountrySelect
60
- multiple
61
- value={value}
62
- onChange={setValue}
63
- />
64
- </div>
65
- );
66
- };
67
-
68
- export const WithNativeNames = () => {
69
- const [value, setValue] = useState<string[]>([]);
70
-
71
- return (
72
- <div className="max-w-sm space-y-2">
73
- <Label>With native names</Label>
74
- <CountrySelect
75
- multiple
76
- value={value}
77
- onChange={setValue}
78
- showNativeName
79
- />
80
- </div>
81
- );
82
- };
83
-
84
- // ============================================================================
85
- // Inline Variants
86
- // ============================================================================
87
-
88
- export const InlineSingle = () => {
89
- const [value, setValue] = useState<string[]>([]);
90
-
91
- return (
92
- <div className="max-w-sm space-y-2">
93
- <Label>Select your country</Label>
94
- <CountrySelect
95
- variant="inline"
96
- value={value}
97
- onChange={setValue}
98
- maxHeight={250}
99
- />
100
- {value.length > 0 && (
101
- <p className="text-sm text-muted-foreground">Selected: {value[0]}</p>
102
- )}
103
- </div>
104
- );
105
- };
106
-
107
- export const InlineMultiple = () => {
108
- const [value, setValue] = useState<string[]>([]);
109
-
110
- return (
111
- <div className="max-w-sm space-y-2">
112
- <Label>Select target markets</Label>
113
- <CountrySelect
114
- variant="inline"
115
- multiple
116
- value={value}
117
- onChange={setValue}
118
- maxHeight={300}
119
- />
120
- </div>
121
- );
122
- };
123
-
124
- export const InlineWithNativeNames = () => {
125
- const [value, setValue] = useState<string[]>(['KR', 'JP']);
126
-
127
- return (
128
- <div className="max-w-sm space-y-2">
129
- <Label>Countries with native names</Label>
130
- <CountrySelect
131
- variant="inline"
132
- multiple
133
- value={value}
134
- onChange={setValue}
135
- showNativeName
136
- maxHeight={300}
137
- />
138
- </div>
139
- );
140
- };
141
-
142
- export const InlineNoSearch = () => {
143
- const [value, setValue] = useState<string[]>([]);
144
-
145
- return (
146
- <div className="max-w-sm space-y-2">
147
- <Label>Without search</Label>
148
- <CountrySelect
149
- variant="inline"
150
- multiple
151
- value={value}
152
- onChange={setValue}
153
- showSearch={false}
154
- maxHeight={200}
155
- />
156
- </div>
157
- );
158
- };
159
-
160
- // ============================================================================
161
- // Filtered Countries
162
- // ============================================================================
163
-
164
- const CIS_COUNTRIES: TCountryCode[] = ['RU', 'KZ', 'UZ', 'KG', 'TJ', 'TM', 'AZ', 'AM', 'GE', 'BY', 'MD', 'UA'];
165
- const ASIAN_AUTO_MARKETS: TCountryCode[] = ['KR', 'JP', 'CN', 'TH', 'MY', 'ID', 'VN', 'PH'];
166
-
167
- export const FilteredCISCountries = () => {
168
- const [value, setValue] = useState<string[]>([]);
169
-
170
- return (
171
- <div className="max-w-sm space-y-2">
172
- <Label>CIS Countries only</Label>
173
- <CountrySelect
174
- variant="inline"
175
- multiple
176
- value={value}
177
- onChange={setValue}
178
- allowedCountries={CIS_COUNTRIES}
179
- maxHeight={300}
180
- />
181
- </div>
182
- );
183
- };
184
-
185
- export const FilteredAsianMarkets = () => {
186
- const [value, setValue] = useState<string[]>([]);
187
-
188
- return (
189
- <div className="max-w-sm space-y-2">
190
- <Label>Asian Auto Markets</Label>
191
- <CountrySelect
192
- multiple
193
- value={value}
194
- onChange={setValue}
195
- allowedCountries={ASIAN_AUTO_MARKETS}
196
- showNativeName
197
- />
198
- </div>
199
- );
200
- };
201
-
202
- export const ExcludeSanctioned = () => {
203
- const [value, setValue] = useState<string[]>([]);
204
- const excluded: TCountryCode[] = ['KP', 'IR', 'SY', 'CU'];
205
-
206
- return (
207
- <div className="max-w-sm space-y-2">
208
- <Label>All except sanctioned</Label>
209
- <CountrySelect
210
- multiple
211
- value={value}
212
- onChange={setValue}
213
- excludedCountries={excluded}
214
- />
215
- </div>
216
- );
217
- };
218
-
219
- // ============================================================================
220
- // States
221
- // ============================================================================
222
-
223
- export const Disabled = () => (
224
- <div className="max-w-sm space-y-4">
225
- <div className="space-y-2">
226
- <Label>Disabled dropdown</Label>
227
- <CountrySelect
228
- value={['US']}
229
- onChange={() => {}}
230
- disabled
231
- />
232
- </div>
233
- <div className="space-y-2">
234
- <Label>Disabled inline</Label>
235
- <CountrySelect
236
- variant="inline"
237
- multiple
238
- value={['US', 'GB']}
239
- onChange={() => {}}
240
- disabled
241
- maxHeight={150}
242
- />
243
- </div>
244
- </div>
245
- );
246
-
247
- export const MaxDisplayBadges = () => {
248
- const [value, setValue] = useState<string[]>(['US', 'GB', 'DE', 'FR', 'IT', 'ES']);
249
-
250
- return (
251
- <div className="max-w-sm space-y-2">
252
- <Label>Max 2 badges displayed</Label>
253
- <CountrySelect
254
- multiple
255
- value={value}
256
- onChange={setValue}
257
- maxDisplay={2}
258
- />
259
- </div>
260
- );
261
- };