From 8ef10d5273a77883f89d38e05069a0c925b42f2e Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Wed, 23 Jun 2021 16:37:47 +0700 Subject: [PATCH] added fluid flow toggle --- src/components/flow/FlowStamp/index.tsx | 25 ++++++- .../flow/FlowStamp/styles.module.scss | 9 +++ src/layouts/FlowLayout/index.tsx | 21 +++++- src/layouts/FlowLayout/styles.module.scss | 73 ++++++++++--------- src/utils/hooks/usePersistedState.ts | 19 +++++ 5 files changed, 104 insertions(+), 43 deletions(-) create mode 100644 src/utils/hooks/usePersistedState.ts diff --git a/src/components/flow/FlowStamp/index.tsx b/src/components/flow/FlowStamp/index.tsx index e10071d7..66cf9227 100644 --- a/src/components/flow/FlowStamp/index.tsx +++ b/src/components/flow/FlowStamp/index.tsx @@ -1,23 +1,33 @@ -import React, { FC, useCallback, FormEvent, useMemo, KeyboardEvent } from 'react'; +import React, { FC, FormEvent, useCallback, useMemo } from 'react'; import { IFlowState } from '~/redux/flow/reducer'; import { InputText } from '~/components/input/InputText'; import { FlowRecent } from '../FlowRecent'; -import classnames from 'classnames'; import styles from './styles.module.scss'; -import * as FLOW_ACTIONS from '~/redux/flow/actions'; import { FlowSearchResults } from '../FlowSearchResults'; import { Icon } from '~/components/input/Icon'; +import { Group } from '~/components/containers/Group'; +import { Toggle } from '~/components/input/Toggle'; interface IProps { recent: IFlowState['recent']; updated: IFlowState['updated']; search: IFlowState['search']; + isFluid: boolean; onSearchChange: (text: string) => void; onLoadMore: () => void; + toggleLayout: () => void; } -const FlowStamp: FC = ({ recent, updated, search, onSearchChange, onLoadMore }) => { +const FlowStamp: FC = ({ + recent, + updated, + search, + onSearchChange, + onLoadMore, + isFluid, + toggleLayout, +}) => { const onSearchSubmit = useCallback((event: FormEvent) => { event.preventDefault(); }, []); @@ -81,6 +91,13 @@ const FlowStamp: FC = ({ recent, updated, search, onSearchChange, onLoad )} + +
+ + +
Жидкое течение
+
+
); }; diff --git a/src/components/flow/FlowStamp/styles.module.scss b/src/components/flow/FlowStamp/styles.module.scss index 4927c8f9..a9ae05b0 100644 --- a/src/components/flow/FlowStamp/styles.module.scss +++ b/src/components/flow/FlowStamp/styles.module.scss @@ -99,3 +99,12 @@ opacity: 0.7; } } + +.toggles { + padding: $gap; + font: $font_14_semibold; + + &__label { + cursor: pointer; + } +} diff --git a/src/layouts/FlowLayout/index.tsx b/src/layouts/FlowLayout/index.tsx index 6581b676..7e29c0cd 100644 --- a/src/layouts/FlowLayout/index.tsx +++ b/src/layouts/FlowLayout/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect, useMemo } from 'react'; +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import { FlowGrid } from '~/components/flow/FlowGrid'; import { selectFlow } from '~/redux/flow/selectors'; @@ -17,9 +17,17 @@ import { SidebarRouter } from '~/containers/main/SidebarRouter'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import { INode } from '~/redux/types'; import { selectLabUpdatesNodes } from '~/redux/lab/selectors'; +import { usePersistedState } from '~/utils/hooks/usePersistedState'; +import classNames from 'classnames'; + +enum Layout { + Fluid = 'fluid', + Default = 'default', +} const FlowLayout: FC = () => { const { nodes, heroes, recent, updated, is_loading, search } = useShallowSelect(selectFlow); + const [layout, setLayout] = usePersistedState('flow_layout', Layout.Default); const labUpdates = useShallowSelect(selectLabUpdatesNodes); const user = useShallowSelect(selectUser); const dispatch = useDispatch(); @@ -58,6 +66,11 @@ const FlowLayout: FC = () => { labUpdates, ]); + const isFluid = layout === Layout.Fluid; + const toggleLayout = useCallback(() => { + setLayout(isFluid ? Layout.Default : Layout.Fluid); + }, [setLayout, isFluid]); + useEffect(() => { window.addEventListener('scroll', onLoadMore); @@ -69,7 +82,7 @@ const FlowLayout: FC = () => { }, []); return ( - +
@@ -80,8 +93,10 @@ const FlowLayout: FC = () => { recent={recent} updated={cumulativeUpdates} search={search} + isFluid={isFluid} onSearchChange={onChangeSearch} onLoadMore={onLoadMoreSearch} + toggleLayout={toggleLayout} />
@@ -89,7 +104,7 @@ const FlowLayout: FC = () => {
- +
); }; diff --git a/src/layouts/FlowLayout/styles.module.scss b/src/layouts/FlowLayout/styles.module.scss index 71e24052..192b48e0 100644 --- a/src/layouts/FlowLayout/styles.module.scss +++ b/src/layouts/FlowLayout/styles.module.scss @@ -9,10 +9,29 @@ $cols: $content_width / $cell; +@mixin fluid { + @media(min-width: 640px) { + .fluid & { + @content + } + } +} + +.container { + max-width: $content_width; + width: 100%; + + &.fluid { + max-width: none; + } +} + .grid { + width: 100%; + box-sizing: border-box; display: grid; - grid-template-columns: repeat(5, 1fr); + grid-template-columns: repeat(auto-fit, minmax($cell - 5, 1fr)); grid-template-rows: 50vh $cell; grid-auto-rows: $cell; @@ -20,41 +39,14 @@ $cols: $content_width / $cell; grid-column-gap: $gap; grid-row-gap: $gap; - @include tablet { - padding: 0 $gap; + @include fluid { + grid-template-rows: $cell; } - @media (max-width: $cell * 6) { - grid-template-columns: repeat(5, 1fr); - grid-template-rows: 50vh 20vw; - grid-auto-rows: 20vw; - } - - @media (max-width: $cell * 5) { - grid-template-columns: repeat(4, 1fr); - grid-template-rows: 40vh 25vw; - grid-auto-rows: 25vw; - } - - @media (max-width: $cell * 4) { - grid-template-columns: repeat(3, 1fr); - grid-template-rows: 40vh 33vw; - grid-auto-rows: 33vw; - } - - @media (max-width: $cell * 3) { - grid-template-columns: repeat(2, 1fr); - grid-template-rows: 40vh 50vw; - grid-auto-rows: 50vw; - grid-column-gap: $gap / 2; - grid-row-gap: $gap / 2; - padding: 0 $gap / 2; - } - - @media (max-width: $cell * 2) { - grid-template-columns: repeat(2, 1fr); - grid-template-rows: 40vh 50vw; - grid-auto-rows: 50vw; + @media (max-width: ($cell + 5) * 2) { + // rework stamp, so it will be shown as smaller one on mobiles + grid-template-columns: repeat(auto-fit, minmax($cell / 1.5 - 5, 1fr)); + grid-template-rows: 50vh $cell; } } @@ -64,7 +56,7 @@ $cols: $content_width / $cell; .hero { grid-row-start: 1; - grid-row-end: span 1; + grid-row-end: 2; grid-column-start: 1; grid-column-end: -1; background: darken($content_bg, 2%); @@ -73,10 +65,19 @@ $cols: $content_width / $cell; align-items: center; justify-content: center; font: $font_24_semibold; + + @include fluid { + grid-row-end: span 2; + grid-column-end: span 4; + + @media(max-width: ($cell + 5) * 3) { + grid-column-end: -1; + } + } } .stamp { - grid-row-end: span 2; + grid-row-end: span 3; grid-column: -2 / -1; display: flex; align-items: stretch; diff --git a/src/utils/hooks/usePersistedState.ts b/src/utils/hooks/usePersistedState.ts new file mode 100644 index 00000000..45fba4fc --- /dev/null +++ b/src/utils/hooks/usePersistedState.ts @@ -0,0 +1,19 @@ +import { useEffect, useMemo, useState } from 'react'; + +export const usePersistedState = (key: string, initial: string): [string, (val: string) => any] => { + const stored = useMemo(() => { + try { + return localStorage.getItem(`vault_${key}`) || initial; + } catch (e) { + return initial; + } + }, [key]); + + const [val, setVal] = useState(stored); + + useEffect(() => { + localStorage.setItem(`vault_${key}`, val); + }, [val]); + + return [val, setVal]; +};