Edit labels menu

This commit is contained in:
Jackson Harper 2022-04-08 20:19:18 -07:00
parent ba301c4f0f
commit 278a885f83
3 changed files with 260 additions and 83 deletions

View file

@ -5,6 +5,7 @@ import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsModal } from "./EditLabelsModal"
import { ReaderSettings } from "./ReaderSettingsModal"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@ -54,7 +55,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
<TextAa size={24} color={theme.colors.readerFont.toString()} />
</SpanBox>
}
css={{ m: '0px', p: '0px', outlineStyle: 'solid', outlineWidth: '1px', outlineColor: theme.colors.grayLine.toString() }}
css={{ m: '0px', p: '0px' }}
>
<ReaderSettings userPreferences={props.userPreferences} articleActionHandler={props.articleActionHandler} />
</Dropdown>
@ -63,13 +64,16 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
<Dropdown
side='right'
sideOffset={18}
align='start'
triggerElement={
<SpanBox css={{ width: '100%', marginLeft: 'auto', marginRight: 'auto' }}>
<TagSimple size={24} color={theme.colors.readerFont.toString()} />
</SpanBox>
}
css={{ background: 'red' }}
css={{ m: '0px', p: '0px' }}
>
<EditLabelsModal />
{/* <EditLabelsModal> */}
</Dropdown>

View file

