diff --git a/package.json b/package.json index 5ca469e5..512261d1 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", + "@tippy.js/react": "^3.1.1", "autosize": "^4.0.2", "axios": "^0.21.1", "body-scroll-lock": "^2.6.4", diff --git a/src/components/boris/BorisComments/index.tsx b/src/components/boris/BorisComments/index.tsx new file mode 100644 index 00000000..7cddfd67 --- /dev/null +++ b/src/components/boris/BorisComments/index.tsx @@ -0,0 +1,40 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; +import { Group } from '~/components/containers/Group'; +import { NodeCommentForm } from '~/components/node/NodeCommentForm'; +import { NodeNoComments } from '~/components/node/NodeNoComments'; +import { NodeComments } from '~/components/node/NodeComments'; +import { Footer } from '~/components/main/Footer'; +import { Card } from '~/components/containers/Card'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectAuthUser } from '~/redux/auth/selectors'; +import { IComment, INode } from '~/redux/types'; + +interface IProps { + isLoadingComments: boolean; + commentCount: number; + node: INode; + comments: IComment[]; +} + +const BorisComments: FC<IProps> = ({ isLoadingComments, node, commentCount, comments }) => { + const user = useShallowSelect(selectAuthUser); + + return ( + <Card className={styles.content}> + <Group className={styles.grid}> + {user.is_user && <NodeCommentForm isBefore nodeId={node.id} />} + + {isLoadingComments ? ( + <NodeNoComments is_loading count={7} /> + ) : ( + <NodeComments comments={comments} count={commentCount} user={user} order="ASC" /> + )} + </Group> + + <Footer /> + </Card> + ); +}; + +export { BorisComments }; diff --git a/src/components/boris/BorisComments/styles.module.scss b/src/components/boris/BorisComments/styles.module.scss new file mode 100644 index 00000000..1dcd3c24 --- /dev/null +++ b/src/components/boris/BorisComments/styles.module.scss @@ -0,0 +1,18 @@ +@import "~/styles/variables.scss"; + +.content { + flex: 4; + z-index: 2; + border-radius: $radius; + padding: 0; + background: $node_bg; + box-shadow: inset transparentize(mix($wisegreen, white, 60%), 0.6) 0 1px; + + @include desktop { + flex: 2.5; + } + + @media(max-width: 1024px) { + flex: 2; + } +} diff --git a/src/components/boris/BorisUIDemo/index.tsx b/src/components/boris/BorisUIDemo/index.tsx new file mode 100644 index 00000000..bba0c86a --- /dev/null +++ b/src/components/boris/BorisUIDemo/index.tsx @@ -0,0 +1,51 @@ +import React, { FC } from 'react'; +import { Card } from '~/components/containers/Card'; +import styles from './styles.module.scss'; +import markdown from '~/styles/common/markdown.module.scss'; +import { Group } from '~/components/containers/Group'; +import { Button } from '~/components/input/Button'; + +interface IProps {} + +const BorisUIDemo: FC<IProps> = () => ( + <Card className={styles.card}> + <div className={markdown.wrapper}> + <h1>UI</h1> + <p> + Простая демонстрация элементов интерфейса. Используется, в основном, как подсказка при + разработке + </p> + + <h2>Кнопки</h2> + + <h4>Цвета</h4> + + <Group horizontal className={styles.sample}> + <Button>Primary</Button> + <Button color="secondary">Secondary</Button> + <Button color="outline">Outline</Button> + <Button color="gray">Gray</Button> + <Button color="link">Link</Button> + </Group> + + <h4>Размеры</h4> + + <Group horizontal className={styles.sample}> + <Button size="micro">Micro</Button> + <Button size="mini">Mini</Button> + <Button size="normal">Normal</Button> + <Button size="big">Big</Button> + <Button size="giant">Giant</Button> + </Group> + + <h4>Варианты</h4> + <Group horizontal className={styles.sample}> + <Button iconRight="check">iconRight</Button> + <Button iconLeft="send">iconLeft</Button> + <Button round>Round</Button> + </Group> + </div> + </Card> +); + +export { BorisUIDemo }; diff --git a/src/components/boris/BorisUIDemo/styles.module.scss b/src/components/boris/BorisUIDemo/styles.module.scss new file mode 100644 index 00000000..5d9eacfd --- /dev/null +++ b/src/components/boris/BorisUIDemo/styles.module.scss @@ -0,0 +1,11 @@ +.card { + flex: 3; + align-self: stretch; + position: relative; + z-index: 1; + padding: 20px 30px; +} + +.sample { + flex-wrap: wrap; +} diff --git a/src/components/input/Button/index.tsx b/src/components/input/Button/index.tsx index 1f2a02a6..6d9e3801 100644 --- a/src/components/input/Button/index.tsx +++ b/src/components/input/Button/index.tsx @@ -6,11 +6,15 @@ import React, { createElement, memo, useRef, + useEffect, + useMemo, } from 'react'; import styles from './styles.module.scss'; import { Icon } from '~/components/input/Icon'; import { IIcon } from '~/redux/types'; import { usePopper } from 'react-popper'; +import Tippy from '@tippy.js/react'; +import 'tippy.js/dist/tippy.css'; type IButtonProps = DetailedHTMLProps< ButtonHTMLAttributes<HTMLButtonElement>, @@ -23,7 +27,6 @@ type IButtonProps = DetailedHTMLProps< seamless?: boolean; transparent?: boolean; title?: string; - non_submitting?: boolean; is_loading?: boolean; stretchy?: boolean; iconOnly?: boolean; @@ -41,7 +44,6 @@ const Button: FC<IButtonProps> = memo( children, seamless = false, transparent = false, - non_submitting = false, is_loading, title, stretchy, @@ -52,23 +54,9 @@ const Button: FC<IButtonProps> = memo( round, ...props }) => { - const tooltip = useRef<HTMLSpanElement | null>(null); - const pop = usePopper(tooltip?.current?.parentElement, tooltip.current, { - placement: 'top', - modifiers: [ - { - name: 'offset', - options: { - offset: [0, 5], - }, - }, - ], - }); - - return createElement( - seamless || non_submitting ? 'div' : 'button', - { - className: classnames(styles.button, className, styles[size], styles[color], { + const computedClassName = useMemo( + () => + classnames(styles.button, className, styles[size], styles[color], { seamless, transparent, disabled, @@ -79,18 +67,17 @@ const Button: FC<IButtonProps> = memo( has_icon_right: !!iconRight, round, }), - ...props, - }, - [ - iconLeft && <Icon icon={iconLeft} size={20} key={0} className={styles.icon_left} />, - title ? <span>{title}</span> : children || null, - iconRight && <Icon icon={iconRight} size={20} key={2} className={styles.icon_right} />, - !!label && ( - <span ref={tooltip} className={styles.tooltip} style={pop.styles.popper} key="tooltip"> - {label} - </span> - ), - ] + [seamless, round, disabled, className, is_loading, stretchy, iconLeft, iconRight, size, color] + ); + + return ( + <Tippy content={label || ''} enabled={!!label}> + <button className={computedClassName} {...props}> + {iconLeft && <Icon icon={iconLeft} size={20} key={0} className={styles.icon_left} />} + {!!title ? <span>{title}</span> : children} + {iconRight && <Icon icon={iconRight} size={20} key={2} className={styles.icon_right} />} + </button> + </Tippy> ); } ); diff --git a/src/components/input/Button/styles.module.scss b/src/components/input/Button/styles.module.scss index 255b3594..118131c7 100644 --- a/src/components/input/Button/styles.module.scss +++ b/src/components/input/Button/styles.module.scss @@ -184,32 +184,56 @@ font: $font_12_semibold; padding: 0 15px; border-radius: $radius / 2; + + &:global(.round) { + border-radius: 10px; + } } .mini { height: 28px; border-radius: $radius / 2; + + &:global(.round) { + border-radius: 14px; + } } .small { height: 32px; - // border-radius: $radius / 2; svg { width: 24px; height: 24px; } + + &:global(.round) { + border-radius: 16px; + } } + .normal { height: 38px; + + &:global(.round) { + border-radius: 19px; + } } .big { height: 40px; + + &:global(.round) { + border-radius: 20px; + } } .giant { height: 50px; padding: 0 15px; min-width: 50px; + + &:global(.round) { + border-radius: 25px; + } } .disabled { @@ -234,14 +258,13 @@ z-index: 2; border-radius: $input_radius; text-transform: none; - opacity: 0; - pointer-events: none; touch-action: none; transition: opacity 0.1s; border: 1px solid transparentize(white, 0.9); + //visibility: hidden; .button:hover & { - opacity: 1; + visibility: visible; font: $font_14_semibold; } } diff --git a/src/containers/dialogs/EditorDialog/index.tsx b/src/containers/dialogs/EditorDialog/index.tsx index a5a42243..998ccb59 100644 --- a/src/containers/dialogs/EditorDialog/index.tsx +++ b/src/containers/dialogs/EditorDialog/index.tsx @@ -95,7 +95,11 @@ const EditorDialogUnconnected: FC<IProps> = ({ maxLength={256} /> - <Button title="Сохранить" iconRight="check" /> + <Button + title="Сохранить" + iconRight="check" + color={data.is_promoted ? 'primary' : 'secondary'} + /> </Group> </Padder> ); diff --git a/src/containers/node/BorisLayout/index.tsx b/src/containers/node/BorisLayout/index.tsx index d2593ad4..d0af2db5 100644 --- a/src/containers/node/BorisLayout/index.tsx +++ b/src/containers/node/BorisLayout/index.tsx @@ -2,16 +2,11 @@ import React, { FC, useEffect } from 'react'; import { selectNode, selectNodeComments } from '~/redux/node/selectors'; import { selectUser } from '~/redux/auth/selectors'; import { useDispatch } from 'react-redux'; -import { NodeComments } from '~/components/node/NodeComments'; import styles from './styles.module.scss'; import { Group } from '~/components/containers/Group'; import boris from '~/sprites/boris_robot.svg'; -import { NodeNoComments } from '~/components/node/NodeNoComments'; import { useRandomPhrase } from '~/constants/phrases'; -import { NodeCommentForm } from '~/components/node/NodeCommentForm'; import isBefore from 'date-fns/isBefore'; -import { Card } from '~/components/containers/Card'; -import { Footer } from '~/components/main/Footer'; import { BorisStats } from '~/components/boris/BorisStats'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import { selectBorisStats } from '~/redux/boris/selectors'; @@ -20,6 +15,10 @@ import { nodeLoadNode } from '~/redux/node/actions'; import { borisLoadStats } from '~/redux/boris/actions'; import { Container } from '~/containers/main/Container'; import StickyBox from 'react-sticky-box/dist/esnext'; +import { BorisComments } from '~/components/boris/BorisComments'; +import { URLS } from '~/constants/urls'; +import { Route, Switch } from 'react-router-dom'; +import { BorisUIDemo } from '~/components/boris/BorisUIDemo'; type IProps = {}; @@ -69,24 +68,18 @@ const BorisLayout: FC<IProps> = () => { </div> <div className={styles.container}> - <Card className={styles.content}> - <Group className={styles.grid}> - {user.is_user && <NodeCommentForm isBefore nodeId={node.current.id} />} + { + <Switch> + <Route path={`${URLS.BORIS}/ui`} component={BorisUIDemo} /> - {node.is_loading_comments ? ( - <NodeNoComments is_loading count={7} /> - ) : ( - <NodeComments - comments={comments} - count={node.comment_count} - user={user} - order="ASC" - /> - )} - </Group> - - <Footer /> - </Card> + <BorisComments + isLoadingComments={node.is_loading_comments} + commentCount={node.comment_count} + node={node.current} + comments={node.comments} + /> + </Switch> + } <Group className={styles.stats}> <StickyBox className={styles.sticky} offsetTop={72} offsetBottom={10}> diff --git a/src/containers/node/BorisLayout/styles.module.scss b/src/containers/node/BorisLayout/styles.module.scss index f6e3c8ad..44c56d9b 100644 --- a/src/containers/node/BorisLayout/styles.module.scss +++ b/src/containers/node/BorisLayout/styles.module.scss @@ -7,22 +7,6 @@ flex-direction: column; } -.content { - flex: 4; - z-index: 2; - border-radius: $radius; - padding: 0; - background: $node_bg; - box-shadow: inset transparentize(mix($wisegreen, white, 60%), 0.6) 0 1px; - - @include desktop { - flex: 2.5; - } - - @media(max-width: 1024px) { - flex: 2; - } -} .grid { padding: $gap; diff --git a/yarn.lock b/yarn.lock index a3992a4e..b2d4bc5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1594,6 +1594,14 @@ dependencies: "@babel/runtime" "^7.10.2" +"@tippy.js/react@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@tippy.js/react/-/react-3.1.1.tgz#027e4595e55f31430741fe8e0d92aaddfbe47efd" + integrity sha512-KF45vW/jKh/nBXk/2zzTFslv/T46zOMkIoDJ56ymZ+M00yHttk58J5wZ29oqGqDIUnobWSZD+cFpbR4u/UUvgw== + dependencies: + prop-types "^15.6.2" + tippy.js "^5.1.1" + "@types/aria-query@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" @@ -8330,6 +8338,11 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" +popper.js@^1.16.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" + integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== + portfinder@^1.0.26: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" @@ -10972,6 +10985,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tippy.js@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-5.2.1.tgz#e08d7332c103a15e427124d710d881fca82365d6" + integrity sha512-66UT6JRVn3dXNCORE+0UvUK3JZqV/VhLlU6HTDm3FmrweUUFUxUGvT8tUQ7ycMp+uhuLAwQw6dBabyC+iKf/MA== + dependencies: + popper.js "^1.16.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"