mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-07 13:38:15 -08:00
QOL changes to client (#165)
This commit is contained in:
parent
e7ba34710c
commit
c59c6c3baa
4 changed files with 46 additions and 30 deletions
|
|
@ -130,30 +130,21 @@ h4 {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"] {
|
input[type="text"],
|
||||||
border: 1px solid var(--color-bg);
|
input[type="password"],
|
||||||
}
|
|
||||||
input[type="text"]:focus {
|
|
||||||
outline: none;
|
|
||||||
border: 1px solid var(--color-fg-tertiary);
|
|
||||||
}
|
|
||||||
textarea {
|
textarea {
|
||||||
border: 1px solid var(--color-bg);
|
border: 1px solid var(--color-bg);
|
||||||
}
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
input:focus,
|
||||||
|
button:focus,
|
||||||
|
a:focus,
|
||||||
|
select:focus,
|
||||||
textarea:focus {
|
textarea:focus {
|
||||||
outline: none;
|
border-color: transparent;
|
||||||
border: 1px solid var(--color-fg-tertiary);
|
outline: 2px solid var(--color-fg-tertiary);
|
||||||
}
|
|
||||||
input[type="password"] {
|
|
||||||
border: 1px solid var(--color-bg);
|
|
||||||
}
|
|
||||||
input[type="password"]:focus {
|
|
||||||
outline: none;
|
|
||||||
border: 1px solid var(--color-fg-tertiary);
|
|
||||||
}
|
|
||||||
input[type="checkbox"]:focus {
|
|
||||||
outline: none;
|
|
||||||
border: 1px solid var(--color-fg-tertiary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default function DeleteModal({ open, setOpen, title, id, type }: Props) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
deleteItem(type.toLowerCase(), id).then((r) => {
|
deleteItem(type.toLowerCase(), id).then((r) => {
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
navigate("/");
|
navigate(-1);
|
||||||
} else {
|
} else {
|
||||||
console.log(r);
|
console.log(r);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MergeModal(props: Props) {
|
export default function MergeModal(props: Props) {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState(props.currentTitle);
|
||||||
const [data, setData] = useState<SearchResponse>();
|
const [data, setData] = useState<SearchResponse>();
|
||||||
const [debouncedQuery, setDebouncedQuery] = useState(query);
|
const [debouncedQuery, setDebouncedQuery] = useState(query);
|
||||||
const [mergeTarget, setMergeTarget] = useState<{ title: string; id: number }>(
|
const [mergeTarget, setMergeTarget] = useState<{ title: string; id: number }>(
|
||||||
|
|
@ -101,11 +101,12 @@ export default function MergeModal(props: Props) {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
autoFocus
|
autoFocus
|
||||||
|
defaultValue={props.currentTitle}
|
||||||
// i find my stupid a(n) logic to be a little silly so im leaving it in even if its not optimal
|
// i find my stupid a(n) logic to be a little silly so im leaving it in even if its not optimal
|
||||||
placeholder={`Search for a${
|
placeholder={`Search for a${props.type.toLowerCase()[0] === "a" ? "n" : ""
|
||||||
props.type.toLowerCase()[0] === "a" ? "n" : ""
|
} ${props.type.toLowerCase()} to be merged into the current ${props.type.toLowerCase()}`}
|
||||||
} ${props.type.toLowerCase()} to be merged into the current ${props.type.toLowerCase()}`}
|
|
||||||
className="w-full mx-auto fg bg rounded p-2"
|
className="w-full mx-auto fg bg rounded p-2"
|
||||||
|
onFocus={(e) => { setQuery(e.target.value); e.target.select()}}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<SearchResults selectorMode data={data} onSelect={toggleSelect} />
|
<SearchResults selectorMode data={data} onSelect={toggleSelect} />
|
||||||
|
|
@ -128,7 +129,7 @@ export default function MergeModal(props: Props) {
|
||||||
>
|
>
|
||||||
Merge Items
|
Merge Items
|
||||||
</button>
|
</button>
|
||||||
<div className="flex gap-2 mt-3">
|
<div className="flex items-center gap-2 mt-3">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="reverse-merge-order"
|
name="reverse-merge-order"
|
||||||
|
|
@ -139,7 +140,7 @@ export default function MergeModal(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
{(props.type.toLowerCase() === "album" ||
|
{(props.type.toLowerCase() === "album" ||
|
||||||
props.type.toLowerCase() === "artist") && (
|
props.type.toLowerCase() === "artist") && (
|
||||||
<div className="flex gap-2 mt-3">
|
<div className="flex items-center gap-2 mt-3">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="replace-image"
|
name="replace-image"
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,34 @@ export function Modal({
|
||||||
}
|
}
|
||||||
}, [isOpen, shouldRender]);
|
}, [isOpen, shouldRender]);
|
||||||
|
|
||||||
// Close on Escape key
|
// Handle keyboard events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') onClose();
|
// Close on Escape key
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
onClose()
|
||||||
|
// Trap tab navigation to the modal
|
||||||
|
} else if (e.key === 'Tab') {
|
||||||
|
if (modalRef.current) {
|
||||||
|
const focusableEls = modalRef.current.querySelectorAll<HTMLElement>(
|
||||||
|
'button:not(:disabled), [href], input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
const firstEl = focusableEls[0];
|
||||||
|
const lastEl = focusableEls[focusableEls.length - 1];
|
||||||
|
const activeEl = document.activeElement
|
||||||
|
|
||||||
|
if (e.shiftKey && activeEl === firstEl) {
|
||||||
|
e.preventDefault();
|
||||||
|
lastEl.focus();
|
||||||
|
} else if (!e.shiftKey && activeEl === lastEl) {
|
||||||
|
e.preventDefault();
|
||||||
|
firstEl.focus();
|
||||||
|
} else if (!Array.from(focusableEls).find(node => node.isEqualNode(activeEl))) {
|
||||||
|
e.preventDefault();
|
||||||
|
firstEl.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
if (isOpen) document.addEventListener('keydown', handleKeyDown);
|
if (isOpen) document.addEventListener('keydown', handleKeyDown);
|
||||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
|
@ -70,13 +94,13 @@ export function Modal({
|
||||||
}`}
|
}`}
|
||||||
style={{ maxWidth: maxW ?? 600, height: h ?? '' }}
|
style={{ maxWidth: maxW ?? 600, height: h ?? '' }}
|
||||||
>
|
>
|
||||||
|
{children}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="absolute top-2 right-2 color-fg-tertiary hover:cursor-pointer"
|
className="absolute top-2 right-2 color-fg-tertiary hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
🞪
|
🞪
|
||||||
</button>
|
</button>
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.body
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue