100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useModalAmbientDuck } from '../../context/AmbientMusicContext';
|
|
import { FLEET_GROUP_COLORS, normalizeGroupColor } from '../../help/fleetGroups';
|
|
import './CreateGroupModal.css';
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
agentCount: number;
|
|
onClose: () => void;
|
|
onCreate: (name: string, color: string) => void;
|
|
}
|
|
|
|
export default function CreateGroupModal({ open, agentCount, onClose, onCreate }: Props) {
|
|
useModalAmbientDuck(open);
|
|
const [name, setName] = useState('');
|
|
const [color, setColor] = useState<string>(FLEET_GROUP_COLORS[0]);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
setName('');
|
|
setColor(FLEET_GROUP_COLORS[groupsColorIndex(agentCount) % FLEET_GROUP_COLORS.length]);
|
|
}
|
|
}, [open, agentCount]);
|
|
|
|
if (!open || agentCount < 1) return null;
|
|
|
|
const submit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
const trimmed = name.trim();
|
|
if (!trimmed) return;
|
|
onCreate(trimmed, normalizeGroupColor(color));
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<div className="fleet-group-modal-backdrop" role="presentation" onClick={onClose}>
|
|
<div
|
|
className="fleet-group-modal card"
|
|
role="dialog"
|
|
aria-labelledby="fleet-group-modal-title"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<h2 id="fleet-group-modal-title" className="font-display">Create group</h2>
|
|
<p className="form-hint">
|
|
Saves {agentCount} selected machine{agentCount === 1 ? '' : 's'} — usable in Crucible for bulk commands.
|
|
</p>
|
|
<form onSubmit={submit}>
|
|
<label className="label" htmlFor="fleet-group-name">Group name</label>
|
|
<input
|
|
id="fleet-group-name"
|
|
className="input"
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
placeholder="e.g. Living room PCs"
|
|
autoFocus
|
|
maxLength={64}
|
|
/>
|
|
|
|
<p className="label" style={{ marginTop: '1rem' }}>Group color</p>
|
|
<div className="fleet-group-color-grid">
|
|
{FLEET_GROUP_COLORS.map((c) => (
|
|
<button
|
|
key={c}
|
|
type="button"
|
|
className={`fleet-group-swatch ${color === c ? 'selected' : ''}`}
|
|
style={{ background: c }}
|
|
title={c}
|
|
aria-label={`Color ${c}`}
|
|
onClick={() => setColor(c)}
|
|
/>
|
|
))}
|
|
</div>
|
|
<div className="fleet-group-custom-color">
|
|
<input
|
|
type="color"
|
|
value={normalizeGroupColor(color)}
|
|
onChange={(e) => setColor(e.target.value)}
|
|
aria-label="Custom color"
|
|
/>
|
|
<span className="font-tech">{normalizeGroupColor(color)}</span>
|
|
</div>
|
|
|
|
<div className="fleet-group-modal-actions">
|
|
<button type="button" className="btn btn-outline" onClick={onClose}>
|
|
Cancel
|
|
</button>
|
|
<button type="submit" className="btn btn-primary" disabled={!name.trim()}>
|
|
Create group
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function groupsColorIndex(n: number): number {
|
|
return Math.abs(n) % FLEET_GROUP_COLORS.length;
|
|
}
|