Fixed barrels and input

This commit is contained in:
Sebastian Seedorf
2022-08-19 16:37:55 +02:00
parent 58c3b1a3c3
commit ce682a6939
8 changed files with 224 additions and 24 deletions

View File

@@ -2,6 +2,10 @@
padding: 0.3em;
}
.select :global(.factory-select__multi-value) {
--fixed-item-bg: #ccc;
}
@media (prefers-color-scheme: dark) {
.select :global(.factory-select__control) {
background-color: #222;
@@ -10,6 +14,8 @@
.select :global(.factory-select__multi-value),
.select :global(.factory-select__menu) {
background-color: #444;
--fixed-item-bg: #666;
}
.option:is(:hover, :focus-visible) {

View File

@@ -1,33 +1,93 @@
import { FC, memo, useMemo } from 'react'
import Select from 'react-select'
import { FC, memo, useCallback, useEffect, useMemo } from 'react'
import Select, { ActionMeta, CSSObjectWithLabel } from 'react-select'
import { isNonNullable } from '../../../src/utils'
import details from '../../../res/details.json'
import styles from './FactorySelect.module.css'
import { useFactories } from '../../../src/hooks/useFactories'
import { EntitySpan } from '../EntitySpan/EntitySpan'
interface Props {
id: string
factories: string[]
onSetFactories: (factories: string[]) => void
fixInputs?: boolean
}
const options = details.map(detail => ({
label: detail.name,
value: detail.href
}))
interface Option {
label: string
value: string
isFixed: boolean
}
const selectStyles: Record<
'multiValue' | 'multiValueLabel' | 'multiValueRemove',
(base: CSSObjectWithLabel, state: { data: Option }) => CSSObjectWithLabel
> = {
multiValue: (base, state) => {
return state.data.isFixed
? { ...base, backgroundColor: 'var(--fixed-item-bg) !important' }
: base
},
multiValueLabel: (base, state) => {
return state.data.isFixed ? { ...base, fontWeight: 'bold', paddingRight: '6px' } : base
},
multiValueRemove: (base, state) => {
return state.data.isFixed ? { ...base, display: 'none' } : base
}
}
const orderOptions = (values: Option[]) => {
return values.filter(v => v.isFixed).concat(values.filter(v => !v.isFixed))
}
const FactorySelectBase: FC<Props> = ({ id, factories, onSetFactories, fixInputs }) => {
const { factories: details } = useFactories()
const options = useMemo<Option[]>(
() =>
details.map(detail => ({
label: detail.name,
value: detail.href,
isFixed: !!fixInputs && !detail.recipe
})),
[details, fixInputs]
)
const FactorySelectBase: FC<Props> = ({ id, factories, onSetFactories }) => {
const state = useMemo<typeof options>(() => {
return factories
.map(factory => options.find(option => option.value === factory))
.filter(isNonNullable)
}, [factories])
}, [factories, options])
useEffect(() => {
if (!fixInputs) return
const set = new Set(factories)
const addOptions = options
.filter(option => option.isFixed && !set.has(option.value))
.map(option => option.value)
if (addOptions.length) {
onSetFactories([...factories, ...addOptions])
}
}, [factories, fixInputs, onSetFactories, options])
const onChange = useCallback(
(values: readonly Option[], { action, removedValue }: ActionMeta<Option>) => {
if ((action === 'remove-value' || action === 'pop-value') && removedValue.isFixed) {
return
} else if (action === 'clear') {
const clearedValues = orderOptions(options.filter(v => v.isFixed))
onSetFactories(clearedValues.map(s => s?.value))
} else {
onSetFactories(values.map(s => s?.value))
}
},
[onSetFactories, options]
)
return (
<Select
id={id}
instanceId={id}
value={state}
value={orderOptions(state)}
isClearable={state.some(option => !option.isFixed)}
components={{
MultiValueLabel: ({ data, innerProps }) => (
<EntitySpan {...innerProps} value={data.value} simpleStyle={true} />
@@ -39,12 +99,11 @@ const FactorySelectBase: FC<Props> = ({ id, factories, onSetFactories }) => {
)
}}
isMulti
options={options as never}
onChange={e => {
onSetFactories(e.map(s => s?.value))
}}
options={orderOptions(options)}
onChange={onChange}
className={styles.select}
classNamePrefix={'factory-select'}
styles={selectStyles}
/>
)
}