Files
omnivore/packages/web/components/elements/LabelsPicker.tsx
2023-06-19 21:42:18 +08:00

206 lines
5.7 KiB
TypeScript

import AutosizeInput from 'react-input-autosize'
import { Box, SpanBox } from './LayoutPrimitives'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Label } from '../../lib/networking/fragments/labelFragment'
import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery'
import { LabelChip } from './LabelChip'
import { isTouchScreenDevice } from '../../lib/deviceType'
type LabelsPickerProps = {
selectedLabels: Label[]
focused: boolean
inputValue: string
setInputValue: (value: string) => void
clearInputState: () => void
onFocus?: () => void
setSelectedLabels: (labels: Label[]) => void
deleteLastLabel: () => void
selectOrCreateLabel: (value: string) => void
tabCount: number
setTabCount: (count: number) => void
tabStartValue: string
setTabStartValue: (value: string) => void
highlightLastLabel: boolean
setHighlightLastLabel: (set: boolean) => void
}
export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => {
const inputRef = useRef<HTMLInputElement | null>()
const availableLabels = useGetLabelsQuery()
useEffect(() => {
if (!isTouchScreenDevice() && props.focused && inputRef.current) {
inputRef.current.focus()
}
}, [props.focused])
const autoComplete = useCallback(() => {
const lowerCasedValue = props.inputValue.toLowerCase()
if (lowerCasedValue.length < 1) {
return
}
let _tabCount = props.tabCount
let _tabStartValue = props.tabStartValue.toLowerCase()
if (_tabCount === -1) {
_tabCount = 0
_tabStartValue = lowerCasedValue
props.setTabCount(0)
props.setTabStartValue(lowerCasedValue)
} else {
_tabCount = props.tabCount + 1
props.setTabCount(_tabCount)
}
const matches = availableLabels.labels.filter((l) =>
l.name.toLowerCase().startsWith(_tabStartValue)
)
if (_tabCount < matches.length) {
props.setInputValue(matches[_tabCount].name)
} else if (matches.length > 0) {
props.setTabCount(0)
props.setInputValue(matches[0].name)
}
}, [props.inputValue, availableLabels, props.tabCount, props.tabStartValue])
const clearTabState = useCallback(() => {
props.setTabCount(-1)
props.setTabStartValue('')
}, [])
const isEmpty = useMemo(() => {
return props.selectedLabels.length === 0 && props.inputValue.length === 0
}, [props.inputValue, props.selectedLabels])
return (
<Box
css={{
display: 'inline-block',
bg: '#3D3D3D',
border: '1px transparent solid',
borderRadius: '6px',
verticalAlign: 'center',
padding: '5px',
lineHeight: '2',
width: '100%',
cursor: 'text',
color: '#EBEBEB',
fontSize: '12px',
fontFamily: '$inter',
input: {
all: 'unset',
left: '0px',
outline: 'none',
borderStyle: 'none',
marginLeft: '2px',
},
'&:focus-within': {
outline: 'none',
border: '1px solid $thLibraryMenuUnselected',
},
'>span': {
marginTop: '0px',
marginBottom: '0px',
},
}}
onMouseDown={(event) => {
inputRef.current?.focus()
inputRef.current?.setSelectionRange(
inputRef.current?.value.length,
inputRef.current?.value.length
)
event.preventDefault()
}}
onDoubleClick={(event) => {
inputRef.current?.focus()
inputRef.current?.setSelectionRange(0, inputRef.current?.value.length)
}}
>
{props.selectedLabels.map((label, idx) => (
<LabelChip
key={label.id}
text={label.name}
color={label.color}
isSelected={
props.highlightLastLabel && idx == props.selectedLabels.length - 1
}
xAction={() => {
const idx = props.selectedLabels.findIndex((l) => l.id == label.id)
if (idx !== -1) {
const _selectedLabels = props.selectedLabels
_selectedLabels.splice(idx, 1)
props.setSelectedLabels([..._selectedLabels])
}
}}
useAppAppearance={true}
/>
))}
<SpanBox
css={{
display: 'inline-flex',
height: '24px',
paddingBottom: '20px',
transform: `translateY(-2px)`,
}}
>
<AutosizeInput
placeholder={isEmpty ? 'Filter for label' : undefined}
inputRef={(ref) => {
inputRef.current = ref
}}
onFocus={() => {
if (props.onFocus) {
props.onFocus()
}
}}
minWidth="2px"
maxLength={48}
value={props.inputValue}
onClick={(event) => {
event.stopPropagation()
}}
onKeyUp={(event) => {
switch (event.key) {
case 'Escape':
props.clearInputState()
break
case 'Enter':
props.selectOrCreateLabel(props.inputValue)
event.preventDefault()
break
}
}}
onKeyDown={(event) => {
switch (event.key) {
case 'Tab':
autoComplete()
event.preventDefault()
break
case 'Delete':
case 'Backspace':
clearTabState()
if (props.inputValue.length === 0) {
props.deleteLastLabel()
event.preventDefault()
}
break
}
}}
onChange={function (event) {
props.setInputValue(event.target.value)
}}
/>
</SpanBox>
</Box>
)
}