From 74fddc67108f8e208254df984aa533693ffe34c6 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Fri, 12 Aug 2022 10:33:37 +0200 Subject: [PATCH] Improvements in performance --- components/contexts/GroupProvider.tsx | 113 ++++++------ .../home/EntityIcon/EntityIcon.module.css | 7 +- components/home/EntityIcon/EntityIcon.tsx | 8 +- .../home/EntitySpan/EntitySpan.module.css | 45 ++++- components/home/EntitySpan/EntitySpan.tsx | 21 ++- .../FactorySelect/FactorySelect.module.css | 23 +++ .../home/FactorySelect/FactorySelect.tsx | 21 ++- components/home/Group/Group.module.css | 50 ++++- components/home/Group/Group.tsx | 172 +++++++++--------- components/home/Home.tsx | 43 +---- components/home/Preferences/Preferences.tsx | 42 +++++ package.json | 1 + src/hooks/useDetails.ts | 4 - src/hooks/useFactories.ts | 23 +++ src/types.ts | 12 +- styles/globals.css | 14 +- yarn.lock | 5 + 17 files changed, 390 insertions(+), 214 deletions(-) create mode 100644 components/home/FactorySelect/FactorySelect.module.css create mode 100644 components/home/Preferences/Preferences.tsx delete mode 100644 src/hooks/useDetails.ts create mode 100644 src/hooks/useFactories.ts diff --git a/components/contexts/GroupProvider.tsx b/components/contexts/GroupProvider.tsx index 9e245f8..f4c3b93 100644 --- a/components/contexts/GroupProvider.tsx +++ b/components/contexts/GroupProvider.tsx @@ -1,7 +1,6 @@ -import {createContext, FC, useContext, useMemo, useState} from "react"; -import {Group, NewGroup} from "../../src/types"; +import {createContext, FC, useCallback, useContext, useMemo} from "react"; +import {Group} from "../../src/types"; import {useLocalStorage} from "../../src/hooks/useLocalStorage"; -import {sortByProperty} from "../../src/utils"; import {ReactNodeLike} from "prop-types"; interface Props { @@ -17,13 +16,12 @@ interface GroupContextType { baseFactories: string[] setBaseFactories(factories: string[]): void - groups: NewGroup[] + groups: Record addGroup(name: string, exported?: string[], malls?: string[]): void - removeGroup(idx: number): void - renameGroup(idx: number, newName: string): void + removeGroup(name: string): void + renameGroup(name: string, newName: string): void - getGroup(idx: number): NewGroup - setFactories(idx: number, factories: string[], type: 'inputs'|'intermediates'|'exports'|'malls'): void + setFactories(name: string, factories: string[], type: 'inputs'|'intermediates'|'exports'|'malls'): void getInputType(uid: string): 'base'|'produced'|'unknown' } @@ -37,12 +35,11 @@ const defaultValues: GroupContextType = { baseFactories: [], setBaseFactories() {}, - groups: [], + groups: {}, addGroup() {}, removeGroup() {}, renameGroup() {}, - getGroup() {return {name: 'Lorem', inputs: [], intermediates: [], exports: [], malls: []}}, setFactories() {}, getInputType() {return 'unknown'} } @@ -53,20 +50,51 @@ export const useGroups = () => useContext(GroupContext) export const GroupProvider: FC = ({children}) => { const [excludedSuggestions, setExcludedSuggestions] = useLocalStorage('excludedSuggestions', []) const [basicValues, setBasicValues] = useLocalStorage('basicValues', []) - const [oldGroups, setGroups] = useLocalStorage('serviceGroups', []) - const groups = useMemo(() => { - return oldGroups.map(groupToNewGroup) - }, [oldGroups]) + const [groups, setGroups] = useLocalStorage>('serviceGroups', {}) const doNotSuggest = useMemo>(() => { - return new Set([...groups.flatMap(group => [...group.exports, ...group.malls, ...(group.malls.length ? group.intermediates : [])]), ...excludedSuggestions, ...basicValues]) + return new Set([...Object.values(groups).flatMap(group => [...group.exports, ...group.malls, ...(group.malls.length ? group.intermediates : [])]), ...excludedSuggestions, ...basicValues]) }, [basicValues, groups, excludedSuggestions]) const exportedFactories = useMemo>(() => { - return new Set([...groups.flatMap(group => [...group.exports])]) + return new Set([...Object.values(groups).flatMap(group => [...group.exports])]) }, [groups]) - const value: GroupContextType = { + const addGroup = useCallback((name: string, exports: string[] = [], malls: string[] = []) => { + if (name in groups) return false + setGroups(groups => { + groups[name] = { name, inputs: [], intermediates: [], exports, malls } + return groups + }) + return true + }, [groups, setGroups]) + const removeGroup = useCallback((name: string) => { + setGroups(groups => { + delete groups[name] + return groups + }) + }, [setGroups]) + const renameGroup = useCallback((name: string, newName: string) => { + setGroups(groups => { + groups[newName] = {...groups[name], name: newName} + delete groups[name] + return groups + }) + }, [setGroups]) + + const setFactories = useCallback((name: string, factories: string[], type: 'inputs'|'intermediates'|'exports'|'malls') => { + setGroups(groups => { + groups[name] = {...groups[name], [type]: factories} + return groups + }) + }, [setGroups]) + const getInputType = useCallback((uid: string) => { + if (basicValues.includes(uid)) return 'base' + else if (exportedFactories.has(uid)) return 'produced' + else return 'unknown' + }, [basicValues, exportedFactories]) + + const value: GroupContextType = useMemo(() => ({ doNotSuggest, ignoredFactories: excludedSuggestions, @@ -76,51 +104,12 @@ export const GroupProvider: FC = ({children}) => { setBaseFactories: setBasicValues, groups, - addGroup(name: string, exports: string[] = [], malls: string[] = []) { - setGroups(groups => { - groups.push({ name, inputs: [], intermediates: [], exports, malls }) - return groups.sort(sortByProperty(group => group.name)) - }) - }, - removeGroup(idx: number) { - setGroups(groups => { - groups.splice(idx, 1) - return groups - }) - }, - renameGroup(idx: number, newName: string) { - setGroups(groups => { - groups[idx].name = newName - return groups.sort(sortByProperty(group => group.name)) - }) - }, + addGroup, + removeGroup, + renameGroup, - getGroup(idx: number) { - return groups[idx] - }, - setFactories(idx: number, factories: string[], type: 'inputs'|'intermediates'|'exports'|'malls') { - setGroups(groups => { - const group = groupToNewGroup(groups[idx]) - group[type] = factories - groups[idx] = group - return groups - }) - }, - getInputType(uid: string) { - if (basicValues.includes(uid)) return 'base' - else if (exportedFactories.has(uid)) return 'produced' - else return 'unknown' - } - } + setFactories, + getInputType + }), [addGroup, basicValues, doNotSuggest, excludedSuggestions, getInputType, groups, removeGroup, renameGroup, setBasicValues, setExcludedSuggestions, setFactories]) return {children} } - -function groupToNewGroup(group: Group): NewGroup { - return 'malls' in group ? group : { - name: group.name, - inputs: group.inputs, - intermediates: group.intermediates, - exports: group.isExported ? group.factories : [], - malls: !group.isExported ? group.factories : [] - } -} diff --git a/components/home/EntityIcon/EntityIcon.module.css b/components/home/EntityIcon/EntityIcon.module.css index 4f0f2f8..a70ebfb 100644 --- a/components/home/EntityIcon/EntityIcon.module.css +++ b/components/home/EntityIcon/EntityIcon.module.css @@ -22,6 +22,9 @@ margin-inline-end: 0.2em; } -.strong { - font-weight: 600; +@media (prefers-color-scheme: dark) { + .span { + border-color: #111111; + background-color: #444; + } } diff --git a/components/home/EntityIcon/EntityIcon.tsx b/components/home/EntityIcon/EntityIcon.tsx index a2b7eb8..871302c 100644 --- a/components/home/EntityIcon/EntityIcon.tsx +++ b/components/home/EntityIcon/EntityIcon.tsx @@ -1,6 +1,6 @@ import {FC, HTMLProps, useMemo} from "react" import {Entity} from "../../../src/types" -import {useDetails} from "../../../src/hooks/useDetails" +import {useFactories} from "../../../src/hooks/useFactories" import styles from './EntityIcon.module.css' interface Props extends Omit, 'value'> { @@ -9,17 +9,17 @@ interface Props extends Omit, 'value'> { } export const EntityIcon: FC = ({value, amount, ...rest}) => { - const details = useDetails() + const {findFactory} = useFactories() const entity = useMemo(() => { return typeof value === "object" ? value - : details.find(detail => detail.href === value) ?? { + : findFactory(value) ?? { href: value, name: value, image: value, recipe: undefined } - }, [details, value]) + }, [findFactory, value]) return {/* eslint-disable-next-line @next/next/no-img-element */} diff --git a/components/home/EntitySpan/EntitySpan.module.css b/components/home/EntitySpan/EntitySpan.module.css index 3e0be9e..c878290 100644 --- a/components/home/EntitySpan/EntitySpan.module.css +++ b/components/home/EntitySpan/EntitySpan.module.css @@ -1,9 +1,15 @@ .span { - background: #DDD; + background: lightgray; font-size: 1em; border: 1px solid white; display: inline-block; position: relative; + padding-inline: 0.2em; + border-radius: 4px; +} + +.spanSimple { + padding-inline-start: 0.2em; } .tooltip { @@ -24,7 +30,7 @@ z-index: 1; } -.span:hover > .tooltip { +.span:is(:focus, :hover) > .tooltip { display: initial; } @@ -49,6 +55,18 @@ transform: translateY(0.1em); } +.base { + color: darkgreen; +} + +.produced { + color: darkgoldenrod; +} + +.unknown { + color: inherit; +} + .strong { font-weight: 600; margin-block: 1em 0.4em; @@ -71,3 +89,26 @@ .clickBtn { fill: red; } + +@media (prefers-color-scheme: dark) { + .span { + border-color: #111111; + background-color: #444; + } + + .base { + color: lightgreen; + } + + .produced { + color: lightsalmon; + } + + .unknown { + color: lightgray; + } + + .tooltip { + --background: darkred; + } +} diff --git a/components/home/EntitySpan/EntitySpan.tsx b/components/home/EntitySpan/EntitySpan.tsx index 0ccc7a1..c8ce508 100644 --- a/components/home/EntitySpan/EntitySpan.tsx +++ b/components/home/EntitySpan/EntitySpan.tsx @@ -1,33 +1,36 @@ -import {FC, HTMLProps, useMemo} from "react" +import {FC, HTMLProps, memo, useMemo} from "react" import {Entity} from "../../../src/types" -import {useDetails} from "../../../src/hooks/useDetails" +import {useFactories} from "../../../src/hooks/useFactories" import styles from './EntitySpan.module.css' import {RecipeSpan} from "../Recipe/Recipe"; import {LeftClickIcon} from "../LeftClickIcon/LeftClickIcon"; +import cx from 'classnames'; interface Props extends Omit, 'value'> { value: Entity|string + state?: 'base'|'produced'|'unknown' leftClickText?: string rightClickText?: string + simpleStyle?: boolean } -export const EntitySpan: FC = ({value, leftClickText, rightClickText, ...rest}) => { - const details = useDetails() +const EntitySpanUnmemo: FC = ({value, state, leftClickText, rightClickText, simpleStyle, ...rest}) => { + const {findFactory} = useFactories() const entity = useMemo(() => { return typeof value === "object" ? value - : details.find(detail => detail.href === value) ?? { + : findFactory(value) ?? { href: value, name: value, image: value, recipe: undefined } - }, [details, value]) + }, [findFactory, value]) - return + return {/* eslint-disable-next-line @next/next/no-img-element */} {entity.name}/ - {entity.name} + {entity.name}
{entity.recipe && ( <> @@ -45,3 +48,5 @@ export const EntitySpan: FC = ({value, leftClickText, rightClickText, ...
} + +export const EntitySpan = memo(EntitySpanUnmemo) diff --git a/components/home/FactorySelect/FactorySelect.module.css b/components/home/FactorySelect/FactorySelect.module.css new file mode 100644 index 0000000..264b4fc --- /dev/null +++ b/components/home/FactorySelect/FactorySelect.module.css @@ -0,0 +1,23 @@ +@media (prefers-color-scheme: dark) { + .select :global(.factory-select__control) { + background-color: #222; + } + + .select :global(.factory-select__multi-value), + .select :global(.factory-select__menu) + { + background-color: #444; + } + + .option { + padding: 0.3em; + } + + .option:is(:hover, :focus-visible) { + background-color: black; + } + + .select :global(.factory-select__multi-value__label) { + color: #DDDDDD; + } +} diff --git a/components/home/FactorySelect/FactorySelect.tsx b/components/home/FactorySelect/FactorySelect.tsx index abe55c4..58200a2 100644 --- a/components/home/FactorySelect/FactorySelect.tsx +++ b/components/home/FactorySelect/FactorySelect.tsx @@ -1,9 +1,12 @@ -import {FC, useEffect, useState} from "react"; +import {FC, memo, useEffect, useState} from "react"; import Select from "react-select"; import {isNonNullable} from "../../../src/utils"; import details from "../../../res/details.json"; +import styles from "./FactorySelect.module.css"; +import {EntitySpan} from "../EntitySpan/EntitySpan"; interface Props { + id: string factories: string[] onSetFactories: (factories: string[]) => void } @@ -13,7 +16,7 @@ const options = details.map(detail => ({ value: detail.href })) -export const FactorySelect: FC = ({factories, onSetFactories}) => { +const FactorySelectBase: FC = ({id, factories, onSetFactories}) => { const [state, setState] = useState([]) useEffect(() => { setState(factories @@ -21,13 +24,27 @@ export const FactorySelect: FC = ({factories, onSetFactories}) => { .filter(isNonNullable)) }, [factories]) + console.log("fddsdfd") return setNewGroupValue(e.target.value)}/> - - +
Missing factories
@@ -87,7 +64,7 @@ export const HomeComponent: FC = () => {
{ - groups.map((group, idx) => ) + Object.values(groups).sort(sortByProperty(g => g.name)).map((group) => ) }
diff --git a/components/home/Preferences/Preferences.tsx b/components/home/Preferences/Preferences.tsx new file mode 100644 index 0000000..4a8cdfe --- /dev/null +++ b/components/home/Preferences/Preferences.tsx @@ -0,0 +1,42 @@ +import {FC, useState} from "react"; +import {FactorySelect} from "../FactorySelect/FactorySelect"; +import {useGroups} from "../../contexts/GroupProvider"; + +export const Preferences: FC = () => { + const { + addGroup, + baseFactories, + setBaseFactories, + ignoredFactories, + setIgnoredFactories + } = useGroups() + const [newGroupValue, setNewGroupValue] = useState("New group") + return <> +
+ Basic Values + +
+
+ Ignored Values + +
+
+ Add new groups + setNewGroupValue(e.target.value)}/> + +
+ +} diff --git a/package.json b/package.json index e9bc079..70feb47 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "classnames": "^2.3.1", "next": "12.2.4", "pako": "^2.0.4", "react": "18.2.0", diff --git a/src/hooks/useDetails.ts b/src/hooks/useDetails.ts deleted file mode 100644 index 332baed..0000000 --- a/src/hooks/useDetails.ts +++ /dev/null @@ -1,4 +0,0 @@ -import {Entity} from "../types"; -import details from "../../res/details.json"; - -export const useDetails = () => details as Entity[] diff --git a/src/hooks/useFactories.ts b/src/hooks/useFactories.ts new file mode 100644 index 0000000..40b1550 --- /dev/null +++ b/src/hooks/useFactories.ts @@ -0,0 +1,23 @@ +import {EnrichedEntity, Entity} from "../types"; +import details from "../../res/details.json"; + +const factories = details as Entity[] + +const detailsMap = Object + .fromEntries( + factories.map((detail: EnrichedEntity) => { + detail.usedBy = factories + .filter(f => Object + .keys(f.recipe?.prerequisites ?? {}) + .includes(detail.href) + ) + return [detail.href, detail]; + }) + ) + +export const useFactories = () => ({ + factories: factories, + findFactory: (uid: string): EnrichedEntity|undefined => { + return detailsMap[uid] + } +}) diff --git a/src/types.ts b/src/types.ts index 9ad13e2..7656701 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,17 +14,11 @@ export interface Entity extends UnfetchedEntity { recipe?: Recipe } -export type Group = OldGroup|NewGroup - -export interface OldGroup { - name: string - isExported?: boolean - factories: string[] - intermediates: string[] - inputs: string[] +export interface EnrichedEntity extends Entity { + usedBy?: Entity[] } -export interface NewGroup { +export interface Group { name: string inputs: string[] intermediates: string[] diff --git a/styles/globals.css b/styles/globals.css index 4f18421..372363a 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -6,6 +6,12 @@ body { Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; } +body { + color: black; + background: #FAFAFA; + padding-inline: 2em; +} + a { color: inherit; text-decoration: none; @@ -21,6 +27,12 @@ a { } body { color: white; - background: black; + background: #111; + } +} + +@media (max-width: 1200px) { + body { + padding-inline: 0.5em; } } diff --git a/yarn.lock b/yarn.lock index fb578ba..f3de586 100644 --- a/yarn.lock +++ b/yarn.lock @@ -562,6 +562,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +classnames@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"