1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 04:46:40 +07:00

Merge pull request #2 from muerwre/master

Master
This commit is contained in:
muerwre 2020-04-20 10:05:22 +07:00 committed by GitHub
commit b00c3d6bbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 10734 additions and 513 deletions

View file

@ -99,6 +99,7 @@
"redux-persist": "^5.10.0",
"redux-saga": "^1.1.1",
"reduxsauce": "^1.0.0",
"resize-sensor": "^0.0.6",
"sass-loader": "^7.3.1",
"sass-resources-loader": "^2.0.0",
"scrypt": "^6.0.3",

View file

@ -1,7 +1,4 @@
.blur {
filter: blur(0);
transition: filter 0.25s;
will-change: filter;
padding-top: $header_height + 2px;
display: flex;
box-sizing: border-box;

View file

@ -0,0 +1,41 @@
import React, { FC, ReactComponentElement, DetailsHTMLAttributes, useEffect, useRef } from 'react';
import styles from './styles.scss';
import StickySidebar from 'sticky-sidebar';
import classnames from 'classnames';
import ResizeSensor from 'resize-sensor';
interface IProps extends DetailsHTMLAttributes<HTMLDivElement> {}
(window as any).StickySidebar = StickySidebar;
(window as any).ResizeSensor = ResizeSensor;
const Sticky: FC<IProps> = ({ children }) => {
const ref = useRef(null);
let sb = null;
useEffect(() => {
if (!ref.current) return;
sb = new StickySidebar(ref.current, {
resizeSensor: true,
topSpacing: 72,
bottomSpacing: 10,
});
return () => sb.destroy();
}, [ref.current, children]);
if (sb) {
sb.updateSticky();
}
return (
<div className={classnames(styles.wrap, 'sidebar_container')}>
<div className="sidebar" ref={ref}>
<div className={classnames(styles.sticky, 'sidebar__inner')}>{children}</div>
</div>
</div>
);
};
export { Sticky };

View file

@ -0,0 +1,15 @@
.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;
}
}

View file

@ -0,0 +1,13 @@
import React, { FC, memo } from 'react';
import styles from './styles.scss';
interface IProps {}
const Footer: FC<IProps> = memo(() => (
<div className={styles.footer}>
<div className={styles.slogan}>Уделяй больше времени тишине. Спасибо</div>
<div className={styles.copy}>2009 - {new Date().getFullYear()}</div>
</div>
));
export { Footer };

View file

@ -0,0 +1,23 @@
.footer {
display: flex;
flex-direction: row;
font: $font_12_semibold;
background: transparentize(black, 0.9);
border-radius: 0 0 $radius $radius;
align-items: center;
text-transform: uppercase;
color: darken(white, 80%);
padding-top: 2px;
box-sizing: border-box;
@include outer_shadow();
}
.slogan {
flex: 1;
padding: $gap;
}
.copy {
padding: $gap;
}

View file

@ -6,8 +6,6 @@ import { Switch, Route, Redirect } from 'react-router-dom';
import { history } from '~/redux/store';
import { FlowLayout } from '~/containers/flow/FlowLayout';
import { MainLayout } from '~/containers/main/MainLayout';
import { ImageExample } from '~/containers/examples/ImageExample';
import { EditorExample } from '~/containers/examples/EditorExample';
import { Sprites } from '~/sprites/Sprites';
import { URLS } from '~/constants/urls';
import { Modal } from '~/containers/dialogs/Modal';
@ -39,8 +37,6 @@ const Component: FC<IProps> = ({ modal: { is_shown } }) => {
<Switch>
<Route exact path={URLS.BASE} component={FlowLayout} />
<Route path={URLS.EXAMPLES.IMAGE} component={ImageExample} />
<Route path={URLS.EXAMPLES.EDITOR} component={EditorExample} />
<Route path={URLS.NODE_URL(':id')} component={NodeLayout} />
<Route path={URLS.BORIS} component={BorisLayout} />
<Route path={URLS.ERRORS.NOT_FOUND} component={ErrorNotFound} />
@ -57,7 +53,4 @@ const Component: FC<IProps> = ({ modal: { is_shown } }) => {
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(hot(module)(Component));
export default connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));

