From c731bb9ad087a83de491b5331239b111e94455ef Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Sun, 29 Nov 2020 15:43:30 +0700 Subject: [PATCH 1/5] added promoted switch button --- .../editors/EditorImageUploadButton/index.tsx | 1 - .../editors/EditorPanel/styles.module.scss | 3 +- .../editors/EditorPublicSwitch/index.tsx | 49 +++++++++++++++ .../EditorPublicSwitch/styles.module.scss | 63 +++++++++++++++++++ .../editors/EditorUploadButton/index.tsx | 25 ++------ .../EditorUploadButton/styles.module.scss | 9 +-- src/redux/node/constants.ts | 15 ++++- src/redux/types.ts | 2 + src/sprites/Sprites.tsx | 10 +++ src/styles/variables.scss | 10 +++ 10 files changed, 153 insertions(+), 34 deletions(-) create mode 100644 src/components/editors/EditorPublicSwitch/index.tsx create mode 100644 src/components/editors/EditorPublicSwitch/styles.module.scss diff --git a/src/components/editors/EditorImageUploadButton/index.tsx b/src/components/editors/EditorImageUploadButton/index.tsx index 68504973..f333cf9d 100644 --- a/src/components/editors/EditorImageUploadButton/index.tsx +++ b/src/components/editors/EditorImageUploadButton/index.tsx @@ -1,6 +1,5 @@ import React, { FC } from 'react'; import { EditorUploadButton } from '~/components/editors/EditorUploadButton'; -import { INode } from '~/redux/types'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; import { IEditorComponentProps } from '~/redux/node/types'; diff --git a/src/components/editors/EditorPanel/styles.module.scss b/src/components/editors/EditorPanel/styles.module.scss index a58338b2..5bda9666 100644 --- a/src/components/editors/EditorPanel/styles.module.scss +++ b/src/components/editors/EditorPanel/styles.module.scss @@ -13,11 +13,12 @@ flex-direction: row; & > * { - margin: 0 $gap; + margin: 0 $gap / 2; &:first-child { margin-left: 0; } + &:last-child { margin-right: 0; } diff --git a/src/components/editors/EditorPublicSwitch/index.tsx b/src/components/editors/EditorPublicSwitch/index.tsx new file mode 100644 index 00000000..d8f9f889 --- /dev/null +++ b/src/components/editors/EditorPublicSwitch/index.tsx @@ -0,0 +1,49 @@ +import React, { FC, useCallback, useEffect, useRef } from 'react'; +import styles from './styles.module.scss'; +import { Icon } from '~/components/input/Icon'; +import { IEditorComponentProps } from '~/redux/node/types'; +import classNames from 'classnames'; +import { usePopper } from 'react-popper'; + +interface IProps extends IEditorComponentProps {} + +const EditorPublicSwitch: FC = ({ data, setData }) => { + const tooltip = useRef(null); + const onChange = useCallback(() => setData({ ...data, is_promoted: !data.is_promoted }), [ + data, + setData, + ]); + + const pop = usePopper(tooltip?.current?.parentElement, tooltip.current, { + placement: 'bottom', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 4], + }, + }, + ], + }); + + useEffect(() => console.log(pop), [pop]); + + return ( +
+
+ {data.is_promoted + ? 'Доступно всем на главной странице' + : 'Видно только сотрудникам в лаборатории'} +
+ +
+ {data.is_promoted ? : } +
+
+ ); +}; + +export { EditorPublicSwitch }; diff --git a/src/components/editors/EditorPublicSwitch/styles.module.scss b/src/components/editors/EditorPublicSwitch/styles.module.scss new file mode 100644 index 00000000..e41682c0 --- /dev/null +++ b/src/components/editors/EditorPublicSwitch/styles.module.scss @@ -0,0 +1,63 @@ +@import "src/styles/variables"; + +.wrap { + @include outer_shadow(); + @include editor_round_button(); + + transition: all 0.5s; + fill: $content_bg; + background-color: $olive; + + &.promoted { + background-color: lighten($content_bg, 4%); + fill: $red; + } + + &:hover { + opacity: 1; + + .tooltip { + opacity: 1; + } + } + + input { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + opacity: 0; + z-index: 1; + cursor: pointer; + } +} + +.icon { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.tooltip { + border-radius: 2px; + background: darken($content_bg, 6%); + padding: $gap; + position: absolute; + font: $font_12_semibold; + bottom: 100%; + right: 0; + transform: translate(0, -$gap); + text-align: center; + touch-action: none; + pointer-events: none; + opacity: 0; + width: 100px; + user-select: none; + transition: all 0.1s; +} diff --git a/src/components/editors/EditorUploadButton/index.tsx b/src/components/editors/EditorUploadButton/index.tsx index 34e90a44..03fda22f 100644 --- a/src/components/editors/EditorUploadButton/index.tsx +++ b/src/components/editors/EditorUploadButton/index.tsx @@ -1,15 +1,15 @@ import React, { FC, useCallback, useEffect } from 'react'; import styles from './styles.module.scss'; import { Icon } from '~/components/input/Icon'; -import { IFileWithUUID, INode, IFile } from '~/redux/types'; +import { IFile, IFileWithUUID } from '~/redux/types'; import uuid from 'uuid4'; import { UPLOAD_SUBJECTS, UPLOAD_TARGETS, UPLOAD_TYPES } from '~/redux/uploads/constants'; import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; -import { assocPath } from 'ramda'; -import { append } from 'ramda'; +import { append, assocPath } from 'ramda'; import { selectUploads } from '~/redux/uploads/selectors'; import { connect } from 'react-redux'; import { NODE_SETTINGS } from '~/redux/node/constants'; +import { IEditorComponentProps } from '~/redux/node/types'; const mapStateToProps = state => { const { statuses, files } = selectUploads(state); @@ -22,12 +22,7 @@ const mapDispatchToProps = { }; type IProps = ReturnType & - typeof mapDispatchToProps & { - data: INode; - setData: (val: INode) => void; - temp: string[]; - setTemp: (val: string[]) => void; - + typeof mapDispatchToProps & IEditorComponentProps & { accept?: string; icon?: string; type?: typeof UPLOAD_TYPES[keyof typeof UPLOAD_TYPES]; @@ -79,18 +74,6 @@ const EditorUploadButtonUnconnected: FC = ({ [data, setData] ); - // const onDrop = useCallback( - // (event: React.DragEvent) => { - // event.preventDefault(); - - // if (!event.dataTransfer || !event.dataTransfer.files || !event.dataTransfer.files.length) - // return; - - // onUpload(Array.from(event.dataTransfer.files)); - // }, - // [onUpload] - // ); - useEffect(() => { window.addEventListener('dragover', eventPreventer, false); window.addEventListener('drop', eventPreventer, false); diff --git a/src/components/editors/EditorUploadButton/styles.module.scss b/src/components/editors/EditorUploadButton/styles.module.scss index 3ba89ad9..3347fd46 100644 --- a/src/components/editors/EditorUploadButton/styles.module.scss +++ b/src/components/editors/EditorUploadButton/styles.module.scss @@ -2,17 +2,10 @@ .wrap { @include outer_shadow(); + @include editor_round_button(); - width: $upload_button_height; - height: $upload_button_height; - border-radius: ($upload_button_height / 2) !important; - position: relative; - border-radius: $radius; - cursor: pointer; - // opacity: 0.7; transition: opacity 0.5s; background: $red_gradient; - // box-shadow: $content_bg 0 0 5px 10px; &:hover { opacity: 1; diff --git a/src/redux/node/constants.ts b/src/redux/node/constants.ts index 9d3da6af..30df9a77 100644 --- a/src/redux/node/constants.ts +++ b/src/redux/node/constants.ts @@ -15,6 +15,7 @@ import { EditorUploadCoverButton } from '~/components/editors/EditorUploadCoverB import { modalShowPhotoswipe } from '../modal/actions'; import { IEditorComponentProps } from '~/redux/node/types'; import { EditorFiller } from '~/components/editors/EditorFiller'; +import { EditorPublicSwitch } from '~/components/editors/EditorPublicSwitch'; const prefix = 'NODE.'; export const NODE_ACTIONS = { @@ -63,6 +64,8 @@ export const EMPTY_NODE: INode = { blocks: [], tags: [], + is_public: true, + is_promoted: true, flow: { display: 'single', @@ -120,14 +123,20 @@ export const NODE_EDITORS = { }; export const NODE_PANEL_COMPONENTS: Record[]> = { - [NODE_TYPES.TEXT]: [EditorFiller, EditorUploadCoverButton], - [NODE_TYPES.VIDEO]: [EditorFiller, EditorUploadCoverButton], - [NODE_TYPES.IMAGE]: [EditorImageUploadButton, EditorFiller, EditorUploadCoverButton], + [NODE_TYPES.TEXT]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch], + [NODE_TYPES.VIDEO]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch], + [NODE_TYPES.IMAGE]: [ + EditorImageUploadButton, + EditorFiller, + EditorUploadCoverButton, + EditorPublicSwitch, + ], [NODE_TYPES.AUDIO]: [ EditorAudioUploadButton, EditorImageUploadButton, EditorFiller, EditorUploadCoverButton, + EditorPublicSwitch, ], }; diff --git a/src/redux/types.ts b/src/redux/types.ts index dd5b95b4..b9beecaf 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -124,6 +124,8 @@ export interface INode { description?: string; is_liked?: boolean; is_heroic?: boolean; + is_promoted?: boolean; + is_public?: boolean; like_count?: number; flow: { diff --git a/src/sprites/Sprites.tsx b/src/sprites/Sprites.tsx index d3c8a38e..be5368da 100644 --- a/src/sprites/Sprites.tsx +++ b/src/sprites/Sprites.tsx @@ -235,6 +235,16 @@ const Sprites: FC<{}> = () => ( + + + + + + + + + + diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 8577202c..a518e5d4 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -209,3 +209,13 @@ $sidebar_border: transparentize(white, 0.95); background: transparentize($content_bg, 0.4); box-shadow: transparentize(white, 0.95) -1px 0; } + +@mixin editor_round_button { + width: $upload_button_height; + height: $upload_button_height; + border-radius: ($upload_button_height / 2) !important; + flex: 0 0 $upload_button_height; + position: relative; + border-radius: $radius; + cursor: pointer; +} From 40af254e5c94bb492be0f76b36b01f2322ff7c14 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 5 Mar 2021 17:50:28 +0700 Subject: [PATCH 2/5] replaced div with button --- .../editors/EditorPublicSwitch/index.tsx | 29 +++++++++---------- src/components/input/Button/index.tsx | 3 ++ .../input/Button/styles.module.scss | 3 ++ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/editors/EditorPublicSwitch/index.tsx b/src/components/editors/EditorPublicSwitch/index.tsx index d8f9f889..be6feed0 100644 --- a/src/components/editors/EditorPublicSwitch/index.tsx +++ b/src/components/editors/EditorPublicSwitch/index.tsx @@ -1,9 +1,7 @@ import React, { FC, useCallback, useEffect, useRef } from 'react'; -import styles from './styles.module.scss'; -import { Icon } from '~/components/input/Icon'; import { IEditorComponentProps } from '~/redux/node/types'; -import classNames from 'classnames'; import { usePopper } from 'react-popper'; +import { Button } from '~/components/input/Button'; interface IProps extends IEditorComponentProps {} @@ -29,20 +27,19 @@ const EditorPublicSwitch: FC = ({ data, setData }) => { useEffect(() => console.log(pop), [pop]); return ( -
-
- {data.is_promoted +
+ : 'Видно только сотрудникам в лаборатории' + } + onClick={onChange} + round + /> ); }; diff --git a/src/components/input/Button/index.tsx b/src/components/input/Button/index.tsx index 8c263770..1f2a02a6 100644 --- a/src/components/input/Button/index.tsx +++ b/src/components/input/Button/index.tsx @@ -28,6 +28,7 @@ type IButtonProps = DetailedHTMLProps< stretchy?: boolean; iconOnly?: boolean; label?: string; + round?: boolean; }; const Button: FC = memo( @@ -48,6 +49,7 @@ const Button: FC = memo( iconOnly, label, ref, + round, ...props }) => { const tooltip = useRef(null); @@ -75,6 +77,7 @@ const Button: FC = memo( icon: ((iconLeft || iconRight) && !title && !children) || iconOnly, has_icon_left: !!iconLeft, has_icon_right: !!iconRight, + round, }), ...props, }, diff --git a/src/components/input/Button/styles.module.scss b/src/components/input/Button/styles.module.scss index 676bb62d..667007bd 100644 --- a/src/components/input/Button/styles.module.scss +++ b/src/components/input/Button/styles.module.scss @@ -202,14 +202,17 @@ .normal { height: 38px; } + .big { height: 40px; } + .giant { height: 50px; padding: 0 15px; min-width: 50px; } + .disabled { opacity: 0.5; } From 18ec220a4e508a2bf67fdd076b8b6c15aee0fc7b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 12 Mar 2021 12:04:10 +0700 Subject: [PATCH 3/5] #23 added labs layout --- src/components/containers/Sticky/index.tsx | 45 ++++--------------- .../containers/Sticky/styles.module.scss | 17 ------- src/components/main/Header/index.tsx | 14 ++++-- src/components/node/NodeAudioBlock/index.tsx | 6 +-- .../node/NodeAudioImageBlock/index.tsx | 6 +-- src/constants/urls.ts | 1 + src/containers/lab/LabGrid/index.tsx | 21 +++++++++ src/containers/lab/LabGrid/styles.module.scss | 8 ++++ src/containers/lab/LabLayout/index.tsx | 27 +++++++++++ .../lab/LabLayout/styles.module.scss | 11 +++++ src/containers/lab/LabNode/index.tsx | 34 ++++++++++++++ src/containers/main/MainRouter/index.tsx | 2 + src/utils/hooks/node/useNodeAudios.ts | 4 ++ src/utils/hooks/node/useNodeImages.ts | 4 ++ 14 files changed, 135 insertions(+), 65 deletions(-) delete mode 100644 src/components/containers/Sticky/styles.module.scss create mode 100644 src/containers/lab/LabGrid/index.tsx create mode 100644 src/containers/lab/LabGrid/styles.module.scss create mode 100644 src/containers/lab/LabLayout/index.tsx create mode 100644 src/containers/lab/LabLayout/styles.module.scss create mode 100644 src/containers/lab/LabNode/index.tsx diff --git a/src/components/containers/Sticky/index.tsx b/src/components/containers/Sticky/index.tsx index dfeda268..55288c87 100644 --- a/src/components/containers/Sticky/index.tsx +++ b/src/components/containers/Sticky/index.tsx @@ -1,42 +1,15 @@ -import React, { DetailsHTMLAttributes, FC, useEffect, useRef } from 'react'; -import styles from './styles.module.scss'; +import React, { DetailsHTMLAttributes, FC } from 'react'; +import StickyBox from 'react-sticky-box/dist/esnext'; -import ResizeSensor from 'resize-sensor'; -(window as any).ResizeSensor = ResizeSensor; - -import StickySidebar from 'sticky-sidebar'; -(window as any).StickySidebar = StickySidebar; - -import classnames from 'classnames'; - -interface IProps extends DetailsHTMLAttributes {} - -const Sticky: FC = ({ children }) => { - const ref = useRef(null); - const sb = useRef(null); - - useEffect(() => { - if (!ref.current) return; - - sb.current = new StickySidebar(ref.current, { - resizeSensor: true, - topSpacing: 72, - bottomSpacing: 10, - }); - - return () => sb.current?.destroy(); - }, [ref.current, sb.current, children]); - - if (sb) { - sb.current?.updateSticky(); - } +interface IProps extends DetailsHTMLAttributes { + offsetTop?: number; +} +const Sticky: FC = ({ children, offsetTop = 65 }) => { return ( -
-
-
{children}
-
-
+ + {children} + ); }; diff --git a/src/components/containers/Sticky/styles.module.scss b/src/components/containers/Sticky/styles.module.scss deleted file mode 100644 index eb680535..00000000 --- a/src/components/containers/Sticky/styles.module.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import "src/styles/variables"; - -.wrap { - height: 100%; - width: 100%; - position: relative; - - :global(.sidebar) { - will-change: min-height; - } - - :global(.sidebar__inner) { - transform: translate(0, 0); /* For browsers don't support translate3d. */ - transform: translate3d(0, 0, 0); - will-change: position, transform; - } -} diff --git a/src/components/main/Header/index.tsx b/src/components/main/Header/index.tsx index 43d7646c..69d0dee4 100644 --- a/src/components/main/Header/index.tsx +++ b/src/components/main/Header/index.tsx @@ -82,6 +82,15 @@ const HeaderUnconnected: FC = memo(
+ {is_user && ( + + ЛАБ + + )} + = memo( } ); -const Header = connect( - mapStateToProps, - mapDispatchToProps -)(HeaderUnconnected); +const Header = connect(mapStateToProps, mapDispatchToProps)(HeaderUnconnected); export { Header }; diff --git a/src/components/node/NodeAudioBlock/index.tsx b/src/components/node/NodeAudioBlock/index.tsx index 9581a16b..c064e831 100644 --- a/src/components/node/NodeAudioBlock/index.tsx +++ b/src/components/node/NodeAudioBlock/index.tsx @@ -4,14 +4,12 @@ import { UPLOAD_TYPES } from '~/redux/uploads/constants'; import { AudioPlayer } from '~/components/media/AudioPlayer'; import styles from './styles.module.scss'; import { INodeComponentProps } from '~/redux/node/constants'; +import { useNodeAudios } from '~/utils/hooks/node/useNodeAudios'; interface IProps extends INodeComponentProps {} const NodeAudioBlock: FC = ({ node }) => { - const audios = useMemo( - () => node.files.filter(file => file && file.type === UPLOAD_TYPES.AUDIO), - [node.files] - ); + const audios = useNodeAudios(node); return (
diff --git a/src/components/node/NodeAudioImageBlock/index.tsx b/src/components/node/NodeAudioImageBlock/index.tsx index ca848b3f..e7834737 100644 --- a/src/components/node/NodeAudioImageBlock/index.tsx +++ b/src/components/node/NodeAudioImageBlock/index.tsx @@ -6,14 +6,12 @@ import { path } from 'ramda'; import { getURL } from '~/utils/dom'; import { PRESETS } from '~/constants/urls'; import { INodeComponentProps } from '~/redux/node/constants'; +import { useNodeImages } from '~/utils/hooks/node/useNodeImages'; interface IProps extends INodeComponentProps {} const NodeAudioImageBlock: FC = ({ node }) => { - const images = useMemo( - () => node.files.filter(file => file && file.type === UPLOAD_TYPES.IMAGE), - [node.files] - ); + const images = useNodeImages(node); if (images.length === 0) return null; diff --git a/src/constants/urls.ts b/src/constants/urls.ts index 67eb4a39..527e072e 100644 --- a/src/constants/urls.ts +++ b/src/constants/urls.ts @@ -2,6 +2,7 @@ import { INode } from '~/redux/types'; export const URLS = { BASE: '/', + LAB: '/lab', BORIS: '/boris', AUTH: { LOGIN: '/auth/login', diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx new file mode 100644 index 00000000..1caa8381 --- /dev/null +++ b/src/containers/lab/LabGrid/index.tsx @@ -0,0 +1,21 @@ +import React, { FC } from 'react'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectFlowNodes } from '~/redux/flow/selectors'; +import styles from './styles.module.scss'; +import { LabNode } from '~/containers/lab/LabNode'; + +interface IProps {} + +const LabGrid: FC = () => { + const nodes = useShallowSelect(selectFlowNodes); + + return ( +
+ {nodes.map(node => ( + + ))} +
+ ); +}; + +export { LabGrid }; diff --git a/src/containers/lab/LabGrid/styles.module.scss b/src/containers/lab/LabGrid/styles.module.scss new file mode 100644 index 00000000..3f42c360 --- /dev/null +++ b/src/containers/lab/LabGrid/styles.module.scss @@ -0,0 +1,8 @@ +@import "~/styles/variables.scss"; + +.wrap { + display: grid; + grid-auto-flow: row; + grid-auto-rows: auto; + grid-row-gap: $gap; +} diff --git a/src/containers/lab/LabLayout/index.tsx b/src/containers/lab/LabLayout/index.tsx new file mode 100644 index 00000000..47a77bcd --- /dev/null +++ b/src/containers/lab/LabLayout/index.tsx @@ -0,0 +1,27 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; +import { Card } from '~/components/containers/Card'; +import { Sticky } from '~/components/containers/Sticky'; +import { Container } from '~/containers/main/Container'; +import { LabGrid } from '~/containers/lab/LabGrid'; + +interface IProps {} + +const LabLayout: FC = () => ( +
+ +
+
+ +
+
+ + Test + +
+
+
+
+); + +export { LabLayout }; diff --git a/src/containers/lab/LabLayout/styles.module.scss b/src/containers/lab/LabLayout/styles.module.scss new file mode 100644 index 00000000..c204075d --- /dev/null +++ b/src/containers/lab/LabLayout/styles.module.scss @@ -0,0 +1,11 @@ +@import "~/styles/variables.scss"; + +.wrap { + display: grid; + grid-template-columns: 3fr 1fr; + column-gap: $gap; +} + +.panel { + margin-top: -7px; +} diff --git a/src/containers/lab/LabNode/index.tsx b/src/containers/lab/LabNode/index.tsx new file mode 100644 index 00000000..246a119b --- /dev/null +++ b/src/containers/lab/LabNode/index.tsx @@ -0,0 +1,34 @@ +import React, { FC } from 'react'; +import { INode } from '~/redux/types'; +import { NodePanelInner } from '~/components/node/NodePanelInner'; +import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks'; + +interface IProps { + node: INode; +} + +const LabNode: FC = ({ node }) => { + const { inline, block, head } = useNodeBlocks(node, false); + + return ( +
+ + + {inline} + {block} + {head} +
+ ); +}; + +export { LabNode }; diff --git a/src/containers/main/MainRouter/index.tsx b/src/containers/main/MainRouter/index.tsx index bed1579a..b5a17270 100644 --- a/src/containers/main/MainRouter/index.tsx +++ b/src/containers/main/MainRouter/index.tsx @@ -6,6 +6,7 @@ import { BorisLayout } from '~/containers/node/BorisLayout'; import { ErrorNotFound } from '~/containers/pages/ErrorNotFound'; import { ProfilePage } from '~/containers/profile/ProfilePage'; import { Redirect, Route, Switch, useLocation } from 'react-router'; +import { LabLayout } from '~/containers/lab/LabLayout'; interface IProps {} @@ -15,6 +16,7 @@ const MainRouter: FC = () => { return ( + diff --git a/src/utils/hooks/node/useNodeAudios.ts b/src/utils/hooks/node/useNodeAudios.ts index 7ece487f..5dc043a1 100644 --- a/src/utils/hooks/node/useNodeAudios.ts +++ b/src/utils/hooks/node/useNodeAudios.ts @@ -3,6 +3,10 @@ import { useMemo } from 'react'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; export const useNodeAudios = (node: INode) => { + if (!node?.files) { + return []; + } + return useMemo(() => node.files.filter(file => file && file.type === UPLOAD_TYPES.AUDIO), [ node.files, ]); diff --git a/src/utils/hooks/node/useNodeImages.ts b/src/utils/hooks/node/useNodeImages.ts index 4f6b71d5..375d6198 100644 --- a/src/utils/hooks/node/useNodeImages.ts +++ b/src/utils/hooks/node/useNodeImages.ts @@ -3,6 +3,10 @@ import { useMemo } from 'react'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; export const useNodeImages = (node: INode) => { + if (!node?.files) { + return []; + } + return useMemo(() => node.files.filter(file => file && file.type === UPLOAD_TYPES.IMAGE), [ node.files, ]); From 3aa2d4f609e909556978bb2173f558fd88a70e10 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 12 Mar 2021 13:56:23 +0700 Subject: [PATCH 4/5] #23 added lab node layout (sample) --- .../node/NodePanelInner/styles.module.scss | 4 +- src/components/node/NodePanelLab/index.tsx | 19 +++++++++ .../node/NodePanelLab/styles.module.scss | 24 +++++++++++ src/constants/api.ts | 3 ++ src/containers/lab/LabGrid/index.tsx | 4 +- src/containers/lab/LabLayout/index.tsx | 42 ++++++++++++------- src/containers/lab/LabNode/index.tsx | 29 ++++++------- src/containers/lab/LabNode/styles.module.scss | 10 +++++ src/containers/main/MainRouter/index.tsx | 10 ++++- src/redux/lab/actions.ts | 12 ++++++ src/redux/lab/api.ts | 8 ++++ src/redux/lab/constants.ts | 6 +++ src/redux/lab/handlers.ts | 20 +++++++++ src/redux/lab/index.ts | 14 +++++++ src/redux/lab/sagas.ts | 21 ++++++++++ src/redux/lab/selectors.ts | 4 ++ src/redux/lab/types.ts | 19 +++++++++ src/redux/store.ts | 7 ++++ 18 files changed, 218 insertions(+), 38 deletions(-) create mode 100644 src/components/node/NodePanelLab/index.tsx create mode 100644 src/components/node/NodePanelLab/styles.module.scss create mode 100644 src/containers/lab/LabNode/styles.module.scss create mode 100644 src/redux/lab/actions.ts create mode 100644 src/redux/lab/api.ts create mode 100644 src/redux/lab/constants.ts create mode 100644 src/redux/lab/handlers.ts create mode 100644 src/redux/lab/index.ts create mode 100644 src/redux/lab/sagas.ts create mode 100644 src/redux/lab/selectors.ts create mode 100644 src/redux/lab/types.ts diff --git a/src/components/node/NodePanelInner/styles.module.scss b/src/components/node/NodePanelInner/styles.module.scss index 58d925fa..c9b9a94b 100644 --- a/src/components/node/NodePanelInner/styles.module.scss +++ b/src/components/node/NodePanelInner/styles.module.scss @@ -31,8 +31,6 @@ .wrap { display: flex; - align-items: center; - justify-content: stretch; position: relative; width: 100%; flex-direction: row; @@ -88,7 +86,7 @@ @include tablet { white-space: nowrap; padding-bottom: 0; - font: $font_20_semibold; + font: $font_16_semibold; } } diff --git a/src/components/node/NodePanelLab/index.tsx b/src/components/node/NodePanelLab/index.tsx new file mode 100644 index 00000000..c33714d4 --- /dev/null +++ b/src/components/node/NodePanelLab/index.tsx @@ -0,0 +1,19 @@ +import React, { FC } from 'react'; +import { INode } from '~/redux/types'; +import styles from './styles.module.scss'; +import { URLS } from '~/constants/urls'; +import { Link } from 'react-router-dom'; + +interface IProps { + node: INode; +} + +const NodePanelLab: FC = ({ node }) => ( +
+
+ {node.title || '...'} +
+
+); + +export { NodePanelLab }; diff --git a/src/components/node/NodePanelLab/styles.module.scss b/src/components/node/NodePanelLab/styles.module.scss new file mode 100644 index 00000000..095dafe5 --- /dev/null +++ b/src/components/node/NodePanelLab/styles.module.scss @@ -0,0 +1,24 @@ +@import "~/styles/variables.scss"; + +.wrap { + padding: $gap; +} + +.title { + text-transform: uppercase; + font: $font_24_semibold; + overflow: hidden; + flex: 1; + text-overflow: ellipsis; + + a { + text-decoration: none; + color: inherit; + } + + @include tablet { + white-space: nowrap; + padding-bottom: 0; + font: $font_16_semibold; + } +} diff --git a/src/constants/api.ts b/src/constants/api.ts index 6f57d689..c9c4c287 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -50,4 +50,7 @@ export const API = { NODES: `/tag/nodes`, AUTOCOMPLETE: `/tag/autocomplete`, }, + LAB: { + NODES: `/lab/`, + }, }; diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx index 1caa8381..6dc04ef3 100644 --- a/src/containers/lab/LabGrid/index.tsx +++ b/src/containers/lab/LabGrid/index.tsx @@ -1,13 +1,13 @@ import React, { FC } from 'react'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; -import { selectFlowNodes } from '~/redux/flow/selectors'; import styles from './styles.module.scss'; import { LabNode } from '~/containers/lab/LabNode'; +import { selectLabListNodes } from '~/redux/lab/selectors'; interface IProps {} const LabGrid: FC = () => { - const nodes = useShallowSelect(selectFlowNodes); + const nodes = useShallowSelect(selectLabListNodes); return (
diff --git a/src/containers/lab/LabLayout/index.tsx b/src/containers/lab/LabLayout/index.tsx index 47a77bcd..087a8546 100644 --- a/src/containers/lab/LabLayout/index.tsx +++ b/src/containers/lab/LabLayout/index.tsx @@ -1,27 +1,37 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import styles from './styles.module.scss'; import { Card } from '~/components/containers/Card'; import { Sticky } from '~/components/containers/Sticky'; import { Container } from '~/containers/main/Container'; import { LabGrid } from '~/containers/lab/LabGrid'; +import { useDispatch } from 'react-redux'; +import { labGetList } from '~/redux/lab/actions'; interface IProps {} -const LabLayout: FC = () => ( -
- -
-
- +const LabLayout: FC = () => { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(labGetList()); + }, [dispatch]); + + return ( +
+ +
+
+ +
+
+ + Test + +
-
- - Test - -
-
- -
-); + +
+ ); +}; export { LabLayout }; diff --git a/src/containers/lab/LabNode/index.tsx b/src/containers/lab/LabNode/index.tsx index 246a119b..5f64c55f 100644 --- a/src/containers/lab/LabNode/index.tsx +++ b/src/containers/lab/LabNode/index.tsx @@ -2,6 +2,9 @@ import React, { FC } from 'react'; import { INode } from '~/redux/types'; import { NodePanelInner } from '~/components/node/NodePanelInner'; import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks'; +import styles from './styles.module.scss'; +import { Card } from '~/components/containers/Card'; +import { NodePanelLab } from '~/components/node/NodePanelLab'; interface IProps { node: INode; @@ -10,24 +13,18 @@ interface IProps { const LabNode: FC = ({ node }) => { const { inline, block, head } = useNodeBlocks(node, false); - return ( -
- + console.log(node.id, { inline, block, head }); + + return ( + +
+ +
- {inline} - {block} {head} -
+ {block} + {inline} + ); }; diff --git a/src/containers/lab/LabNode/styles.module.scss b/src/containers/lab/LabNode/styles.module.scss new file mode 100644 index 00000000..8e002f2b --- /dev/null +++ b/src/containers/lab/LabNode/styles.module.scss @@ -0,0 +1,10 @@ +@import "~/styles/variables.scss"; + +.wrap { + min-width: 0; +} + +.head { + background-color: transparentize(black, 0.9); + border-radius: $radius $radius 0 0; +} diff --git a/src/containers/main/MainRouter/index.tsx b/src/containers/main/MainRouter/index.tsx index b5a17270..ac8988dc 100644 --- a/src/containers/main/MainRouter/index.tsx +++ b/src/containers/main/MainRouter/index.tsx @@ -7,21 +7,29 @@ import { ErrorNotFound } from '~/containers/pages/ErrorNotFound'; import { ProfilePage } from '~/containers/profile/ProfilePage'; import { Redirect, Route, Switch, useLocation } from 'react-router'; import { LabLayout } from '~/containers/lab/LabLayout'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectAuthUser } from '~/redux/auth/selectors'; interface IProps {} const MainRouter: FC = () => { + const { is_user } = useShallowSelect(selectAuthUser); const location = useLocation(); return ( - + {is_user && ( + <> + + + )} + ); diff --git a/src/redux/lab/actions.ts b/src/redux/lab/actions.ts new file mode 100644 index 00000000..1e1ff97a --- /dev/null +++ b/src/redux/lab/actions.ts @@ -0,0 +1,12 @@ +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { ILabState } from '~/redux/lab/types'; + +export const labGetList = (after?: string) => ({ + type: LAB_ACTIONS.GET_LIST, + after, +}); + +export const labSetList = (list: Partial) => ({ + type: LAB_ACTIONS.SET_LIST, + list, +}); diff --git a/src/redux/lab/api.ts b/src/redux/lab/api.ts new file mode 100644 index 00000000..5fa97bc0 --- /dev/null +++ b/src/redux/lab/api.ts @@ -0,0 +1,8 @@ +import { api, cleanResult } from '~/utils/api'; +import { API } from '~/constants/api'; +import { GetLabNodesRequest, GetLabNodesResult } from '~/redux/lab/types'; + +export const getLabNodes = ({ after }: GetLabNodesRequest) => + api + .get(API.LAB.NODES, { params: { after } }) + .then(cleanResult); diff --git a/src/redux/lab/constants.ts b/src/redux/lab/constants.ts new file mode 100644 index 00000000..d2e670da --- /dev/null +++ b/src/redux/lab/constants.ts @@ -0,0 +1,6 @@ +const prefix = 'LAB.'; + +export const LAB_ACTIONS = { + GET_LIST: `${prefix}GET_LIST`, + SET_LIST: `${prefix}SET_LIST`, +}; diff --git a/src/redux/lab/handlers.ts b/src/redux/lab/handlers.ts new file mode 100644 index 00000000..b09812e2 --- /dev/null +++ b/src/redux/lab/handlers.ts @@ -0,0 +1,20 @@ +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { labSetList } from '~/redux/lab/actions'; +import { ILabState } from '~/redux/lab/types'; + +type LabHandler any> = ( + state: Readonly, + payload: ReturnType +) => Readonly; + +const setList: LabHandler = (state, { list }) => ({ + ...state, + list: { + ...state.list, + ...list, + }, +}); + +export const LAB_HANDLERS = { + [LAB_ACTIONS.SET_LIST]: setList, +}; diff --git a/src/redux/lab/index.ts b/src/redux/lab/index.ts new file mode 100644 index 00000000..56879a52 --- /dev/null +++ b/src/redux/lab/index.ts @@ -0,0 +1,14 @@ +import { createReducer } from '~/utils/reducer'; +import { LAB_HANDLERS } from '~/redux/lab/handlers'; +import { ILabState } from '~/redux/lab/types'; + +const INITIAL_STATE: ILabState = { + list: { + is_loading: false, + nodes: [], + count: 0, + error: '', + }, +}; + +export default createReducer(INITIAL_STATE, LAB_HANDLERS); diff --git a/src/redux/lab/sagas.ts b/src/redux/lab/sagas.ts new file mode 100644 index 00000000..5fc48b8b --- /dev/null +++ b/src/redux/lab/sagas.ts @@ -0,0 +1,21 @@ +import { takeLeading, call, put } from 'redux-saga/effects'; +import { labGetList, labSetList } from '~/redux/lab/actions'; +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { Unwrap } from '~/redux/types'; +import { getLabNodes } from '~/redux/lab/api'; + +function* getList({ after = '' }: ReturnType) { + try { + yield put(labSetList({ is_loading: true })); + const { nodes, count }: Unwrap = yield call(getLabNodes, { after }); + yield put(labSetList({ nodes, count })); + } catch (error) { + yield put(labSetList({ error: error.message })); + } finally { + yield put(labSetList({ is_loading: false })); + } +} + +export default function* labSaga() { + yield takeLeading(LAB_ACTIONS.GET_LIST, getList); +} diff --git a/src/redux/lab/selectors.ts b/src/redux/lab/selectors.ts new file mode 100644 index 00000000..0854ac25 --- /dev/null +++ b/src/redux/lab/selectors.ts @@ -0,0 +1,4 @@ +import { IState } from '~/redux/store'; + +export const selectLab = (state: IState) => state.lab; +export const selectLabListNodes = (state: IState) => state.lab.list.nodes; diff --git a/src/redux/lab/types.ts b/src/redux/lab/types.ts new file mode 100644 index 00000000..7614807b --- /dev/null +++ b/src/redux/lab/types.ts @@ -0,0 +1,19 @@ +import { IError, INode } from '~/redux/types'; + +export type ILabState = Readonly<{ + list: { + is_loading: boolean; + nodes: INode[]; + count: number; + error: IError; + }; +}>; + +export type GetLabNodesRequest = { + after?: string; +}; + +export type GetLabNodesResult = { + nodes: INode[]; + count: number; +}; diff --git a/src/redux/store.ts b/src/redux/store.ts index ea16f709..73e31db9 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -17,6 +17,10 @@ import nodeSaga from '~/redux/node/sagas'; import flow, { IFlowState } from '~/redux/flow/reducer'; import flowSaga from '~/redux/flow/sagas'; +import lab from '~/redux/lab'; +import labSaga from '~/redux/lab/sagas'; +import { ILabState } from '~/redux/lab/types'; + import uploads, { IUploadState } from '~/redux/uploads/reducer'; import uploadSaga from '~/redux/uploads/sagas'; @@ -69,6 +73,7 @@ export interface IState { boris: IBorisState; messages: IMessagesState; tag: ITagState; + lab: ILabState; } export const sagaMiddleware = createSagaMiddleware(); @@ -93,6 +98,7 @@ export const store = createStore( player: persistReducer(playerPersistConfig, player), messages, tag: tag, + lab: lab, }), composeEnhancers(applyMiddleware(routerMiddleware(history), sagaMiddleware)) ); @@ -110,6 +116,7 @@ export function configureStore(): { sagaMiddleware.run(borisSaga); sagaMiddleware.run(messagesSaga); sagaMiddleware.run(tagSaga); + sagaMiddleware.run(labSaga); window.addEventListener('message', message => { if (message && message.data && message.data.type === 'oauth_login' && message.data.token) From 05b4ed19c6b85044db6e3f6d0a4056c89bab6c1b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 12 Mar 2021 14:16:45 +0700 Subject: [PATCH 5/5] #23 made sample lab layout --- src/components/lab/LabBanner/index.tsx | 22 ++++++++ .../lab/LabBanner/styles.module.scss | 5 ++ src/components/lab/LabHero/index.tsx | 22 ++++++++ src/components/lab/LabHero/styles.module.scss | 10 ++++ .../lab/LabNode/index.tsx | 0 .../lab/LabNode/styles.module.scss | 1 + src/containers/lab/LabGrid/index.tsx | 2 +- src/containers/lab/LabLayout/index.tsx | 54 ++++++++++++++++++- .../lab/LabLayout/styles.module.scss | 9 ++++ 9 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/components/lab/LabBanner/index.tsx create mode 100644 src/components/lab/LabBanner/styles.module.scss create mode 100644 src/components/lab/LabHero/index.tsx create mode 100644 src/components/lab/LabHero/styles.module.scss rename src/{containers => components}/lab/LabNode/index.tsx (100%) rename src/{containers => components}/lab/LabNode/styles.module.scss (99%) diff --git a/src/components/lab/LabBanner/index.tsx b/src/components/lab/LabBanner/index.tsx new file mode 100644 index 00000000..df8f60ea --- /dev/null +++ b/src/components/lab/LabBanner/index.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; +import { Card } from '~/components/containers/Card'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import { Group } from '~/components/containers/Group'; + +interface IProps {} + +const LabBanner: FC = () => ( + + + + + + + + + + +); + +export { LabBanner }; diff --git a/src/components/lab/LabBanner/styles.module.scss b/src/components/lab/LabBanner/styles.module.scss new file mode 100644 index 00000000..f235ed41 --- /dev/null +++ b/src/components/lab/LabBanner/styles.module.scss @@ -0,0 +1,5 @@ +@import "~/styles/variables.scss"; + +.wrap { + background: $red_gradient_alt; +} diff --git a/src/components/lab/LabHero/index.tsx b/src/components/lab/LabHero/index.tsx new file mode 100644 index 00000000..1a059815 --- /dev/null +++ b/src/components/lab/LabHero/index.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import { Group } from '~/components/containers/Group'; +import { Icon } from '~/components/input/Icon'; +import styles from './styles.module.scss'; + +interface IProps {} + +const LabHero: FC = () => ( + +
+ +
+ + + + + +
+); + +export { LabHero }; diff --git a/src/components/lab/LabHero/styles.module.scss b/src/components/lab/LabHero/styles.module.scss new file mode 100644 index 00000000..b1a7e9cc --- /dev/null +++ b/src/components/lab/LabHero/styles.module.scss @@ -0,0 +1,10 @@ +@import "~/styles/variables.scss"; + +.wrap { + margin-bottom: $gap; +} + +.star { + fill: #2c2c2c; +} + diff --git a/src/containers/lab/LabNode/index.tsx b/src/components/lab/LabNode/index.tsx similarity index 100% rename from src/containers/lab/LabNode/index.tsx rename to src/components/lab/LabNode/index.tsx diff --git a/src/containers/lab/LabNode/styles.module.scss b/src/components/lab/LabNode/styles.module.scss similarity index 99% rename from src/containers/lab/LabNode/styles.module.scss rename to src/components/lab/LabNode/styles.module.scss index 8e002f2b..0e8e59ab 100644 --- a/src/containers/lab/LabNode/styles.module.scss +++ b/src/components/lab/LabNode/styles.module.scss @@ -8,3 +8,4 @@ background-color: transparentize(black, 0.9); border-radius: $radius $radius 0 0; } + diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx index 6dc04ef3..b6961b5f 100644 --- a/src/containers/lab/LabGrid/index.tsx +++ b/src/containers/lab/LabGrid/index.tsx @@ -1,7 +1,7 @@ import React, { FC } from 'react'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import styles from './styles.module.scss'; -import { LabNode } from '~/containers/lab/LabNode'; +import { LabNode } from '~/components/lab/LabNode'; import { selectLabListNodes } from '~/redux/lab/selectors'; interface IProps {} diff --git a/src/containers/lab/LabLayout/index.tsx b/src/containers/lab/LabLayout/index.tsx index 087a8546..020df216 100644 --- a/src/containers/lab/LabLayout/index.tsx +++ b/src/containers/lab/LabLayout/index.tsx @@ -6,6 +6,11 @@ import { Container } from '~/containers/main/Container'; import { LabGrid } from '~/containers/lab/LabGrid'; import { useDispatch } from 'react-redux'; import { labGetList } from '~/redux/lab/actions'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import { Grid } from '~/components/containers/Grid'; +import { Group } from '~/components/containers/Group'; +import { LabHero } from '~/components/lab/LabHero'; +import { LabBanner } from '~/components/lab/LabBanner'; interface IProps {} @@ -25,7 +30,54 @@ const LabLayout: FC = () => {
- Test + + + + + + + +
+ + + +
+ +
+ + + + + + +
+ +
+ + + +
+ + + +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ + +
diff --git a/src/containers/lab/LabLayout/styles.module.scss b/src/containers/lab/LabLayout/styles.module.scss index c204075d..ee69442c 100644 --- a/src/containers/lab/LabLayout/styles.module.scss +++ b/src/containers/lab/LabLayout/styles.module.scss @@ -9,3 +9,12 @@ .panel { margin-top: -7px; } + +.tags { + display: flex; + flex-wrap: wrap; + + & > * { + margin: 0 $gap $gap 0; + } +}