From e9954ba0de0743e798093bbeb7e204d7d4d0a0d6 Mon Sep 17 00:00:00 2001 From: LukBukkit Date: Mon, 4 Sep 2023 16:45:56 +0200 Subject: [PATCH] Only show the translations for a subset of selected languages --- resources/src/App.tsx | 7 +- resources/src/Data.tsx | 51 +++++++++--- resources/src/IconActionHighlight.tsx | 56 ++++++++----- resources/src/Table.tsx | 113 ++++++++++++-------------- 4 files changed, 133 insertions(+), 94 deletions(-) diff --git a/resources/src/App.tsx b/resources/src/App.tsx index d15d9ca..970128e 100644 --- a/resources/src/App.tsx +++ b/resources/src/App.tsx @@ -6,6 +6,7 @@ import { AppLoader } from './AppLoader' import { ErrorMessage } from './ErrorMessage' import { AppFooter } from './AppFooter' import { IconActions } from './IconActions' +import { SelectHighlights } from './IconActionHighlight' import 'inter-ui/inter.css' import './App.css' @@ -26,7 +27,11 @@ const GeistApp = () => { tldr translation progress - } error={}> + } + error={} + selection={} + > diff --git a/resources/src/Data.tsx b/resources/src/Data.tsx index e85c102..cddf7c0 100644 --- a/resources/src/Data.tsx +++ b/resources/src/Data.tsx @@ -1,4 +1,11 @@ -import { ReactElement, createContext, useEffect, useState, PropsWithChildren } from 'react' +import { + createContext, + PropsWithChildren, + ReactElement, + useEffect, + useState, + useTransition, +} from 'react' // https://reactjs.org/docs/faq-ajax.html // https://reactjs.org/docs/hooks-reference.html#usecontext @@ -32,13 +39,21 @@ const DataContext = createContext<{ error: string | null highlighted: Set setHighlighted: (languages: Language[]) => void -}>({ data: null, error: null, highlighted: new Set(), setHighlighted: () => {} }) - -const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: ReactElement }>) => { +}>({ + data: null, + error: null, + highlighted: new Set(['en']), + setHighlighted: () => {}, +}) + +const DataFetcher = ( + props: PropsWithChildren<{ error: ReactElement; loading: ReactElement; selection: ReactElement }>, +) => { const [error, setError] = useState(null) const [isLoaded, setIsLoaded] = useState(false) const [data, setData] = useState(null) - const [highlighted, setHighlighted] = useState>(new Set()) + const [highlighted, setHighlighted] = useState>(new Set(['en'])) + const [isPending, startTransition] = useTransition() const webStorageHighlightedKey = 'language-highlighted' // Fetch the data.json file @@ -53,12 +68,16 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re }) .then( (result) => { - setData(result as TranslationData) - setIsLoaded(true) + startTransition(() => { + setData(result as TranslationData) + setIsLoaded(true) + }) }, (error) => { - setError(error.toString()) - setIsLoaded(true) + startTransition(() => { + setError(error.toString()) + setIsLoaded(true) + }) }, ) }, []) @@ -68,14 +87,16 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re useEffect(() => { const stored = localStorage.getItem(webStorageHighlightedKey) if (stored) { - setHighlighted(new Set(JSON.parse(stored))) + let languages: Set = new Set(JSON.parse(stored)) + languages.add('en') + startTransition(() => setHighlighted(new Set(languages))) } }, []) const setHighlightedExternal = (languages: Language[]) => { // Convert the array to a set for better performance and broadcast the change // See: https://stackoverflow.com/a/57277566/4106848 - setHighlighted(new Set(languages)) + startTransition(() => setHighlighted(new Set(languages))) // Persist the user selection of highlighted columns localStorage.setItem(webStorageHighlightedKey, JSON.stringify(Array.from(languages))) } @@ -90,7 +111,7 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re return {props.error} } - if (!isLoaded) { + if (!isLoaded || isPending) { return props.loading } @@ -100,7 +121,11 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re highlighted: highlighted, setHighlighted: setHighlightedExternal, } - return {props.children} + return ( + + {highlighted.size < 2 ? props.selection : props.children} + + ) } export { DataFetcher, DataContext, TranslationStatus } diff --git a/resources/src/IconActionHighlight.tsx b/resources/src/IconActionHighlight.tsx index 44a05ed..f3bd3e8 100644 --- a/resources/src/IconActionHighlight.tsx +++ b/resources/src/IconActionHighlight.tsx @@ -1,6 +1,6 @@ -import { useContext, useState, useEffect } from 'react' -import { Checkbox, Grid, Modal, Tooltip, useModal } from '@geist-ui/core' -import { Bookmark } from '@geist-ui/icons' +import { useContext, useState } from 'react' +import { Button, Checkbox, Grid, Modal, Text, Tooltip, useModal } from '@geist-ui/core' +import { MessageCircle, Save } from '@geist-ui/icons' import { useEscClose } from './useEscClose' import { DataContext, Language } from './Data' @@ -12,7 +12,9 @@ const HighlightCheckboxes = (props: { const checkboxes = data!.languages.map((language) => ( - {language} + + {language} + )) @@ -25,6 +27,28 @@ const HighlightCheckboxes = (props: { ) } +const SelectHighlights = (props: {}) => { + // Columns which are being highlighted + const { highlighted, setHighlighted } = useContext(DataContext) + // We only want to apply the highlight selection once it has been confirmed, so we create a temporary store + const [tmpHighlighted, setTmpHighlighted] = useState(Array.from(highlighted)) + + return ( + <> + Select languages to show +
+ setTmpHighlighted(selected)} + /> +
+ + + ) +} + const IconActionHighlight = (props: { side?: boolean }) => { const { visible, setVisible, bindings } = useModal() useEscClose(visible, setVisible) @@ -33,23 +57,13 @@ const IconActionHighlight = (props: { side?: boolean }) => { const { highlighted, setHighlighted } = useContext(DataContext) // We only want to apply the highlight selection once it has been confirmed, so we create a temporary store const [tmpHighlighted, setTmpHighlighted] = useState(Array.from(highlighted)) - // An extra state to begin saving the selection and applying it to the table - const [saving, setSaving] = useState(false) - - useEffect(() => { - if (saving) { - setHighlighted(tmpHighlighted) - setVisible(false) - setSaving(false) - } - }, [saving, setHighlighted, setVisible, tmpHighlighted]) const placement = props.side ? 'left' : 'top' return ( <> - - + { @@ -60,8 +74,8 @@ const IconActionHighlight = (props: { side?: boolean }) => { /> - Highlight columns - Choose columns to highlight + Select Languages + Choose languages to display { { - setSaving(true) + setVisible(false) + setHighlighted(tmpHighlighted) }} - loading={saving} > Save @@ -89,4 +103,4 @@ const IconActionHighlight = (props: { side?: boolean }) => { ) } -export { IconActionHighlight } +export { IconActionHighlight, SelectHighlights } diff --git a/resources/src/Table.tsx b/resources/src/Table.tsx index f3f7358..f0d794a 100644 --- a/resources/src/Table.tsx +++ b/resources/src/Table.tsx @@ -15,21 +15,20 @@ const DataTableHeader = () => { // We're applying the sticky class to each , because Chrome does not support sticky on and // https://bugs.chromium.org/p/chromium/issues/detail?id=702927 - const languageRows = data?.languages.map((lang) => { - let classNames = 'vertical-padding sticky bg-white-transparent' - if (lang.length > 3) { - classNames += ' small-font' - } - if (highlighted.has(lang)) { - classNames += ' highlighted' - } + const languageRows = data?.languages + .filter((lang) => highlighted.has(lang)) + .map((lang) => { + let classNames = 'vertical-padding sticky bg-white-transparent' + if (lang.length > 3) { + classNames += ' small-font' + } - return ( - - {lang} - - ) - }) + return ( + + {lang} + + ) + }) return ( @@ -58,19 +57,17 @@ const DataTableOSHeader = (props: { os: OperatingSystem }) => { const { data, highlighted } = useContext(DataContext) const osProgress = data!.entries[props.os].progress - const percentages = data!.languages.map((lang) => { - let classNames = 'vertical-padding small-font' + const percentages = data!.languages + .filter((lang) => highlighted.has(lang)) + .map((lang) => { + let classNames = 'vertical-padding small-font' - if (highlighted.has(lang)) { - classNames += ' highlighted' - } - - return ( - - {osProgress[lang]}% - - ) - }) + return ( + + {osProgress[lang]}% + + ) + }) return ( @@ -105,41 +102,39 @@ const DataTableOSPageRow = (props: { os: OperatingSystem; pageName: string }) => } // Pick symbols from: https://rsms.me/inter/#charset - const cells = data!.languages.map((lang) => { - let classNames = 'cursor-pointer' - let onClick = null - let symbol = '?' - - if (lang in pageData.status) { - const status = pageData.status[lang] - switch (status) { - case TranslationStatus.Translated: - classNames += ' background-green' - onClick = () => handleClick(FileAction.View, lang) - symbol = '✓' - break - case TranslationStatus.Outdated: - classNames += ' background-yellow' - onClick = () => handleClick(FileAction.View, lang) - symbol = '◇' - break + const cells = data!.languages + .filter((lang) => highlighted.has(lang)) + .map((lang) => { + let classNames = 'cursor-pointer' + let onClick = null + let symbol = '?' + + if (lang in pageData.status) { + const status = pageData.status[lang] + switch (status) { + case TranslationStatus.Translated: + classNames += ' background-green' + onClick = () => handleClick(FileAction.View, lang) + symbol = '✓' + break + case TranslationStatus.Outdated: + classNames += ' background-yellow' + onClick = () => handleClick(FileAction.View, lang) + symbol = '◇' + break + } + } else { + classNames += ' background-red' + onClick = () => handleClick(FileAction.Create, lang) + symbol = '✗' } - } else { - classNames += ' background-red' - onClick = () => handleClick(FileAction.Create, lang) - symbol = '✗' - } - - if (highlighted.has(lang)) { - classNames += ' highlighted' - } - return ( - - {symbol} - - ) - }) + return ( + + {symbol} + + ) + }) return (