View file

@ -1,71 +0,0 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import { Card } from '~/components/containers/Card';
import * as styles from './styles.scss';
import { Group } from '~/components/containers/Group';
import { CellGrid } from '~/components/containers/CellGrid';
import { Panel } from '~/components/containers/Panel';
import { Scroll } from '~/components/containers/Scroll';
import { Tags } from '~/components/node/Tags';
import { Button } from '~/components/input/Button';
import { Filler } from '~/components/containers/Filler';
import { InputText } from '~/components/input/InputText';
import { Icon } from '~/components/input/Icon';
import { Grid } from '~/components/containers/Grid';
interface IProps {}
const EditorExample: FC<IProps> = () => (
<Card className={styles.wrap} seamless>
<Group horizontal className={styles.group} seamless>
<div className={styles.editor}>
<Panel className={classNames(styles.editor_panel, styles.editor_image_panel)}>
<Scroll>
<CellGrid className={styles.editor_image_container} size={200}>
<div className={styles.editor_image} />
<div className={styles.editor_image} />
<div className={styles.editor_image} />
<div className={styles.editor_image} />
</CellGrid>
</Scroll>
</Panel>
<Panel>
<Grid columns="1fr" stretchy>
<Card className={styles.feature_card}>
<div className={styles.cover} />
</Card>
</Grid>
</Panel>
</div>
<div className={styles.panel}>
<Panel>
<Group>
<InputText title="Заголовок" />
<Tags
tags={[
{ title: 'Избранный' },
{ title: 'Плейлист' },
{ title: 'Просто' },
{ title: '+ фото' },
{ title: '+ с музыкой' },
]}
/>
</Group>
</Panel>
<Panel stretchy />
<Panel>
<Button iconRight="play" stretchy>
Submit?
</Button>
</Panel>
</div>
</Group>
</Card>
);
export { EditorExample };

View file

