feat: enhance dropdown components by integrating useClickOutside hook for improved user experience and accessibility in NewWorkshopDropdown and WorkshopTabs
This commit is contained in:
@@ -7,6 +7,7 @@ import { Input } from '@/components/ui/Input';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Avatar } from '@/components/ui/Avatar';
|
||||
import { Select } from '@/components/ui/Select';
|
||||
import { getTeamMembersForShare, type TeamWithMembers } from '@/lib/share-utils';
|
||||
import type { ShareRole } from '@prisma/client';
|
||||
|
||||
@@ -41,8 +42,10 @@ interface ShareModalProps {
|
||||
helpText?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SELECT_STYLE =
|
||||
'appearance-none rounded-lg border border-border bg-card px-3 py-2.5 pr-10 text-sm text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20';
|
||||
const ROLE_OPTIONS = [
|
||||
{ value: 'EDITOR', label: 'Éditeur' },
|
||||
{ value: 'VIEWER', label: 'Lecteur' },
|
||||
] as const;
|
||||
|
||||
export function ShareModal({
|
||||
isOpen,
|
||||
@@ -151,10 +154,12 @@ export function ShareModal({
|
||||
className="flex-1"
|
||||
required
|
||||
/>
|
||||
<select value={role} onChange={(e) => setRole(e.target.value as ShareRole)} className={SELECT_STYLE}>
|
||||
<option value="EDITOR">Éditeur</option>
|
||||
<option value="VIEWER">Lecteur</option>
|
||||
</select>
|
||||
<Select
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value as ShareRole)}
|
||||
options={[...ROLE_OPTIONS]}
|
||||
wrapperClassName="w-auto shrink-0 min-w-[7rem]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -171,30 +176,25 @@ export function ShareModal({
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<select
|
||||
value={selectedMemberId}
|
||||
onChange={(e) => setSelectedMemberId(e.target.value)}
|
||||
className={`w-full ${SELECT_STYLE}`}
|
||||
required
|
||||
>
|
||||
<option value="">Sélectionner un membre</option>
|
||||
{teamMembers.map((m) => (
|
||||
<option key={m.id} value={m.id}>
|
||||
{m.name || m.email} {m.name && `(${m.email})`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||
<svg className="h-4 w-4 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<select value={role} onChange={(e) => setRole(e.target.value as ShareRole)} className={SELECT_STYLE}>
|
||||
<option value="EDITOR">Éditeur</option>
|
||||
<option value="VIEWER">Lecteur</option>
|
||||
</select>
|
||||
<Select
|
||||
value={selectedMemberId}
|
||||
onChange={(e) => setSelectedMemberId(e.target.value)}
|
||||
options={[
|
||||
{ value: '', label: 'Sélectionner un membre', disabled: true },
|
||||
...teamMembers.map((m) => ({
|
||||
value: m.id,
|
||||
label: m.name ? `${m.name} (${m.email})` : m.email,
|
||||
})),
|
||||
]}
|
||||
wrapperClassName="flex-1 min-w-0"
|
||||
required
|
||||
/>
|
||||
<Select
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value as ShareRole)}
|
||||
options={[...ROLE_OPTIONS]}
|
||||
wrapperClassName="w-auto shrink-0 min-w-[7rem]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -211,39 +211,27 @@ export function ShareModal({
|
||||
.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={teamId}
|
||||
onChange={(e) => setTeamId(e.target.value)}
|
||||
className={`w-full ${SELECT_STYLE}`}
|
||||
required
|
||||
>
|
||||
<option value="">Sélectionner une équipe</option>
|
||||
{userTeams.map((team) => (
|
||||
<option key={team.id} value={team.id}>
|
||||
{team.name} {team.userRole === 'ADMIN' && '(Admin)'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||
<svg className="h-4 w-4 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select value={role} onChange={(e) => setRole(e.target.value as ShareRole)} className={`w-full ${SELECT_STYLE}`}>
|
||||
<option value="EDITOR">Éditeur</option>
|
||||
<option value="VIEWER">Lecteur</option>
|
||||
</select>
|
||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||
<svg className="h-4 w-4 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={teamId}
|
||||
onChange={(e) => setTeamId(e.target.value)}
|
||||
options={[
|
||||
{ value: '', label: 'Sélectionner une équipe', disabled: true },
|
||||
...userTeams.map((team) => ({
|
||||
value: team.id,
|
||||
label: `${team.name}${team.userRole === 'ADMIN' ? ' (Admin)' : ''}`,
|
||||
})),
|
||||
]}
|
||||
wrapperClassName="flex-1 min-w-0"
|
||||
required
|
||||
/>
|
||||
<Select
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value as ShareRole)}
|
||||
options={[...ROLE_OPTIONS]}
|
||||
wrapperClassName="w-auto shrink-0 min-w-[7rem]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user