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}
/>
)
}

View File

@@ -14,6 +14,7 @@ export const Preferences: FC = () => {
id={'baseFactoriesSelect'}
factories={baseFactories}
onSetFactories={setBaseFactories}
fixInputs={true}
/>
</fieldset>
<fieldset>

7
res/exclude.json Normal file
View File

@@ -0,0 +1,7 @@
[
"/Blueprint",
"/Deconstruction_planner",
"/Upgrade_planner",
"/Blueprint_book",
"/Space_science_pack"
]

View File

@@ -71,5 +71,124 @@
"/Solid_fuel": 1
}
}
},
{
"name": "Crude oil barrel",
"href": "/Crude_oil_barrel",
"image": "/images/thumb/Crude_oil_barrel.png/32px-Crude_oil_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Crude_oil": 50
},
"time": 0.2,
"output": {
"/Crude_oil_barrel": 1
}
}
},
{
"name": "Heavy oil barrel",
"href": "/Heavy_oil_barrel",
"image": "/images/thumb/Heavy_oil_barrel.png/32px-Heavy_oil_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Heavy_oil": 50
},
"time": 0.2,
"output": {
"/Heavy_oil_barrel": 1
}
}
},
{
"name": "Light oil barrel",
"href": "/Light_oil_barrel",
"image": "/images/thumb/Light_oil_barrel.png/32px-Light_oil_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Light_oil": 50
},
"time": 0.2,
"output": {
"/Light_oil_barrel": 1
}
}
},
{
"name": "Lubricant barrel",
"href": "/Lubricant_barrel",
"image": "/images/thumb/Lubricant_barrel.png/32px-Lubricant_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Lubricant": 50
},
"time": 0.2,
"output": {
"/Lubricant_barrel": 1
}
}
},
{
"name": "Petroleum gas barrel",
"href": "/Petroleum_gas_barrel",
"image": "/images/thumb/Petroleum_gas_barrel.png/32px-Petroleum_gas_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Petroleum_gas": 50
},
"time": 0.2,
"output": {
"/Petroleum_gas_barrel": 1
}
}
},
{
"name": "Sulfuric acid barrel",
"href": "/Sulfuric_acid_barrel",
"image": "/images/thumb/Sulfuric_acid_barrel.png/32px-Sulfuric_acid_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Sulfuric_acid": 50
},
"time": 0.2,
"output": {
"/Sulfuric_acid_barrel": 1
}
}
},
{
"name": "Water barrel",
"href": "/Water_barrel",
"image": "/images/thumb/Water_barrel.png/32px-Water_barrel.png",
"recipe": {
"prerequisites": {
"/Empty_barrel": 1,
"/Water": 50
},
"time": 0.2,
"output": {
"/Water_barrel": 1
}
}
},
{
"name": "Steam",
"href": "/Steam",
"image": "/images/thumb/Steam.png/32px-Steam.png",
"recipe": {
"prerequisites": {
"/Water": 1
},
"time": 0,
"output": {
"/Steam": 1
}
}
}
]

View File

@@ -1,15 +1,22 @@
import { EnrichedEntity, Entity } from '../types'
import details from '../../res/details.json'
import manual from '../../res/manual.json'
import exclude from '../../res/exclude.json'
const joined = [...details, ...manual] as Entity[]
const manualEntities = manual as Entity[]
const manualKeys = new Set(manualEntities.map(entity => entity.href))
const detailEntities = (details as Entity[]).filter(detail => !manualKeys.has(detail.href))
const factories = joined.map((detail: EnrichedEntity) => {
detail.usedBy = joined.filter(f =>
Object.keys(f.recipe?.prerequisites ?? {}).includes(detail.href)
)
return detail
})
const joined = [...detailEntities, ...manualEntities]
const factories = joined
.map((detail: EnrichedEntity) => {
detail.usedBy = joined.filter(f =>
Object.keys(f.recipe?.prerequisites ?? {}).includes(detail.href)
)
return detail
})
.filter(detail => !(exclude as string[]).includes(detail.href))
const detailsMap = Object.fromEntries(
factories.map((detail: EnrichedEntity) => [detail.href, detail])

View File

@@ -1,7 +1,7 @@
export interface Recipe {
prerequisites: Dict<number>
prerequisites: Dict<number | undefined>
time: number
output: Dict<number>
output: Dict<number | undefined>
}
export interface UnfetchedEntity {

View File

@@ -6,7 +6,8 @@
"jest.config.js",
"next.config.js",
"src/backend-custom-server/index",
"scripts/*"
"scripts/*",
"res/*.json"
],
"exclude": ["dist", ".next", "out", "node_modules", "cypress"]
}