From 0a4215c57ba18e28775a3845ea16ac77c43ae78b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 10 Apr 2022 14:52:30 -0700 Subject: [PATCH] Handle toggling label state --- .../templates/article/ArticleActionsMenu.tsx | 1 + .../templates/article/EditLabelsModal.tsx | 358 ++++++++++-------- 2 files changed, 194 insertions(+), 165 deletions(-) diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx index a97a4370d..42b8651a5 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -1,5 +1,6 @@ import { Separator } from "@radix-ui/react-separator" import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react" +import { useRef } from "react" import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences" import { Button } from "../../elements/Button" import { Dropdown } from "../../elements/DropdownElements" diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx index c04f7321f..e9b2172e0 100644 --- a/packages/web/components/templates/article/EditLabelsModal.tsx +++ b/packages/web/components/templates/article/EditLabelsModal.tsx @@ -9,7 +9,7 @@ import { StyledText } from '../../elements/StyledText' import { CrossIcon } from '../../elements/images/CrossIcon' import { styled, theme } from '../../tokens/stitches.config' import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' -import { ChangeEvent, useCallback, useRef, useState, useMemo } from 'react' +import { ChangeEvent, useCallback, useRef, useState, useMemo, useEffect } from 'react' import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation' import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' import { LabelChip } from '../../elements/LabelChip' @@ -26,6 +26,8 @@ type EditLabelsModalProps = { type HeaderProps = { filterText: string + focused: boolean + parentRef: React.RefObject setFilterText: (text: string) => void } @@ -44,70 +46,68 @@ const FormInput = styled('input', { const StyledLabel = styled('label', { display: 'flex', justifyContent: 'flex-start', - '&:focus-visible': { - backgroundColor: 'red', + '&:focus': { + bg: '$grayBgActive', + }, + 'input:&:focus-within': { + bg: '$grayBgActive', }, }) +const useToggleLabels = () => { + const [selectedLabels, setSelectedLabels] = useState([]) + + const isSelected = useCallback((label: Label): boolean => { + return selectedLabels.some((other) => { + return other.id === label.id + }) + }, [selectedLabels]) + + const toggleLabel = useCallback((label: Label) => { + if (isSelected(label)) { + setSelectedLabels(selectedLabels.filter((other) => { + return other.id !== label.id + })) + } else { + setSelectedLabels([...selectedLabels, label]) + } + }, [isSelected, selectedLabels]) + + return [isSelected, toggleLabel] +} + function Header(props: HeaderProps): JSX.Element { const inputRef = useRef(null) + useEffect(() => { + if (props.focused && inputRef.current) { + inputRef.current.focus() + } + }, [props.focused]) + return ( - {/* */} - {/* - Apply labels to this page - */} - {/* */} - {/* */} -
{ - // event.preventDefault() - // props.applySearchQuery(searchTerm || '') - inputRef.current?.blur() - }} - > { event.target.select() //setFocused(true) + console.log('input::focused()') }} onBlur={() => { - //setFocused(false) - console.log('blurred') + // setFocused(false) + console.log('input::blurred') }} onKeyDown={(event) => { console.log('keydown', event.key) @@ -134,94 +134,82 @@ function Header(props: HeaderProps): JSX.Element { }, }} /> -
) } -type LabelsListProps = { - // pageId: string - // selectedLabels: Label[] - availableLabels: Label[] -// setSelectedLabels: (labels: Label[]) => void +type LabelListItemProps = { + label: Label + focused: boolean + selected: boolean } -function LabelsList(props: LabelsListProps): JSX.Element { - const isSelected = useCallback((label: Label) => { - // return props.selectedLabels.some((other) => { - // return other.id === label.id - // }) - return false - }, []) +function LabelListItem(props: LabelListItemProps): JSX.Element { + const ref = useRef(null) + const { label, focused, selected } = props + + useEffect(() => { + if (props.focused && ref.current) { + ref.current.focus() + } + }, [props.focused]) return ( - - {props.availableLabels && - props.availableLabels.map((label, idx) => ( - { - // console.log('selected label', label) - // if (props.selectedLabels.includes(label)) { - // props.setSelectedLabels( - // props.selectedLabels.filter((id) => id !== label) - // ) - // } else { - // props.setSelectedLabels([...props.selectedLabels, label]) - // } - // const result = await setLabelsMutation(props.pageId, props.selectedLabels.map((l) => l.id)) - // console.log('result', result) - }} - > - - {isSelected(label) && } - - - - - - {label.name} - - - {isSelected(label) && } - - {/* - - */} - {/* */} - - ))} - ) + { + console.log('toggling label') + }} + > + + + {selected && } + + + + + + {label.name} + + + {selected && } + + + ) } export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element { + const parentRef = useRef(null) const [filterText, setFilterText] = useState('') - const [selectedLabels, setSelectedLabels] = useState([]) const { labels } = useGetLabelsQuery() - const saveAndExit = useCallback(async () => { - // const result = await setLabelsMutation(props.article.id, selectedLabels.map((l) => l.id)) - // console.log('result of setting labels', result) - // // props.onOpenChange(false) - // // props.setLabels(selectedLabels) - }, [props, selectedLabels]) + const [selectedLabels, setSelectedLabels] = useState([]) + + const isSelected = useCallback((label: Label): boolean => { + return selectedLabels.some((other) => { + return other.id === label.id + }) + }, [selectedLabels]) + + const toggleLabel = useCallback((label: Label) => { + if (isSelected(label)) { + setSelectedLabels(selectedLabels.filter((other) => { + return other.id !== label.id + })) + } else { + setSelectedLabels([...selectedLabels, label]) + } + }, [isSelected, selectedLabels]) const filteredLabels = useMemo(() => { if (!labels) { @@ -232,58 +220,98 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element { }) }, [labels, filterText]) + useEffect(() => { + setFocusedIndex(undefined) + }, [filterText]) + + // Move focus through the labels list on tab or arrow up/down keys + const [focusedIndex, setFocusedIndex] = useState(undefined) + const handleKeyDown = useCallback((event: React.KeyboardEvent) => { + if (event.key === 'ArrowUp') { + event.preventDefault() + if (focusedIndex) { + setFocusedIndex(Math.max(0, focusedIndex - 1)) + } else { + setFocusedIndex(undefined) + } + } + if (event.key === 'ArrowDown' || event.key === 'Tab') { + event.preventDefault() + if (focusedIndex === undefined) { + setFocusedIndex(0) + } else { + setFocusedIndex(Math.min(filteredLabels.length - 1, focusedIndex + 1)) + } + } + if (event.key === 'Enter') { + event.preventDefault() + if (focusedIndex !== undefined) { + const label = filteredLabels[focusedIndex] + if (label) { + toggleLabel(label) + } + } + } + }, [filteredLabels, focusedIndex]) + return ( - // - // - // { - // event.preventDefault() - // }} - // css={{ p: '0', width: '100%', maxWidth: '400px' }} - // > - +
+ + {labels && + labels.map((label, idx) => ( + + ))} + + {filterText && ( + )} + {/* Footer */} + -
- - {filterText && ( - )} - {/* Footer */} - - - Edit labels - - - // - // + 'a:visited': { + color: theme.colors.grayText.toString(), + }, + }} + > + + Edit labels + + ) } \ No newline at end of file