diff --git a/src/components/config/theme.ts b/src/components/config/theme.ts index 2c3b10e7..1c7f633d 100644 --- a/src/components/config/theme.ts +++ b/src/components/config/theme.ts @@ -65,35 +65,15 @@ const customTheme = createMuiTheme({ palette: { type: 'dark', primary: { - // TODO: rm when dialed in - // OLD - // light: '#66ab9d', - // main: '#409685', - // dark: '#2c695d', - // NEW light: '#62aca0', main: '#379587', dark: '#286a61', contrastText: '#fff', }, secondary: { - // TODO: rm when dialed in - // light: '#2f86c1', - // main: '#016aa3', - // dark: '#014971', - // light: '#6386b0', - // main: '#325b93', - // dark: '#2e456b', - // light: '#20c5e0', - // main: '#139fb4', - // dark: '#0d7d8c', - // OPTION 3 light: '#55a9c1', main: '#207d96', dark: '#2d6777', - // light: '#56abc2', - // main: '#207d96', - // dark: '#2d6676', contrastText: '#fff', }, }, @@ -110,6 +90,15 @@ const customTheme = createMuiTheme({ // Global overrides of MUI components that need to be re-styled often customTheme.overrides = { + MuiFormHelperText: { + root: { + fontSize: '0.65rem', + }, + contained: { + marginLeft: '0.75rem', + marginRight: 0, + }, + }, MuiInput: { root: { fontSize: customTheme.typography.body2.fontSize, // default inputs: huge @@ -121,6 +110,11 @@ customTheme.overrides = { }, }, }, + MuiInputAdornment: { + root: { + color: customTheme.palette.text.secondary, + }, + }, MuiDialog: { // Outside boundary of all dialogs paper: { @@ -146,6 +140,13 @@ customTheme.overrides = { color: customTheme.palette.secondary.light, }, }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + MuiAutocomplete: { + root: { + marginBottom: '1rem', + }, + }, } export const theme = responsiveFontSizes(customTheme) diff --git a/src/components/context/MapToolsContext.tsx b/src/components/context/MapToolsContext.tsx index 03eaa3f2..209d1e1b 100644 --- a/src/components/context/MapToolsContext.tsx +++ b/src/components/context/MapToolsContext.tsx @@ -3,6 +3,7 @@ import React, { FC } from 'react' import * as Types from './types' const initialState = { + autoZoomCensus: true, boundariesVisible: false, geolocActive: false, } as Types.InitialMapToolsState @@ -19,6 +20,8 @@ function reducer( action: Types.MapToolsAction ) { switch (action.type) { + case 'TOGGLE_CENSUS_AUTO_ZOOM': + return { ...state, autoZoomCensus: !state.autoZoomCensus } case 'SET_BOUNDARIES_VISIBLE': return { ...state, boundariesVisible: action.payload } case 'SET_GEOLOC_ACTIVE': diff --git a/src/components/context/SymbAndLabelContext.tsx b/src/components/context/SymbAndLabelContext.tsx index af88237e..bb2ae4a2 100644 --- a/src/components/context/SymbAndLabelContext.tsx +++ b/src/components/context/SymbAndLabelContext.tsx @@ -4,20 +4,24 @@ import { LangSchemaCol } from './types' type Dispatch = React.Dispatch type InitialState = { - activeLabelID: LangSchemaCol | '' | 'None' + activeLabelID: LangSchemaCol activeSymbGroupID: LangSchemaCol hideLangPoints: boolean + hideLangLabels: boolean } export type Action = - | { type: 'TOGGLE_LANG_POINTS' } - | { type: 'SET_LANG_LAYER_LABELS'; payload: LangSchemaCol | '' } + | { type: 'TOGGLE_LANG_LABELS'; payload?: boolean } + | { type: 'TOGGLE_LANG_POINTS'; payload?: boolean } + | { type: 'SET_LANG_LAYER_LABELS'; payload: LangSchemaCol } | { type: 'SET_LANG_LAYER_SYMBOLOGY'; payload: LangSchemaCol } const initialState = { - activeLabelID: '', + activeLabelID: 'Language', activeSymbGroupID: 'World Region', + autoZoomCensus: true, hideLangPoints: false, + hideLangLabels: true, } as InitialState const SymbAndLabelContext = React.createContext( @@ -32,18 +36,23 @@ function reducer(state: InitialState, action: Action) { case 'TOGGLE_LANG_POINTS': return { ...state, - hideLangPoints: !state.hideLangPoints, + hideLangPoints: + // `undefined` if from individual checkboxes (as opposed to "all", + // e.g. from Census panel) + action.payload === undefined ? !state.hideLangPoints : action.payload, } - case 'SET_LANG_LAYER_LABELS': + case 'TOGGLE_LANG_LABELS': return { ...state, - activeLabelID: action.payload, + hideLangLabels: + // `undefined` if from individual checkboxes (as opposed to "all", + // e.g. from Census panel) + action.payload === undefined ? !state.hideLangLabels : action.payload, } + case 'SET_LANG_LAYER_LABELS': + return { ...state, activeLabelID: action.payload } case 'SET_LANG_LAYER_SYMBOLOGY': - return { - ...state, - activeSymbGroupID: action.payload, - } + return { ...state, activeSymbGroupID: action.payload } default: { return state } diff --git a/src/components/context/types.ts b/src/components/context/types.ts index 964ae06e..57956dd0 100644 --- a/src/components/context/types.ts +++ b/src/components/context/types.ts @@ -59,6 +59,8 @@ export type LangLevelOptional = CensusFields & addlNeighborhoods: string[] // suuuper shakes mcgee Audio: string descriptionID: string + // Used in "Language Profile" (aka Pre-Details), not Details + langProfileDescripID: string Glottocode: string Macrocommunity: string[] Neighborhood: string[] @@ -95,19 +97,18 @@ type CensusFieldPayload = { } export type InitialMapToolsState = { + autoZoomCensus: boolean boundariesVisible: boolean geolocActive: boolean censusActiveField?: CensusFieldPayload } export type MapToolsAction = + | { type: 'CLEAR_CENSUS_FIELD' } | { type: 'SET_BOUNDARIES_VISIBLE'; payload: boolean } + | { type: 'SET_CENSUS_FIELD'; payload: CensusFieldPayload } | { type: 'SET_GEOLOC_ACTIVE'; payload: boolean } - | { type: 'CLEAR_CENSUS_FIELD' } - | { - type: 'SET_CENSUS_FIELD' - payload: CensusFieldPayload - } + | { type: 'TOGGLE_CENSUS_AUTO_ZOOM' } export type MapToolsDispatch = React.Dispatch diff --git a/src/components/details/DetailedIntro.tsx b/src/components/details/DetailedIntro.tsx index 97fc5cd9..d01eb99f 100644 --- a/src/components/details/DetailedIntro.tsx +++ b/src/components/details/DetailedIntro.tsx @@ -1,11 +1,13 @@ import React, { FC } from 'react' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' +import { Route } from 'react-router-dom' import { Media } from 'components/media' import { MoreLikeThis } from 'components/details' import { CensusPopover } from 'components/explore/CensusPopover' import { StatsAndMeta } from 'components/explore/StatsAndMeta' -import { FancyHorizRule } from 'components/generic' +import { LangProfileDescrip } from 'components/explore' +import { routes } from 'components/config/api' import { LangOrEndoIntro } from './LangOrEndoIntro' import { DetailedIntroProps } from './types' import { ReadMoreLangDescrip } from './ReadMoreLangDescrip' @@ -14,11 +16,14 @@ const useStyles = makeStyles((theme: Theme) => createStyles({ root: { textAlign: 'center', + paddingBottom: '0.75rem', + marginBottom: '1rem', + borderBottom: `solid 1px ${theme.palette.divider}`, }, }) ) -// The intro section of pre-Details and Details views +// The intro section of pre-Details ("Language Profile") and Details views export const DetailedIntro: FC = (props) => { const { data, shareNoun, isInstance, langDescripID } = props const classes = useStyles() @@ -32,7 +37,13 @@ export const DetailedIntro: FC = (props) => { {langDescripID && } - + + {data.langProfileDescripID && ( + + )} + ) } diff --git a/src/components/details/NeighborhoodList.tsx b/src/components/details/NeighborhoodList.tsx index 961f7bef..f2facd20 100644 --- a/src/components/details/NeighborhoodList.tsx +++ b/src/components/details/NeighborhoodList.tsx @@ -110,6 +110,7 @@ export const NeighborhoodList: FC = (props) => { let url // TODO: de-shabbify, wire up w/Town const footer = + // TODO: use Routes, dude if (!isInstance) url = `/details/${instanceIDs ? instanceIDs[i] : 999999}` else url = `/Explore/${locRouteName}/${loc}` diff --git a/src/components/explore/CustomCard.tsx b/src/components/explore/CustomCard.tsx index 7ce96d6a..0a259f3c 100644 --- a/src/components/explore/CustomCard.tsx +++ b/src/components/explore/CustomCard.tsx @@ -93,7 +93,7 @@ export const GlottoIsoFooter: FC = (props) => { } export const CustomCard: FC = (props) => { - const { title, url, uniqueInstances, intro, icon, footer, footerIcon } = props + const { title, url, uniqueInstances, intro, icon, footer } = props const classes = useStyles() return ( @@ -126,7 +126,6 @@ export const CustomCard: FC = (props) => { variant="caption" className={classes.footer} > - {footerIcon} {footer || (uniqueInstances !== undefined && utils.prettyTruncate(uniqueInstances as string[]))} diff --git a/src/components/explore/LangProfileDescrip.tsx b/src/components/explore/LangProfileDescrip.tsx new file mode 100644 index 00000000..c16c6e99 --- /dev/null +++ b/src/components/explore/LangProfileDescrip.tsx @@ -0,0 +1,21 @@ +import React, { FC } from 'react' + +import { ReadMore } from 'components/generic' +import { useAirtable } from './hooks' + +export const LangProfileDescrip: FC<{ langProfileDescripID: string }> = ( + props +) => { + const { langProfileDescripID } = props + + const { data, error, isLoading } = useAirtable<{ Description: string }>( + // TODO: TS-ify the names of all tables + 'Language Profiles', + // TODO: TS-ify the field names for all tables + { filterByFormula: `{id} = '${langProfileDescripID}'` } + ) + + if (isLoading || error || !data.length) return null + + return +} diff --git a/src/components/explore/MidLevelExplore.tsx b/src/components/explore/MidLevelExplore.tsx index 0f4e104c..965516c9 100644 --- a/src/components/explore/MidLevelExplore.tsx +++ b/src/components/explore/MidLevelExplore.tsx @@ -5,7 +5,6 @@ import { Route, Link as RouterLink, } from 'react-router-dom' -import { BiMapPin } from 'react-icons/bi' import { FlagFromHook } from 'components/generic/icons-and-swatches' import { SwatchOnly } from 'components/legend' @@ -87,9 +86,6 @@ export const MidLevelExplore: FC = (props) => { ) } - const footerIcon = (value !== undefined || tableName === 'Language') && ( - - ) const { definition, plural } = landingData[0] || {} let primaryData @@ -131,7 +127,6 @@ export const MidLevelExplore: FC = (props) => { key={nameOrLang} intro={value || field === 'Language' ? nameOrLang : ''} title={tableName === 'Language' ? row.Endonym : nameOrLang} - footerIcon={footerIcon} uniqueInstances={uniqueInstances} url={`${url}/${nameOrLang}`} // TODO: use and refactor SwatchOrFlagOrIcon for icon prop diff --git a/src/components/explore/index.ts b/src/components/explore/index.ts index 47cd6803..79a2f30a 100644 --- a/src/components/explore/index.ts +++ b/src/components/explore/index.ts @@ -3,6 +3,7 @@ export * from './CardList' export * from './CustomCard' export * from './Explore' export * from './LangCardsList' +export * from './LangProfileDescrip' export * from './MidLevelExplore' export * from './config' diff --git a/src/components/explore/types.ts b/src/components/explore/types.ts index 099dbb3e..1261c958 100644 --- a/src/components/explore/types.ts +++ b/src/components/explore/types.ts @@ -13,7 +13,7 @@ export type ReactQueryOptions = { } // TODO: try to reuse some of these, they're pretty common in sev. components -type CategoryProps = { +export type CustomCardProps = { title: string uniqueInstances?: unknown[] url: string @@ -22,10 +22,6 @@ type CategoryProps = { intro?: string } -export type CustomCardProps = CategoryProps & { - footerIcon?: React.ReactNode -} - export type CategoryConfig = { name: keyof InstanceLevelSchema definition?: string @@ -41,7 +37,7 @@ export type RouteMatch = { language?: string } -export type CardConfig = CategoryProps & { +export type CardConfig = CustomCardProps & { footer: string to: string } diff --git a/src/components/generic/ReadMore.tsx b/src/components/generic/ReadMore.tsx index 93264039..c72efe89 100644 --- a/src/components/generic/ReadMore.tsx +++ b/src/components/generic/ReadMore.tsx @@ -50,7 +50,8 @@ const useStyles = makeStyles((theme: Theme) => description: { textAlign: 'left', fontSize: (props: ReadMoreStyles) => props.fontSize, - marginBottom: '0.25rem', + marginBottom: '0.75rem', + lineHeight: 1.75, }, }) ) diff --git a/src/components/generic/ToggleableSection.tsx b/src/components/generic/ToggleableSection.tsx index d9102046..52f8b58c 100644 --- a/src/components/generic/ToggleableSection.tsx +++ b/src/components/generic/ToggleableSection.tsx @@ -25,6 +25,9 @@ const useStyles = makeStyles(() => }) ) +// TODO: consider ellipsizing with `line-clamp`: +// https://css-tricks.com/multiline-truncated-text-with-show-more-button/ +// https://css-tricks.com/almanac/properties/l/line-clamp/ export const ToggleableSection: FC = (props) => { const { children, show, initialHeight = 0 } = props const classes = useStyles({ show, initialHeight }) diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 4456f35e..db7395c0 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -1,15 +1,13 @@ import React, { FC } from 'react' -import { GoSearch } from 'react-icons/go' import { LegendPanel } from 'components/legend' -import { PanelContentSimple, PanelHeading } from 'components/panels' +import { PanelContentSimple } from 'components/panels' import { SearchByOmnibox } from './SearchByOmnibox' import { FiltersWarning } from './FiltersWarning' export const Home: FC = () => { return ( - } text="Search language communities" /> diff --git a/src/components/home/OmniboxResult.tsx b/src/components/home/OmniboxResult.tsx index 9a7db7e1..0e00119e 100644 --- a/src/components/home/OmniboxResult.tsx +++ b/src/components/home/OmniboxResult.tsx @@ -1,7 +1,6 @@ import React, { FC } from 'react' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { Typography, Box } from '@material-ui/core' -import { FaStreetView } from 'react-icons/fa' import { PreppedAutocompleteGroup } from './types' @@ -16,17 +15,10 @@ const useStyles = makeStyles((theme: Theme) => fontSize: '0.8rem', }, resultHeading: { - alignItems: 'center', - color: theme.palette.secondary.main, - display: 'flex', + color: theme.palette.secondary.light, fontSize: '1em', lineHeight: 1, marginTop: 4, - '& svg': { - color: theme.palette.secondary.main, - flexShrink: 0, // otherwise squished next to super-long headings - marginRight: 3, - }, }, // The footer details: { @@ -72,7 +64,6 @@ export const OmniboxResult: FC<{ data: PreppedAutocompleteGroup }> = ( return ( - {data.location} diff --git a/src/components/home/SearchByOmnibox.tsx b/src/components/home/SearchByOmnibox.tsx index a473f175..daf00336 100644 --- a/src/components/home/SearchByOmnibox.tsx +++ b/src/components/home/SearchByOmnibox.tsx @@ -2,9 +2,10 @@ import React, { FC } from 'react' import { useHistory } from 'react-router-dom' import matchSorter from 'match-sorter' import Autocomplete from '@material-ui/lab/Autocomplete' -import { TextField } from '@material-ui/core' +import { TextField, InputAdornment } from '@material-ui/core' import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' import { MdClose } from 'react-icons/md' +import { GoSearch } from 'react-icons/go' import { routes } from 'components/config/api' import { useAirtable } from 'components/explore/hooks' @@ -19,9 +20,6 @@ import { PreppedAutocompleteGroup } from './types' // ...to make sure it fits on iPhone? const useStyles = makeStyles((theme: Theme) => createStyles({ - root: { - marginBottom: '1.25rem', - }, paper: { // Stands out against panels behind it backgroundColor: theme.palette.background.default, @@ -36,9 +34,9 @@ const useStyles = makeStyles((theme: Theme) => borderBottom: `1px solid ${theme.palette.text.hint}`, color: theme.palette.text.primary, fontFamily: theme.typography.h1.fontFamily, - fontSize: '1.1rem', - paddingLeft: 12, + fontSize: '1.25rem', fontWeight: theme.typography.h1.fontWeight, + paddingLeft: 12, }, }, // The
  • items. Not sure why it works via classes and `groupUl` doesn't. @@ -47,9 +45,12 @@ const useStyles = makeStyles((theme: Theme) => display: 'flex', paddingLeft: 12, }, - inputRoot: { - // Decrease placeholder font size but prevent unwanted iOS zoom on focus - '& input:not(:focus)': { fontSize: '0.8em' }, + input: { + // Make text more opaque than the 0.5 default + // CRED: https://stackoverflow.com/a/48545561/1048518 + '&::placeholder': { + opacity: 0.85, + }, }, }) ) @@ -113,13 +114,26 @@ export const SearchByOmnibox: FC = (props) => { React.HTMLAttributes > } - renderInput={(params) => ( - - )} + renderInput={(params) => { + // eslint-disable-next-line no-param-reassign + params.InputProps.startAdornment = ( + <> + + + + {params.InputProps.startAdornment} + + ) + + return ( + + ) + }} /> ) } diff --git a/src/components/home/utils.tsx b/src/components/home/utils.tsx index 5dd9c747..dc7f05d5 100644 --- a/src/components/home/utils.tsx +++ b/src/components/home/utils.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { Link as RouterLink } from 'react-router-dom' import { VariableSizeList } from 'react-window' import { AutocompleteRenderGroupParams } from '@material-ui/lab/Autocomplete' import ListSubheader from '@material-ui/core/ListSubheader' @@ -24,8 +25,12 @@ export function useResetCache( // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export const renderGroup = (params: AutocompleteRenderGroupParams) => { return [ - - {params.group} + // Could NOT get it to style properly if setting component to anything but + // "div" and could not make it stay sticky either. Frustration station. + + + {params.group} + , params.children, ] diff --git a/src/components/legend/LangPointsToggle.tsx b/src/components/legend/LangPointsToggle.tsx index 1c379566..b048d869 100644 --- a/src/components/legend/LangPointsToggle.tsx +++ b/src/components/legend/LangPointsToggle.tsx @@ -3,7 +3,15 @@ import React, { FC } from 'react' import { FormControlLabel, Switch } from '@material-ui/core' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' -import { useLabelAndSymbDispatch } from 'components/context/SymbAndLabelContext' +import { + useLabelAndSymbDispatch, + useSymbAndLabelState, +} from 'components/context/SymbAndLabelContext' + +type CustomFormControlProps = { + label: string + switchControl: React.ReactElement +} const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -21,29 +29,88 @@ const useStyles = makeStyles((theme: Theme) => }) ) -// TODO: abstract and reuse for labels -export const LangPointsToggle: FC<{ checked: boolean }> = (props) => { - const { checked } = props +export const CustomFormControl: FC = (props) => { + const { label, switchControl } = props const classes = useStyles() const { smallerText, root } = classes + + return ( + + ) +} + +export const LangPointsToggle: FC = (props) => { + const { hideLangPoints } = useSymbAndLabelState() const symbLabelDispatch = useLabelAndSymbDispatch() - const handleBoundariesToggle = () => { + const handleChange = (event: React.ChangeEvent) => { symbLabelDispatch({ type: 'TOGGLE_LANG_POINTS' }) } return ( - + } + /> + ) +} + +export const LangLabelsToggle: FC = (props) => { + const { hideLangLabels } = useSymbAndLabelState() + const symbLabelDispatch = useLabelAndSymbDispatch() + + const handleChange = (event: React.ChangeEvent) => { + symbLabelDispatch({ type: 'TOGGLE_LANG_LABELS' }) + } + + return ( + + } + /> + ) +} + +export const AllLangDataToggle: FC = () => { + const { hideLangLabels, hideLangPoints } = useSymbAndLabelState() + const symbLabelDispatch = useLabelAndSymbDispatch() + + const handleChange = (event: React.ChangeEvent) => { + const payload = event.target.checked + + symbLabelDispatch({ type: 'TOGGLE_LANG_POINTS', payload }) + symbLabelDispatch({ type: 'TOGGLE_LANG_LABELS', payload }) + } + + return ( + } - label="Hide symbols" /> ) } diff --git a/src/components/legend/LayerLabelSelect.tsx b/src/components/legend/LayerLabelSelect.tsx index e8ef1074..db0151e7 100644 --- a/src/components/legend/LayerLabelSelect.tsx +++ b/src/components/legend/LayerLabelSelect.tsx @@ -28,7 +28,6 @@ export const LayerLabelSelect: FC = () => { onChange={handleChange} inputProps={{ name: 'label', id: 'lang-label-TextField' }} > - {labelFields.map((label: string) => (