diff options
4 files changed, 86 insertions, 46 deletions
diff --git a/web/source/css/base.css b/web/source/css/base.css index 2122e5aae..615616725 100644 --- a/web/source/css/base.css +++ b/web/source/css/base.css @@ -493,9 +493,8 @@ section.with-form { gap: 0.4rem; & > input { - height: 100%; - width: 5%; - min-width: 1.2rem; + height: 1rem; + width: 1rem; align-self: center; } } diff --git a/web/source/settings/components/form/inputs.tsx b/web/source/settings/components/form/inputs.tsx index 1495e18f7..654f1a08f 100644 --- a/web/source/settings/components/form/inputs.tsx +++ b/web/source/settings/components/form/inputs.tsx @@ -122,10 +122,6 @@ export function FileInput({ label, field, ...props }: FileInputProps) { const ref = useRef<HTMLInputElement>(null); const { onChange, infoComponent } = field; const id = nanoid(); - const onClick = (e) => { - e.preventDefault(); - ref.current?.click(); - }; return ( <div className="form-field file"> @@ -133,11 +129,9 @@ export function FileInput({ label, field, ...props }: FileInputProps) { className="label-wrapper" htmlFor={id} tabIndex={0} - onClick={onClick} onKeyDown={(e) => { if (e.key === "Enter") { - e.preventDefault(); - onClick(e); + ref.current?.click(); } }} role="button" diff --git a/web/source/settings/views/moderation/domain-permissions/form.tsx b/web/source/settings/views/moderation/domain-permissions/form.tsx index 807648438..1c0eadba7 100644 --- a/web/source/settings/views/moderation/domain-permissions/form.tsx +++ b/web/source/settings/views/moderation/domain-permissions/form.tsx @@ -71,9 +71,6 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp }, [exportResult]); const importFileRef = useRef<HTMLInputElement>(null); - const importFileOnClick = () => { - importFileRef.current?.click(); - }; return ( <> @@ -109,11 +106,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp <label className={`button with-icon${form.permType.value === undefined || form.permType.value.length === 0 ? " disabled" : ""}`} tabIndex={0} - onClick={importFileOnClick} onKeyDown={(e) => { if (e.key === "Enter") { - e.preventDefault(); - importFileOnClick(); + importFileRef.current?.click(); } }} role="button" diff --git a/web/source/settings/views/user/posts/interaction-policy-settings/index.tsx b/web/source/settings/views/user/posts/interaction-policy-settings/index.tsx index 143cf0865..3c4d51422 100644 --- a/web/source/settings/views/user/posts/interaction-policy-settings/index.tsx +++ b/web/source/settings/views/user/posts/interaction-policy-settings/index.tsx @@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import React, { useCallback, useMemo } from "react"; +import React, { forwardRef, useCallback, useMemo, useRef } from "react"; import { useDefaultInteractionPoliciesQuery, useResetDefaultInteractionPoliciesMutation, @@ -191,57 +191,109 @@ function InteractionPoliciesForm({ defaultPolicies }: InteractionPoliciesFormPro // A tablist of tab buttons, one for each visibility. function PolicyPanelsTablist({ selectedVis }: { selectedVis: TextFormInputHook}) { + const publicRef = useRef<HTMLButtonElement>(null); + const unlistedRef = useRef<HTMLButtonElement>(null); + const privateRef = useRef<HTMLButtonElement>(null); + return ( <div className="tab-buttons" role="tablist"> <Tab - thisVisibility="public" label="Public" selectedVis={selectedVis} + prevVis="private" + thisVis="public" + nextVis="unlisted" + prevRef={privateRef} + thisRef={publicRef} + nextRef={unlistedRef} /> <Tab - thisVisibility="unlisted" label="Unlisted" selectedVis={selectedVis} + prevVis="public" + thisVis="unlisted" + nextVis="private" + prevRef={publicRef} + thisRef={unlistedRef} + nextRef={privateRef} /> <Tab - thisVisibility="private" label="Followers-only" selectedVis={selectedVis} + prevVis="unlisted" + thisVis="private" + nextVis="public" + prevRef={unlistedRef} + thisRef={privateRef} + nextRef={publicRef} /> </div> ); } interface TabProps { - thisVisibility: string; - label: string, - selectedVis: TextFormInputHook + label: string; + selectedVis: TextFormInputHook; + prevVis: string; + thisVis: string; + nextVis: string; + prevRef: React.RefObject<HTMLButtonElement>; + thisRef: React.RefObject<HTMLButtonElement>; + nextRef: React.RefObject<HTMLButtonElement>; } // One tab in a tablist, corresponding to the given thisVisibility. -function Tab({ thisVisibility, label, selectedVis }: TabProps) { - const selected = useMemo(() => { - return selectedVis.value === thisVisibility; - }, [selectedVis, thisVisibility]); - - return ( - <button - id={`tab-${thisVisibility}`} - title={label} - role="tab" - className={`tab-button ${selected && "active"}`} - onClick={(e) => { - e.preventDefault(); - selectedVis.setter(thisVisibility); - }} - aria-selected={selected} - aria-controls={`panel-${thisVisibility}`} - tabIndex={selected ? 0 : -1} - > - {label} - </button> - ); -} +const Tab = forwardRef( + function Tab({ + label, + selectedVis, + prevVis, + thisVis, + nextVis, + prevRef, + thisRef, + nextRef, + }: TabProps) { + const selected = useMemo(() => { + return selectedVis.value === thisVis; + }, [selectedVis, thisVis]); + + return ( + <button + id={`tab-${thisVis}`} + title={label} + role="tab" + ref={thisRef} + className={`tab-button ${selected && "active"}`} + onClick={(e) => { + // Allow tab to be clicked. + e.preventDefault(); + selectedVis.setter(thisVis); + }} + onKeyDown={(e) => { + // Allow cycling through + // tabs with arrow keys. + if (e.key === "ArrowLeft") { + // Select and set + // focus on previous tab. + selectedVis.setter(prevVis); + prevRef.current?.focus(); + } else if (e.key === "ArrowRight") { + // Select and set + // focus on next tab. + selectedVis.setter(nextVis); + nextRef.current?.focus(); + } + }} + aria-selected={selected} + aria-controls={`panel-${thisVis}`} + tabIndex={selected ? 0 : -1} + > + {label} + </button> + ); + } +); interface PolicyPanelProps { policyForm: PolicyForm; |
