fix: Improve UX with searchable selects and higher modal z-index
✅ Issue 1: Modal Z-Index Fixed - Increased dialog z-index: z-[9999] → z-[99999] - Now properly appears above fullscreen mode (z-50) ✅ Issue 2: Searchable Select for Large Lists - Replaced Select with SearchableSelect for: - Countries (200+ options) - Currencies (100+ options) - Timezones (400+ options) - Users can now type to search instead of scrolling - Better UX for large datasets ✅ Issue 3: Input Type Support - Input component already supports type attribute - No changes needed (already working) ✅ Issue 4: Timezone Options Fixed - Replaced optgroup (not supported) with flat list - SearchableSelect handles filtering by continent name - Shows: 'Asia/Jakarta (UTC+7:00)' - Search includes continent, city, and offset 📊 Result: - ✅ Modal always on top - ✅ Easy search for countries/currencies/timezones - ✅ No more scrolling through hundreds of options - ✅ Better accessibility Addresses user feedback issues 1-4
This commit is contained in:
@@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-[9999] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-[99999] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -36,7 +36,7 @@ const DialogContent = React.forwardRef<
|
|||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-[9999] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-[99999] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { SettingsCard } from './components/SettingsCard';
|
|||||||
import { SettingsSection } from './components/SettingsSection';
|
import { SettingsSection } from './components/SettingsSection';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
|
import { SearchableSelect } from '@/components/ui/searchable-select';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
interface StoreSettings {
|
interface StoreSettings {
|
||||||
@@ -234,18 +235,16 @@ export default function StoreDetailsPage() {
|
|||||||
description="Used for shipping origin, invoices, and tax calculations"
|
description="Used for shipping origin, invoices, and tax calculations"
|
||||||
>
|
>
|
||||||
<SettingsSection label="Country/Region" required htmlFor="country">
|
<SettingsSection label="Country/Region" required htmlFor="country">
|
||||||
<Select value={settings.country} onValueChange={(v) => updateSetting('country', v)}>
|
<SearchableSelect
|
||||||
<SelectTrigger id="country">
|
value={settings.country}
|
||||||
<SelectValue />
|
onChange={(v) => updateSetting('country', v)}
|
||||||
</SelectTrigger>
|
options={countries.map((country: { code: string; name: string }) => ({
|
||||||
<SelectContent>
|
value: country.code,
|
||||||
{countries.map((country: { code: string; name: string }) => (
|
label: country.name,
|
||||||
<SelectItem key={country.code} value={country.code}>
|
searchText: country.name,
|
||||||
{country.name}
|
}))}
|
||||||
</SelectItem>
|
placeholder="Select country..."
|
||||||
))}
|
/>
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
<SettingsSection label="Street address" htmlFor="address">
|
<SettingsSection label="Street address" htmlFor="address">
|
||||||
@@ -293,18 +292,16 @@ export default function StoreDetailsPage() {
|
|||||||
description="How prices are displayed in your store"
|
description="How prices are displayed in your store"
|
||||||
>
|
>
|
||||||
<SettingsSection label="Currency" required htmlFor="currency">
|
<SettingsSection label="Currency" required htmlFor="currency">
|
||||||
<Select value={settings.currency} onValueChange={(v) => updateSetting('currency', v)}>
|
<SearchableSelect
|
||||||
<SelectTrigger id="currency">
|
value={settings.currency}
|
||||||
<SelectValue />
|
onChange={(v) => updateSetting('currency', v)}
|
||||||
</SelectTrigger>
|
options={currencies.map((currency: { code: string; name: string; symbol: string }) => ({
|
||||||
<SelectContent>
|
value: currency.code,
|
||||||
{currencies.map((currency: { code: string; name: string; symbol: string }) => (
|
label: `${currency.name} (${currency.symbol})`,
|
||||||
<SelectItem key={currency.code} value={currency.code}>
|
searchText: `${currency.code} ${currency.name} ${currency.symbol}`,
|
||||||
{currency.name} ({currency.symbol})
|
}))}
|
||||||
</SelectItem>
|
placeholder="Select currency..."
|
||||||
))}
|
/>
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
<SettingsSection label="Currency position" htmlFor="currencyPosition">
|
<SettingsSection label="Currency position" htmlFor="currencyPosition">
|
||||||
@@ -377,22 +374,18 @@ export default function StoreDetailsPage() {
|
|||||||
description="Timezone and measurement units"
|
description="Timezone and measurement units"
|
||||||
>
|
>
|
||||||
<SettingsSection label="Timezone" htmlFor="timezone">
|
<SettingsSection label="Timezone" htmlFor="timezone">
|
||||||
<Select value={settings.timezone} onValueChange={(v) => updateSetting('timezone', v)}>
|
<SearchableSelect
|
||||||
<SelectTrigger id="timezone">
|
value={settings.timezone}
|
||||||
<SelectValue />
|
onChange={(v) => updateSetting('timezone', v)}
|
||||||
</SelectTrigger>
|
options={Object.entries(timezones).flatMap(([continent, tzList]: [string, any]) =>
|
||||||
<SelectContent>
|
tzList.map((tz: { value: string; label: string; offset: string }) => ({
|
||||||
{Object.entries(timezones).map(([continent, tzList]: [string, any]) => (
|
value: tz.value,
|
||||||
<optgroup key={continent} label={continent}>
|
label: `${tz.label} (${tz.offset})`,
|
||||||
{tzList.map((tz: { value: string; label: string; offset: string }) => (
|
searchText: `${continent} ${tz.label} ${tz.offset}`,
|
||||||
<SelectItem key={tz.value} value={tz.value}>
|
}))
|
||||||
{tz.label} ({tz.offset})
|
)}
|
||||||
</SelectItem>
|
placeholder="Select timezone..."
|
||||||
))}
|
/>
|
||||||
</optgroup>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user