Refine inline device alias editing
This commit is contained in:
@@ -200,6 +200,8 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
|
||||
const previousScrollTop = useRef(0);
|
||||
const movementAnimations = useRef(new Map());
|
||||
const previousTraffic = useRef(new Map());
|
||||
const aliasBaseline = useRef({ id: '', value: '' });
|
||||
const aliasWidth = useRef('1px');
|
||||
const copyTimer = useRef(null);
|
||||
const trafficDeltaTimer = useRef(null);
|
||||
const trafficOrder = useRef({ direction: sortDirection, ids: [] });
|
||||
@@ -346,10 +348,14 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAlias(event, device) {
|
||||
event.preventDefault();
|
||||
if (!await updateDevice(device, { alias })) return;
|
||||
setEditingId('');
|
||||
async function saveAlias(device) {
|
||||
const nextAlias = alias.trim();
|
||||
if (aliasBaseline.current.id === device.id && nextAlias === aliasBaseline.current.value.trim()) {
|
||||
setEditingId((current) => current === device.id ? '' : current);
|
||||
return;
|
||||
}
|
||||
if (!await updateDevice(device, { alias: nextAlias })) return;
|
||||
setEditingId((current) => current === device.id ? '' : current);
|
||||
}
|
||||
|
||||
async function updatePolicy(device, mode) {
|
||||
@@ -395,9 +401,12 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
|
||||
copyTimer.current = setTimeout(() => setCopyFeedback(null), COPY_FEEDBACK_MS);
|
||||
}
|
||||
|
||||
function startEditing(device) {
|
||||
function startEditing(device, width = 1) {
|
||||
const value = device.alias || device.hostname || '';
|
||||
aliasBaseline.current = { id: device.id, value };
|
||||
aliasWidth.current = `${width}px`;
|
||||
setEditingId(device.id);
|
||||
setAlias(device.alias || device.hostname || '');
|
||||
setAlias(value);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -560,56 +569,56 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
|
||||
</span>
|
||||
|
||||
<div className="client-device-main">
|
||||
{editing ? (
|
||||
<form className="client-device-alias" onSubmit={(event) => saveAlias(event, device)}>
|
||||
<h3 className={`client-device-name-heading${hasName ? '' : ' is-address-only'}${editing ? ' is-editing' : ''}`}>
|
||||
{editing ? (
|
||||
<input
|
||||
className="client-device-alias-input"
|
||||
value={alias}
|
||||
style={{ '--alias-width': aliasWidth.current }}
|
||||
maxLength="64"
|
||||
autoFocus
|
||||
aria-label="Название устройства"
|
||||
aria-busy={saving}
|
||||
disabled={saving}
|
||||
onChange={(event) => setAlias(event.target.value)}
|
||||
onBlur={() => saveAlias(device)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') event.currentTarget.blur();
|
||||
}}
|
||||
/>
|
||||
<button type="submit" aria-label="Сохранить название" disabled={saving}>✓</button>
|
||||
<button type="button" aria-label="Отменить изменение" disabled={saving} onClick={() => setEditingId('')}>×</button>
|
||||
</form>
|
||||
) : (
|
||||
<>
|
||||
<h3 className={`client-device-name-heading${hasName ? '' : ' is-address-only'}`}>
|
||||
{hasName && <button
|
||||
className="client-device-alias-trigger"
|
||||
type="button"
|
||||
aria-label={`Изменить название ${title}`}
|
||||
onClick={() => startEditing(device)}
|
||||
>{title}</button>}
|
||||
{hasName && device.ip && <span className="client-device-name-separator" aria-hidden="true">·</span>}
|
||||
{device.ip ? <button
|
||||
className={`client-device-ip${copied ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
|
||||
type="button"
|
||||
aria-label={`Скопировать IP ${device.ip} устройства ${title}`}
|
||||
onClick={() => copyDeviceIp(device)}
|
||||
>{device.ip}</button> : !hasName && <span>Неизвестное устройство</span>}
|
||||
</h3>
|
||||
{!hasName && <span className="client-device-edit-wrap client-tooltip-anchor">
|
||||
<button
|
||||
className={`client-device-edit${pencilAnimationId === device.id ? ' is-writing' : ''}`}
|
||||
type="button"
|
||||
aria-label={`Изменить название ${title}`}
|
||||
onPointerEnter={() => setPencilAnimationId(device.id)}
|
||||
onFocus={() => setPencilAnimationId(device.id)}
|
||||
onClick={() => startEditing(device)}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
onAnimationEnd={() => setPencilAnimationId((id) => id === device.id ? '' : id)}
|
||||
>
|
||||
<path d="m4 20 4.2-1 10.3-10.3a2 2 0 0 0-2.8-2.8L5.4 16.2 4 20ZM14.5 7.1l2.8 2.8" />
|
||||
</svg>
|
||||
</button>
|
||||
<Tooltip>Изменить название</Tooltip>
|
||||
</span>}
|
||||
</>
|
||||
)}
|
||||
) : hasName && <button
|
||||
className="client-device-alias-trigger"
|
||||
type="button"
|
||||
aria-label={`Изменить название ${title}`}
|
||||
onClick={(event) => startEditing(device, event.currentTarget.getBoundingClientRect().width)}
|
||||
>{title}</button>}
|
||||
{(hasName || (editing && Boolean(alias))) && device.ip && <span className="client-device-name-separator" aria-hidden="true">·</span>}
|
||||
{device.ip ? <button
|
||||
className={`client-device-ip${copied ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
|
||||
type="button"
|
||||
aria-label={`Скопировать IP ${device.ip} устройства ${title}`}
|
||||
onClick={() => copyDeviceIp(device)}
|
||||
>{device.ip}</button> : !hasName && !editing && <span>Неизвестное устройство</span>}
|
||||
</h3>
|
||||
{!hasName && !editing && <span className="client-device-edit-wrap client-tooltip-anchor">
|
||||
<button
|
||||
className={`client-device-edit${pencilAnimationId === device.id ? ' is-writing' : ''}`}
|
||||
type="button"
|
||||
aria-label={`Изменить название ${title}`}
|
||||
onPointerEnter={() => setPencilAnimationId(device.id)}
|
||||
onFocus={() => setPencilAnimationId(device.id)}
|
||||
onClick={() => startEditing(device)}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
onAnimationEnd={() => setPencilAnimationId((id) => id === device.id ? '' : id)}
|
||||
>
|
||||
<path d="m4 20 4.2-1 10.3-10.3a2 2 0 0 0-2.8-2.8L5.4 16.2 4 20ZM14.5 7.1l2.8 2.8" />
|
||||
</svg>
|
||||
</button>
|
||||
<Tooltip>Изменить название</Tooltip>
|
||||
</span>}
|
||||
<span className={`client-device-last-seen${online ? ' is-online' : ''}`} tabIndex="0">
|
||||
<time
|
||||
dateTime={device.lastSeenAt}
|
||||
|
||||
Reference in New Issue
Block a user