@ -1,87 +0,0 @@
.wrap {
align-items: stretch;
justify-content: center;
display: flex;
background: $editor_bg;
flex: 1;
}
.group {
display: flex;
align-items: stretch !important;
justify-content: stretch;
//flex: 1;
}
.panel {
background: $editor_panel_bg;
flex: 1;
border-radius: $radius;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.editor {
flex: 2;
display: flex;
align-items: stretch;
flex-direction: column;
}
.editor_image_panel {
flex: 1;
}
.editor_image_container {
flex: 1;
}
.editor_image {
background: transparentize(white, 0.95);
padding-bottom: 100%;
border-radius: $radius;
}
.feature_card {
height: 120px;
background: darken($main_bg_color, 6%);
color: transparentize(white, 0.5);
display: flex;
align-items: center;
justify-content: center;
text-transform: uppercase;
font: $font_18_semibold;
box-shadow: none;
padding: 0;
}
.cover {
border-radius: $radius;
background: url("http://37.192.131.144/full/attached/2017/11/f01fdaaea789915284757634baf7cd11.jpg");
flex: 1;
height: 120px;
background-size: cover;
opacity: 0.3;
}
.panel_main {
flex: 1;
display: flex;
}
.close_icon {
height: 24px;
width: 24px;
background: transparentize(white, 0.95);
flex: 0 0 24px;
border-radius: $radius;
}
.views {
div {
display: flex;
align-items: center;
justify-content: center;
}
}

View file

@ -1,59 +0,0 @@
import React, { FC } from 'react';
import { Card } from '~/components/containers/Card';
import * as styles from './styles.scss';
import { Padder } from '~/components/containers/Padder';
import { Group } from '~/components/containers/Group';
import { InputText } from '~/components/input/InputText';
import { Button } from '~/components/input/Button';
import { Filler } from '~/components/containers/Filler';
import { Icon } from '~/components/input/Icon';
interface IProps {}
const HorizontalExample: FC<IProps> = () => (
<div className={styles.wrap}>
<Card seamless className={styles.card}>
<div className={styles.editor}>
<div className={styles.uploads}>
<div className={styles.cell} />
<div className={styles.cell} />
<div className={styles.cell} />
<div className={styles.cell} />
<div className={styles.cell} />
<div className={styles.cell} />
</div>
<Padder className={styles.features}>
<Group horizontal>
<div className={styles.feature_add_btn}>
<Icon icon="plus" />
</div>
<div className={styles.feature}>
<Group horizontal>
<div>ОБЛОЖКА</div>
<Icon icon="close" />
</Group>
</div>
<Filler />
<div className={styles.feature_cell}>
<Icon icon="cell-single" size={24} />
</div>
</Group>
</Padder>
</div>
<Padder className={styles.panel}>
<Group horizontal>
<InputText title="Название" />
<Button title="Сохранить" iconRight="check" />
</Group>
</Padder>
</Card>
</div>
);
export { HorizontalExample };

View file

@ -1,91 +0,0 @@
.wrap {
flex: 1;
align-items: center;
justify-content: center;
display: flex;
flex-direction: column;
}
.card {
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
background: darken($content_bg, 4%);
box-shadow: transparentize(black, 0.7) 0 10px 5px;
}
.editor {
background: $content_bg;
min-height: 200px;
min-width: 50vw;
border-radius: $radius;
display: flex;
align-items: stretch;
justify-content: stretch;
flex-direction: column;
flex: 1;
@include outer_shadow();
}
.panel {
}
.features {
display: flex;
}
.feature_add_btn {
width: 40px;
height: 40px;
border-radius: 24px;
background: $red_gradient;
display: flex;
align-items: center;
justify-content: center;
}
.feature {
background: lighten($content_bg, 4%);
padding: $gap $gap $gap 20px;
border-radius: 24px;
font: $font_14_semibold;
height: 40px;
box-sizing: border-box;
}
.feature_cell {
background: lighten($content_bg, 4%);
display: flex;
align-items: center;
justify-content: center;
height: 40px;
width: 40px;
box-sizing: border-box;
border-radius: $radius;
}
.uploads {
flex: 1;
padding: $gap;
// padding-bottom: 0;
display: grid;
align-items: center;
justify-content: center;
grid-column-gap: $gap;
grid-row-gap: $gap;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
@media (max-width: 600px) {
grid-template-columns: repeat(auto-fill, minmax(30vw, 1fr));
}
}
.cell {
background: lighten($content_bg, 6%);
border-radius: $radius;
padding-bottom: 100%;
}

View file

@ -1,30 +0,0 @@
import React, { FC } from 'react';
import range from 'ramda/es/range';
import { Card } from '~/components/containers/Card';
import * as styles from './styles.scss';
import { Group } from '~/components/containers/Group';
import { Padder } from '~/components/containers/Padder';
import { Comment } from '~/components/node/Comment';
import { NodePanel } from '~/components/node/NodePanel';
import { NodeRelated } from '~/components/node/NodeRelated';
import { Tags } from '~/components/node/Tags';
import { NodeNoComments } from '~/components/node/NodeNoComments';
import { ImageSwitcher } from '~/components/node/ImageSwitcher';
interface IProps {}
const ImageExample: FC<IProps> = () => <Card className={styles.node} seamless></Card>;
export { ImageExample };
/*
<Padder className={styles.buttons}>
<Group>
<MenuButton title="На главной" description="плывет по течению" icon="star" />
<MenuButton title="Видно всем" icon="star" />
<MenuButton title="Редактировать" icon="star" />
</Group>
</Padder>
*/

View file

@ -1,53 +0,0 @@
.image_container {
width: 100%;
background: $node_image_bg;
border-radius: $panel_radius 0 0 $panel_radius;
display: flex;
align-items: center;
justify-content: center;
.image {
max-height: 800px;
opacity: 1;
width: 100%;
border-radius: $radius $radius 0 0;
}
}
.content {
align-items: stretch !important;
@include vertical_at_tablet;
}
.comments {
flex: 3 1;
}
.panel {
flex: 1 3;
display: flex;
align-items: flex-start;
justify-content: flex-start;
padding-left: $gap / 2;
@include tablet {
padding-left: 0;
}
}
.node {
background: $node_bg;
box-shadow: $node_shadow;
}
.image {
background: red;
}
.buttons {
background: $node_buttons_bg;
flex: 1;
border-radius: $panel_radius;
box-shadow: $comment_shadow;
}

View file

@ -19,6 +19,6 @@
width: 100%;
max-width: $content_width;
display: flex;
padding-bottom: 64px;
padding-bottom: 29px;
flex-direction: column;
}