@ -3,108 +3,281 @@ import {
ModalOverlay,
ModalRoot,
} from '../../elements/ModalPrimitives'
import { HStack, VStack } from '../../elements/LayoutPrimitives'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { CrossIcon } from '../../elements/images/CrossIcon'
import { theme } from '../../tokens/stitches.config'
import { styled, theme } from '../../tokens/stitches.config'
import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
import { ChangeEvent, useCallback, useState } from 'react'
import { ChangeEvent, useCallback, useRef, useState, useMemo } from 'react'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { LabelChip } from '../../elements/LabelChip'
import { Check, Circle, Pen, PencilSimple, PencilSimpleLine, Plus, TagSimple } from 'phosphor-react'
import Link from 'next/link'
type EditLabelsModalProps = {
article: ArticleAttributes
onOpenChange: (open: boolean) => void
setLabels: (labels: Label[]) => void
// labels: Label[]
// article: ArticleAttributes
// onOpenChange: (open: boolean) => void
// setLabels: (labels: Label[]) => void
}
type HeaderProps = {
filterText: string
setFilterText: (text: string) => void
}
const FormInput = styled('input', {
width: '100%',
fontSize: '16px',
fontFamily: 'inter',
fontWeight: 'normal',
lineHeight: '1.8',
color: '$grayTextContrast',
'&:focus': {
outline: 'none',
},
})
const StyledLabel = styled('label', {
display: 'flex',
justifyContent: 'flex-start',
'&:focus-visible': {
backgroundColor: 'red',
},
})
function Header(props: HeaderProps): JSX.Element {
const inputRef = useRef<HTMLInputElement>(null)
return (
<VStack css={{ width: '100%', my: '0px', borderBottom: '1px solid $grayBorder',
}}>
{/* <HStack
distribution="between"
alignment="center"
css={{
width: '100%',
flex: '0 0 20px',
mx: '0px',
p: '0px',
borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
}}
> */}
{/* <StyledText style="modalHeadline" css={{ p: '16px', fontSize: '12px', }}>
Apply labels to this page
</StyledText> */}
{/* <Button
css={{ pt: '16px', pr: '16px' }}
style="ghost"
onClick={() => {
}}
>
<CrossIcon
size={20}
strokeColor={theme.colors.grayText.toString()}
/>
</Button> */}
{/* </HStack> */}
<Box css={{
width: '100%',
my: '14px',
px: '14px',
}}>
<form
onSubmit={(event) => {
// event.preventDefault()
// props.applySearchQuery(searchTerm || '')
inputRef.current?.blur()
}}
>
<FormInput
ref={inputRef}
type="text"
autoFocus={true}
value={props.filterText}
placeholder="Filter for label"
onFocus={(event) => {
event.target.select()
//setFocused(true)
}}
onBlur={() => {
//setFocused(false)
console.log('blurred')
}}
onKeyDown={(event) => {
console.log('keydown', event.key)
}}
onChange={(event) => {
console.log('event', event)
props.setFilterText(event.target.value)
}}
css={{
border: '1px solid $grayBorder',
borderRadius: '8px',
width: '100%',
bg: 'transparent',
fontSize: '16px',
fontFamily: 'inter',
fontWeight: 'normal',
marginBottom: '2px',
textIndent: '8px',
paddingLeft: '8px',
color: '$grayTextContrast',
'&:focus': {
outline: 'none',
boxShadow: '0px 0px 2px 2px rgba(255, 234, 159, 0.56)',
},
}}
/>
</form>
</Box>
</VStack>)
}
type LabelsListProps = {
// pageId: string
// selectedLabels: Label[]
availableLabels: Label[]
// setSelectedLabels: (labels: Label[]) => void
}
function LabelsList(props: LabelsListProps): JSX.Element {
const isSelected = useCallback((label: Label) => {
// return props.selectedLabels.some((other) => {
// return other.id === label.id
// })
return false
}, [])
return (
<VStack css={{ flexGrow: '1', overflow: 'scroll', width: '100%', height: '100%', maxHeight: '400px', }}>
{props.availableLabels &&
props.availableLabels.map((label, idx) => (
<StyledLabel
key={label.id}
tabIndex={idx + 1}
css={{
width: '100%',
height: '42px',
borderBottom: '1px solid $grayBorder',
}}
onClick={async () => {
// 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)
}}
>
<Box css={{ pl: '5px', width: '25px', height: '45px', display: 'flex', alignItems: 'center' }}>
{isSelected(label) && <Check color={theme.colors.grayText.toString()} weight='bold' />}
</Box>
<Box css={{ width: '30px', height: '100%', display: 'flex', alignItems: 'center' }}>
<Circle width={22} height={22} color={label.color} weight='fill' />
</Box>
<Box css={{ overflow: 'clip', height: '100%', display: 'flex', alignItems: 'center' }}>
<StyledText style="caption">{label.name}</StyledText>
</Box>
<Box css={{ pl: '10px', width: '40px', marginLeft: 'auto', height: '100%', display: 'flex', alignItems: 'center' }}>
{isSelected(label) && <CrossIcon
size={14}
strokeColor={theme.colors.grayText.toString()}
/>}
</Box>
{/*
<CrossIcon
size={14}
strokeColor={theme.colors.grayText.toString()}
/> */}
{/* <input
type="checkbox"
value={label.id}
onChange={handleChange}
checked={selectedLabels.includes(label)}
/> */}
</StyledLabel>
))}
</VStack>)
}
export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
const [selectedLabels, setSelectedLabels] = useState(props.labels)
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)
// 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 handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
// const label = event.target.value
// if (event.target.checked) {
// setSelectedLabels([...selectedLabels, label])
// } else {
// setSelectedLabels(selectedLabels.filter((l) => l !== label))
// }
},
[selectedLabels]
)
const filteredLabels = useMemo(() => {
if (!labels) {
return []
}
return labels.filter((label) => {
return label.name.toLowerCase().includes(filterText.toLowerCase())
})
}, [labels, filterText])
return (
<ModalRoot defaultOpen onOpenChange={saveAndExit}>
<ModalOverlay />
<ModalContent
onPointerDownOutside={(event) => {
event.preventDefault()
}}
css={{ overflow: 'auto', p: '0' }}
>
<VStack distribution="start" css={{ p: '0' }}>
<HStack
distribution="between"
alignment="center"
css={{ width: '100%' }}
>
<StyledText style="modalHeadline" css={{ p: '16px' }}>
Edit Labels
</StyledText>
<Button
css={{ pt: '16px', pr: '16px' }}
style="ghost"
// <ModalRoot defaultOpen onOpenChange={saveAndExit}>
// <ModalOverlay />
// <ModalContent
// onPointerDownOutside={(event) => {
// event.preventDefault()
// }}
// css={{ p: '0', width: '100%', maxWidth: '400px' }}
// >
<VStack distribution="start" css={{
p: '0', maxHeight: '400px',
width: '265px',
}}>
<Header setFilterText={setFilterText} filterText={filterText} />
<LabelsList
availableLabels={filteredLabels}
// selectedLabels={selectedLabels || []}
/>
{filterText && (
<Button style='modalOption' css={{
pl: '26px', color: theme.colors.grayText.toString(), height: '42px', borderBottom: '1px solid $grayBorder'
}}
onClick={() => {
props.onOpenChange(false)
// props.createLabel(filterText)
// setFilterText('')
}}
>
<CrossIcon
size={20}
strokeColor={theme.colors.grayText.toString()}
/>
</Button>
</HStack>
{labels &&
labels.map((label) => (
<HStack
key={label.id}
css={{ height: '50px', verticalAlign: 'middle' }}
onClick={() => {
// if (selectedLabels.includes(label.id)) {
// setSelectedLabels(
// selectedLabels.filter((id) => id !== label.id)
// )
// } else {
// setSelectedLabels([...selectedLabels, label.id])
// }
}}
>
<LabelChip color={label.color} text={label.name} />
<input
type="checkbox"
value={label.id}
onChange={handleChange}
checked={selectedLabels.includes(label)}
/>
</HStack>
))}
<HStack css={{ width: '100%', mb: '16px' }} alignment="center">
<Button style="ctaDarkYellow" onClick={saveAndExit}>
Save
</Button>
<HStack alignment='center' distribution='start' css={{ gap: '8px' }}>
<Plus size={18} color={theme.colors.grayText.toString()} />
<SpanBox css={{ fontSize: '12px' }}>{`Create new label "${filterText}"`}</SpanBox>
</HStack>
</Button>)}
{/* Footer */}
<HStack distribution="start" alignment="center" css={{
ml: '20px', gap: '8px', width: '100%', fontSize: '12px', p: '8px', height: '42px',
'a:link': {
textDecoration: 'none',
},
'a:visited': {
color: theme.colors.grayText.toString(),
},
}}
>
<PencilSimple size={18} color={theme.colors.grayText.toString()} />
<Link href="/settings/labels">Edit labels</Link>
</HStack>
</VStack>
</ModalContent>
</ModalRoot>
// </ModalContent>
// </ModalRoot>
)
}
}

View file

@ -65,7 +65,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
<Button style='plainIcon' css={{ pt: '10px', px: '4px' }} onClick={() => props.articleActionHandler('decrementMarginWidth')}>
<ArrowsInLineHorizontal size={24} color={theme.colors.readerFont.toString()} />
</Button>
<TickedRangeSlider min={10} max={28} value={props.userPreferences.margin} onChange={() => {}} />
<TickedRangeSlider min={10} max={28} value={props.userPreferences?.margin || 0} onChange={() => {}} />
<Button style='plainIcon' css={{ pt: '10px', px: '4px' }} onClick={() => props.articleActionHandler('incrementMarginWidth')}>
<ArrowsOutLineHorizontal size={24} color={theme.colors.readerFont.toString()} />
</Button>