feat(a11y): add focus management to UserSelectionDialog and ExecutionReportDialog
UserSelectionDialog: - Add ARIA attributes (role, aria-modal, aria-labelledby) - Integrate useDialogFocus hook with FocusLock - Add Escape key handling (was missing) - Initial focus on first user card ExecutionReportDialog: - Add ARIA attributes with dynamic aria-labelledby - Add aria-live for progress updates - Integrate useDialogFocus hook with FocusLock - Escape key only closes when not executing
This commit is contained in:
@@ -7,8 +7,10 @@
|
|||||||
* - Error list (if any)
|
* - Error list (if any)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react'
|
import React, { useRef } from 'react'
|
||||||
import { CheckCircle, XCircle, SkipForward, Package, Loader2 } from 'lucide-react'
|
import { CheckCircle, XCircle, SkipForward, Package, Loader2 } from 'lucide-react'
|
||||||
|
import FocusLock from 'react-focus-lock'
|
||||||
|
import { useDialogFocus } from '../hooks/useDialogFocus'
|
||||||
|
|
||||||
interface CleanerProgress {
|
interface CleanerProgress {
|
||||||
message: string
|
message: string
|
||||||
@@ -46,6 +48,34 @@ export const ExecutionReportDialog: React.FC<ExecutionReportDialogProps> = ({
|
|||||||
progress = null,
|
progress = null,
|
||||||
startTime = null
|
startTime = null
|
||||||
}) => {
|
}) => {
|
||||||
|
const dialogRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
// Setup focus management with custom escape key handling
|
||||||
|
const { focusLockProps } = useDialogFocus({
|
||||||
|
isOpen,
|
||||||
|
dialogRef,
|
||||||
|
onClose
|
||||||
|
})
|
||||||
|
|
||||||
|
// Custom escape key handling - only close when NOT executing
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isOpen) return
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
// Only allow escape to close when not executing
|
||||||
|
if ((event.key === 'Escape' || event.keyCode === 27) && !isExecuting) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}
|
||||||
|
}, [isOpen, isExecuting, onClose])
|
||||||
|
|
||||||
if (!isOpen) return null
|
if (!isOpen) return null
|
||||||
|
|
||||||
const hasErrors = errors.length > 0
|
const hasErrors = errors.length > 0
|
||||||
@@ -91,188 +121,202 @@ export const ExecutionReportDialog: React.FC<ExecutionReportDialogProps> = ({
|
|||||||
}, [showProgress, startTime, progress, now])
|
}, [showProgress, startTime, progress, now])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="execution-report-overlay" onClick={showProgress ? undefined : onClose}>
|
<FocusLock {...focusLockProps}>
|
||||||
<div
|
<div
|
||||||
className="execution-report-dialog"
|
className="execution-report-overlay"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={showProgress ? undefined : onClose}
|
||||||
style={{ width: showProgress ? '560px' : '480px' }}
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby={
|
||||||
|
showProgress ? 'execution-dialog-progress-title' : 'execution-dialog-report-title'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{showProgress ? (
|
<div
|
||||||
// Progress View
|
ref={dialogRef}
|
||||||
<div className="progress-header">
|
className="execution-report-dialog"
|
||||||
<div className="progress-icon-wrapper">
|
onClick={(e) => e.stopPropagation()}
|
||||||
<Loader2 className="progress-icon spinning" />
|
style={{ width: showProgress ? '560px' : '480px' }}
|
||||||
</div>
|
>
|
||||||
<h2 className="progress-title">正在执行清理...</h2>
|
|
||||||
<p className="progress-subtitle">{progress?.message || '处理中...'}</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
// Result View
|
|
||||||
<div className="report-header">
|
|
||||||
<div className="report-icon-wrapper">
|
|
||||||
{dryRun ? (
|
|
||||||
<Package className="report-icon preview" />
|
|
||||||
) : hasErrors ? (
|
|
||||||
<XCircle className="report-icon error" />
|
|
||||||
) : (
|
|
||||||
<CheckCircle className="report-icon success" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<h2 className="report-title">
|
|
||||||
{dryRun ? '预览执行报告' : hasErrors ? '执行完成 (有错误)' : '执行完成'}
|
|
||||||
</h2>
|
|
||||||
<p className="report-subtitle">
|
|
||||||
{dryRun
|
|
||||||
? '预览模式 - 未实际删除数据'
|
|
||||||
: hasErrors
|
|
||||||
? '部分操作未能完成,请查看下方错误信息'
|
|
||||||
: '所有操作已成功完成'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="report-body">
|
|
||||||
{showProgress ? (
|
{showProgress ? (
|
||||||
// Progress View Content
|
// Progress View
|
||||||
<div className="progress-content">
|
<div className="progress-header">
|
||||||
<div className="progress-bar-container">
|
<div className="progress-icon-wrapper">
|
||||||
<div
|
<Loader2 className="progress-icon spinning" />
|
||||||
className="progress-bar-fill"
|
|
||||||
style={{ width: `${progress?.progress || 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<h2 id="execution-dialog-progress-title" className="progress-title">
|
||||||
<div className="progress-stats">
|
正在执行清理...
|
||||||
<div className="progress-stat-item">
|
</h2>
|
||||||
<span className="stat-label">订单进度</span>
|
<p className="progress-subtitle" aria-live="polite" aria-atomic="true">
|
||||||
<span className="stat-value">
|
{progress?.message || '处理中...'}
|
||||||
{Math.min(progress!.currentOrderIndex, progress!.totalOrders)} /{' '}
|
</p>
|
||||||
{progress!.totalOrders}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{progress!.totalMaterialsInOrder > 0 && (
|
|
||||||
<div className="progress-stat-item">
|
|
||||||
<span className="stat-label">物料进度</span>
|
|
||||||
<span className="stat-value">
|
|
||||||
{progress!.currentMaterialIndex} / {progress!.totalMaterialsInOrder}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="progress-stat-item">
|
|
||||||
<span className="stat-label">总进度</span>
|
|
||||||
<span className="stat-value">{Math.round(progress?.progress || 0)}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{progress?.currentOrderNumber && (
|
|
||||||
<div className="current-order-info">
|
|
||||||
<Package size={14} className="order-icon" />
|
|
||||||
<span className="order-label">当前订单:</span>
|
|
||||||
<span className="order-number">{progress.currentOrderNumber}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{estimatedTime && (
|
|
||||||
<div className="estimated-time-info">
|
|
||||||
<div className="estimated-time-item">
|
|
||||||
<span className="time-label">预估还要</span>
|
|
||||||
<span className="time-value">{estimatedTime.remainingMinutes} 分钟</span>
|
|
||||||
</div>
|
|
||||||
<div className="estimated-time-item">
|
|
||||||
<span className="time-label">将会在</span>
|
|
||||||
<span className="time-value">{estimatedTime.formattedTime}</span>
|
|
||||||
<span className="time-label">执行完毕</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// Result View Content
|
// Result View
|
||||||
<>
|
<div className="report-header">
|
||||||
<div className="stats-grid">
|
<div className="report-icon-wrapper">
|
||||||
<div className="stat-card">
|
{dryRun ? (
|
||||||
<div className="stat-icon orders">
|
<Package className="report-icon preview" />
|
||||||
<Package size={20} />
|
) : hasErrors ? (
|
||||||
|
<XCircle className="report-icon error" />
|
||||||
|
) : (
|
||||||
|
<CheckCircle className="report-icon success" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h2 id="execution-dialog-report-title" className="report-title">
|
||||||
|
{dryRun ? '预览执行报告' : hasErrors ? '执行完成 (有错误)' : '执行完成'}
|
||||||
|
</h2>
|
||||||
|
<p className="report-subtitle">
|
||||||
|
{dryRun
|
||||||
|
? '预览模式 - 未实际删除数据'
|
||||||
|
: hasErrors
|
||||||
|
? '部分操作未能完成,请查看下方错误信息'
|
||||||
|
: '所有操作已成功完成'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="report-body">
|
||||||
|
{showProgress ? (
|
||||||
|
// Progress View Content
|
||||||
|
<div className="progress-content">
|
||||||
|
<div className="progress-bar-container">
|
||||||
|
<div
|
||||||
|
className="progress-bar-fill"
|
||||||
|
style={{ width: `${progress?.progress || 0}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="progress-stats">
|
||||||
|
<div className="progress-stat-item">
|
||||||
|
<span className="stat-label">订单进度</span>
|
||||||
|
<span className="stat-value">
|
||||||
|
{Math.min(progress!.currentOrderIndex, progress!.totalOrders)} /{' '}
|
||||||
|
{progress!.totalOrders}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-content">
|
{progress!.totalMaterialsInOrder > 0 && (
|
||||||
<div className="stat-label">处理订单</div>
|
<div className="progress-stat-item">
|
||||||
<div className="stat-value">{ordersProcessed}</div>
|
<span className="stat-label">物料进度</span>
|
||||||
|
<span className="stat-value">
|
||||||
|
{progress!.currentMaterialIndex} / {progress!.totalMaterialsInOrder}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="progress-stat-item">
|
||||||
|
<span className="stat-label">总进度</span>
|
||||||
|
<span className="stat-value">{Math.round(progress?.progress || 0)}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
{progress?.currentOrderNumber && (
|
||||||
<div className="stat-icon deleted">
|
<div className="current-order-info">
|
||||||
<CheckCircle size={20} />
|
<Package size={14} className="order-icon" />
|
||||||
|
<span className="order-label">当前订单:</span>
|
||||||
|
<span className="order-number">{progress.currentOrderNumber}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-content">
|
)}
|
||||||
<div className="stat-label">{dryRun ? '拟删除物料' : '删除物料'}</div>
|
|
||||||
<div className="stat-value">{materialsDeleted}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="stat-card">
|
{estimatedTime && (
|
||||||
<div className="stat-icon skipped">
|
<div className="estimated-time-info">
|
||||||
<SkipForward size={20} />
|
<div className="estimated-time-item">
|
||||||
|
<span className="time-label">预估还要</span>
|
||||||
|
<span className="time-value">{estimatedTime.remainingMinutes} 分钟</span>
|
||||||
|
</div>
|
||||||
|
<div className="estimated-time-item">
|
||||||
|
<span className="time-label">将会在</span>
|
||||||
|
<span className="time-value">{estimatedTime.formattedTime}</span>
|
||||||
|
<span className="time-label">执行完毕</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-content">
|
)}
|
||||||
<div className="stat-label">跳过物料</div>
|
</div>
|
||||||
<div className="stat-value">{materialsSkipped}</div>
|
) : (
|
||||||
|
// Result View Content
|
||||||
|
<>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-icon orders">
|
||||||
|
<Package size={20} />
|
||||||
|
</div>
|
||||||
|
<div className="stat-content">
|
||||||
|
<div className="stat-label">处理订单</div>
|
||||||
|
<div className="stat-value">{ordersProcessed}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-icon deleted">
|
||||||
|
<CheckCircle size={20} />
|
||||||
|
</div>
|
||||||
|
<div className="stat-content">
|
||||||
|
<div className="stat-label">{dryRun ? '拟删除物料' : '删除物料'}</div>
|
||||||
|
<div className="stat-value">{materialsDeleted}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-icon skipped">
|
||||||
|
<SkipForward size={20} />
|
||||||
|
</div>
|
||||||
|
<div className="stat-content">
|
||||||
|
<div className="stat-label">跳过物料</div>
|
||||||
|
<div className="stat-value">{materialsSkipped}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{hasErrors && (
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-icon errors">
|
||||||
|
<XCircle size={20} />
|
||||||
|
</div>
|
||||||
|
<div className="stat-content">
|
||||||
|
<div className="stat-label">错误数量</div>
|
||||||
|
<div className="stat-value error">{errors.length}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasErrors && (
|
{hasErrors && (
|
||||||
<div className="stat-card">
|
<div className="errors-section">
|
||||||
<div className="stat-icon errors">
|
<div className="errors-title">错误详情</div>
|
||||||
<XCircle size={20} />
|
<div className="errors-list">
|
||||||
</div>
|
{errors.map((error, index) => (
|
||||||
<div className="stat-content">
|
<div key={index} className="error-item">
|
||||||
<div className="stat-label">错误数量</div>
|
<XCircle size={14} className="error-icon" />
|
||||||
<div className="stat-value error">{errors.length}</div>
|
<span className="error-text">{error}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{hasErrors && (
|
{!hasErrors && !dryRun && (
|
||||||
<div className="errors-section">
|
<div className="success-message">
|
||||||
<div className="errors-title">错误详情</div>
|
<CheckCircle size={16} className="success-icon" />
|
||||||
<div className="errors-list">
|
<span>操作已成功完成,数据已同步到 ERP 系统</span>
|
||||||
{errors.map((error, index) => (
|
|
||||||
<div key={index} className="error-item">
|
|
||||||
<XCircle size={14} className="error-icon" />
|
|
||||||
<span className="error-text">{error}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
{!hasErrors && !dryRun && (
|
{!hasErrors && dryRun && (
|
||||||
<div className="success-message">
|
<div className="preview-message">
|
||||||
<CheckCircle size={16} className="success-icon" />
|
<Package size={16} className="preview-icon" />
|
||||||
<span>操作已成功完成,数据已同步到 ERP 系统</span>
|
<span>预览模式结束,数据未实际修改。确认无误后可正式执行。</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{!hasErrors && dryRun && (
|
<div className="report-footer">
|
||||||
<div className="preview-message">
|
{!showProgress && (
|
||||||
<Package size={16} className="preview-icon" />
|
<button className="btn-report-close" onClick={onClose}>
|
||||||
<span>预览模式结束,数据未实际修改。确认无误后可正式执行。</span>
|
关闭
|
||||||
</div>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="report-footer">
|
<style>{`
|
||||||
{!showProgress && (
|
|
||||||
<button className="btn-report-close" onClick={onClose}>
|
|
||||||
关闭
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>{`
|
|
||||||
.execution-report-overlay {
|
.execution-report-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -698,8 +742,9 @@ export const ExecutionReportDialog: React.FC<ExecutionReportDialogProps> = ({
|
|||||||
background: #096dd9;
|
background: #096dd9;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FocusLock>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
* - Return selected user info
|
* - Return selected user info
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useRef } from 'react'
|
||||||
|
import FocusLock from 'react-focus-lock'
|
||||||
|
import { useDialogFocus } from '../hooks/useDialogFocus'
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
id: number
|
id: number
|
||||||
@@ -32,6 +34,14 @@ export const UserSelectionDialog: React.FC<UserSelectionDialogProps> = ({
|
|||||||
onCancel
|
onCancel
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedUserId, setSelectedUserId] = useState<number | null>(null)
|
const [selectedUserId, setSelectedUserId] = useState<number | null>(null)
|
||||||
|
const dialogRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const { focusLockProps } = useDialogFocus({
|
||||||
|
isOpen,
|
||||||
|
dialogRef,
|
||||||
|
onClose: onCancel,
|
||||||
|
initialFocusSelector: users.length > 0 ? '.user-item:first-child' : undefined
|
||||||
|
})
|
||||||
|
|
||||||
// Reset selection when dialog opens
|
// Reset selection when dialog opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -58,60 +68,68 @@ export const UserSelectionDialog: React.FC<UserSelectionDialogProps> = ({
|
|||||||
if (!isOpen) return null
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="user-selection-overlay">
|
<FocusLock {...focusLockProps}>
|
||||||
<div className="user-selection-dialog">
|
<div
|
||||||
<div className="user-selection-header">
|
className="user-selection-overlay"
|
||||||
<h2 className="user-selection-title">选择用户</h2>
|
role="dialog"
|
||||||
<p className="user-selection-hint">当前登录:{currentUsername}</p>
|
aria-modal="true"
|
||||||
</div>
|
aria-labelledby="user-selection-dialog-title"
|
||||||
|
>
|
||||||
|
<div className="user-selection-dialog" ref={dialogRef}>
|
||||||
|
<div className="user-selection-header">
|
||||||
|
<h2 className="user-selection-title" id="user-selection-dialog-title">
|
||||||
|
选择用户
|
||||||
|
</h2>
|
||||||
|
<p className="user-selection-hint">当前登录:{currentUsername}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="user-selection-body">
|
<div className="user-selection-body">
|
||||||
<div className="user-list">
|
<div className="user-list">
|
||||||
{users.map((user) => (
|
{users.map((user) => (
|
||||||
<div
|
<div
|
||||||
key={user.id}
|
key={user.id}
|
||||||
className={`user-item ${selectedUserId === user.id ? 'selected' : ''}`}
|
className={`user-item ${selectedUserId === user.id ? 'selected' : ''}`}
|
||||||
onClick={() => setSelectedUserId(user.id)}
|
onClick={() => setSelectedUserId(user.id)}
|
||||||
onDoubleClick={() => handleDoubleClick(user)}
|
onDoubleClick={() => handleDoubleClick(user)}
|
||||||
>
|
>
|
||||||
<div className="user-item-content">
|
<div className="user-item-content">
|
||||||
<div className="user-item-row">
|
|
||||||
<span className="user-name">{user.username}</span>
|
|
||||||
<span className={`user-type user-type-${user.userType.toLowerCase()}`}>
|
|
||||||
{user.userType}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{user.createTime && (
|
|
||||||
<div className="user-item-row">
|
<div className="user-item-row">
|
||||||
<span className="user-create-time">
|
<span className="user-name">{user.username}</span>
|
||||||
创建于:{new Date(user.createTime).toLocaleString('zh-CN')}
|
<span className={`user-type user-type-${user.userType.toLowerCase()}`}>
|
||||||
|
{user.userType}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{user.createTime && (
|
||||||
|
<div className="user-item-row">
|
||||||
|
<span className="user-create-time">
|
||||||
|
创建于:{new Date(user.createTime).toLocaleString('zh-CN')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="user-selection-footer">
|
<div className="user-selection-footer">
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
disabled={selectedUserId === null}
|
disabled={selectedUserId === null}
|
||||||
>
|
>
|
||||||
确认
|
确认
|
||||||
</button>
|
</button>
|
||||||
<button className="btn btn-secondary" onClick={onCancel}>
|
<button className="btn btn-secondary" onClick={onCancel}>
|
||||||
取消
|
取消
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="user-selection-hint-footer">双击用户可直接选择</div>
|
<div className="user-selection-hint-footer">双击用户可直接选择</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>{`
|
<style>{`
|
||||||
.user-selection-overlay {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -278,7 +296,7 @@ export const UserSelectionDialog: React.FC<UserSelectionDialogProps> = ({
|
|||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</FocusLock>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user