View file

@ -14,6 +14,8 @@ import { NodeCommentForm } from '~/components/node/NodeCommentForm';
import * as NODE_ACTIONS from '~/redux/node/actions';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import isBefore from 'date-fns/isBefore';
import { Card } from '~/components/containers/Card';
import { Footer } from '~/components/main/Footer';
const mapStateToProps = state => ({
node: selectNode(state),
@ -68,58 +70,38 @@ const BorisLayoutUnconnected: FC<IProps> = ({
<div className={styles.caption}>
<div className={styles.caption_text}>{title}</div>
</div>
<img src={boris} />
<img src={boris} alt="Борис" />
</div>
<div className={styles.container}>
<div className={styles.column}>
<div className={styles.daygrid}>
<div className={styles.label}>Убежищу сегодня:</div>
<div className={styles.day}>10</div>
<div>лет</div>
<div className={styles.day}>2</div>
<div>месяца</div>
<Card className={styles.content}>
<Group className={styles.grid}>
{is_user && <NodeCommentForm is_before />}
<div className={styles.line} />
{is_loading_comments ? (
<NodeNoComments is_loading />
) : (
<NodeComments
comments={comments}
comment_data={comment_data}
comment_count={comment_count}
user={user}
onDelete={nodeLockComment}
onEdit={nodeEditComment}
onLoadMore={nodeLoadMoreComments}
order="ASC"
/>
)}
</Group>
<div className={styles.label}>Мы собрали:</div>
<div className={styles.day}>2374</div>
<div>поста</div>
<div className={styles.day}>14765</div>
<div>комментариев</div>
<div className={styles.day}>4260</div>
<div>файла</div>
<div className={styles.day}>54</div>
<div>жителя</div>
</div>
</div>
<Group className={styles.content}>
{is_user && <NodeCommentForm is_before />}
{is_loading_comments ? (
<NodeNoComments is_loading />
) : (
<NodeComments
comments={comments}
comment_data={comment_data}
comment_count={comment_count}
user={user}
onDelete={nodeLockComment}
onEdit={nodeEditComment}
onLoadMore={nodeLoadMoreComments}
order="ASC"
/>
)}
</Group>
<Footer />
</Card>
</div>
</div>
);
};
const BorisLayout = connect(
mapStateToProps,
mapDispatchToProps
)(BorisLayoutUnconnected);
const BorisLayout = connect(mapStateToProps, mapDispatchToProps)(BorisLayoutUnconnected);
export { BorisLayout };

View file

@ -8,21 +8,15 @@
.content {
flex: 3;
z-index: 2;
padding: $gap;
background: $content_bg;
border-radius: $radius;
flex: 0 1 $limited_width;
padding: 0;
background: $node_bg;
box-shadow: inset transparentize(mix($wisegreen, white, 60%), 0.6) 0 1px;
}
.column {
flex: 1;
background: $content_bg;
position: relative;
z-index: 2;
margin-right: $gap;
border-radius: $radius;
padding: $gap * 2;
display: none;
.grid {
padding: $gap;
}
.cover {
@ -33,7 +27,7 @@
width: 100%;
height: 100vh;
overflow: hidden;
background: 50% 0% no-repeat url("~/sprites/boris_bg.svg");
background: 50% 0% no-repeat url('~/sprites/boris_bg.svg');
background-size: cover;
}
@ -41,32 +35,6 @@
font-size: 2em;
}
.daygrid {
display: grid;
grid-template-columns: 1fr 100%;
column-gap: $gap;
align-items: center;
}
.day {
font-size: 2em;
font-weight: 600;
text-align: right;
}
.label {
font: $font_14_regular;
text-transform: uppercase;
margin-bottom: 5px;
opacity: 0.5;
grid-column: 1/3;
}
.line {
grid-column: 1/3;
height: $gap * 4;
}
.container {
display: flex;
align-items: flex-start;
@ -74,11 +42,6 @@
flex-direction: row;
flex: 0 1 $limited_width;
width: 100%;
// margin: auto;
// @include tablet {
// width: 100%;
// }
}
.image {

View file

@ -20,6 +20,8 @@ import pick from 'ramda/es/pick';
import { NodeRelatedPlaceholder } from '~/components/node/NodeRelated/placeholder';
import { NodeDeletedBadge } from '~/components/node/NodeDeletedBadge';
import { NodeCommentForm } from '~/components/node/NodeCommentForm';
import { Sticky } from '~/components/containers/Sticky';
import { Footer } from '~/components/main/Footer';
const mapStateToProps = state => ({
node: selectNode(state),
@ -158,39 +160,40 @@ const NodeLayoutUnconnected: FC<IProps> = memo(
</Group>
<div className={styles.panel}>
<Group style={{ flex: 1, minWidth: 0 }}>
{!is_loading && (
<NodeTags is_editable={is_user} tags={node.tags} onChange={onTagsChange} />
)}
<Sticky>
<Group style={{ flex: 1, minWidth: 0 }}>
{!is_loading && (
<NodeTags is_editable={is_user} tags={node.tags} onChange={onTagsChange} />
)}
{is_loading && <NodeRelatedPlaceholder />}
{is_loading && <NodeRelatedPlaceholder />}
{!is_loading &&
related &&
related.albums &&
Object.keys(related.albums)
.filter(album => related.albums[album].length > 0)
.map(album => (
<NodeRelated title={album} items={related.albums[album]} key={album} />
))}
{!is_loading &&
related &&
related.albums &&
Object.keys(related.albums)
.filter(album => related.albums[album].length > 0)
.map(album => (
<NodeRelated title={album} items={related.albums[album]} key={album} />
))}
{!is_loading && related && related.similar && related.similar.length > 0 && (
<NodeRelated title="ПОХОЖИЕ" items={related.similar} />
)}
</Group>
{!is_loading && related && related.similar && related.similar.length > 0 && (
<NodeRelated title="ПОХОЖИЕ" items={related.similar} />
)}
</Group>
</Sticky>
</div>
</Group>
</Padder>
</Group>
)}
<Footer />
</Card>
);
}
);
const NodeLayout = connect(
mapStateToProps,
mapDispatchToProps
)(NodeLayoutUnconnected);
const NodeLayout = connect(mapStateToProps, mapDispatchToProps)(NodeLayoutUnconnected);
export { NodeLayout, NodeLayoutUnconnected };

10576
yarn-error.log Normal file

File diff suppressed because it is too large Load diff

View file

@ -1026,9 +1026,9 @@ acorn-jsx@^5.0.0:
integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
acorn@^6.0.7, acorn@^6.2.1:
version "6.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
add-px-to-style@1.0.0:
version "1.0.0"
@ -8510,6 +8510,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resize-sensor@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/resize-sensor/-/resize-sensor-0.0.6.tgz#75147dcb273de6832760e461d2e28de6dcf88c45"
integrity sha512-e+3wwdki9elemYP6AnyG2BK9/Gd7ak46wZN+Z62WwmWfhn2La1XV2rPRRIcar+PhRhfiQDXi29TapGMTIbI3Pg==
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"