mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
made better flow cells
This commit is contained in:
parent
7d6f35b0af
commit
95ec0f2e0e
21 changed files with 282 additions and 43 deletions
|
@ -1,21 +1,26 @@
|
|||
import React, { FC, useMemo } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { DEFAULT_DOMINANT_COLOR } from '~/constants/node';
|
||||
import { convertHexToRGBA } from '~/utils/color';
|
||||
import { DivProps } from '~/utils/types';
|
||||
import classNames from 'classnames';
|
||||
import { transparentize } from 'color2k';
|
||||
import { normalizeBrightColor } from '~/utils/color';
|
||||
|
||||
interface Props extends DivProps {
|
||||
color?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const CellShade: FC<Props> = ({ color, ...rest }) => {
|
||||
const CellShade: FC<Props> = ({ color, size = 50, ...rest }) => {
|
||||
const background = useMemo(() => {
|
||||
if (!color || color === DEFAULT_DOMINANT_COLOR) {
|
||||
const normalized = normalizeBrightColor(color);
|
||||
|
||||
if (!color || color === DEFAULT_DOMINANT_COLOR || !normalized) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return `linear-gradient(7deg, ${color} 50px, ${convertHexToRGBA(color, 0.3)} 250px)`;
|
||||
return `linear-gradient(7deg, ${normalized} ${size}px, ${transparentize(normalized, 1)} ${size *
|
||||
5}px)`;
|
||||
}, [color]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
@import "~/styles/variables";
|
||||
|
||||
.shade {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: linear-gradient(7deg, transparentize($content_bg, 0.05) 30px, transparentize($content_bg, 1) 250px);
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
|
||||
&.black::after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
@include tablet {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
|
45
src/components/flow/FlowCell/index.tsx
Normal file
45
src/components/flow/FlowCell/index.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import React, { FC } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { CellShade } from '~/components/flow/CellShade';
|
||||
import { FlowCellImage } from '~/components/flow/FlowCellImage';
|
||||
import { FlowDisplayVariant } from '~/redux/types';
|
||||
import { FlowCellText } from '~/components/flow/FlowCellText';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface Props {
|
||||
to: string;
|
||||
title: string;
|
||||
image?: string;
|
||||
color?: string;
|
||||
text?: string;
|
||||
display?: FlowDisplayVariant;
|
||||
}
|
||||
|
||||
const FlowCell: FC<Props> = ({ color, to, image, display = 'single', text, title }) => {
|
||||
const withText = ((!!display && display !== 'single') || !image) && !!text;
|
||||
|
||||
return (
|
||||
<NavLink className={classNames(styles.cell, styles[display || 'single'])} to={to}>
|
||||
{withText && (
|
||||
<FlowCellText className={styles.text} title={title}>
|
||||
{text!}
|
||||
</FlowCellText>
|
||||
)}
|
||||
|
||||
{image && (
|
||||
<FlowCellImage
|
||||
src={image}
|
||||
height={400}
|
||||
className={styles.thumb}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CellShade color={color} className={styles.shade} size={withText ? 15 : 50} />
|
||||
{!withText && <h4 className={styles.title}>{title}</h4>}
|
||||
</NavLink>
|
||||
);
|
||||
};
|
||||
|
||||
export { FlowCell };
|
77
src/components/flow/FlowCell/styles.module.scss
Normal file
77
src/components/flow/FlowCell/styles.module.scss
Normal file
|
@ -0,0 +1,77 @@
|
|||
@import "~/styles/variables";
|
||||
|
||||
.cell {
|
||||
@include inner_shadow;
|
||||
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: $radius;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $content_bg;
|
||||
flex-direction: row;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
font: inherit;
|
||||
line-height: inherit;
|
||||
|
||||
&.vertical {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.thumb {
|
||||
@include outer_shadow;
|
||||
|
||||
border-radius: $radius;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.shade {
|
||||
@include outer_shadow;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.text {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
border-radius: $radius;
|
||||
max-height: calc(100% - 20px);
|
||||
max-width: calc(100% - 20px);
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
.quadro &,
|
||||
.horizontal & {
|
||||
max-width: calc(50% - 15px);
|
||||
@include blur(transparentize($content_bg, 0), 10px, 0.5)
|
||||
}
|
||||
|
||||
.quadro &,
|
||||
.vertical & {
|
||||
max-height: calc(50% - 15px);
|
||||
@include blur(transparentize($content_bg, 0), 10px, 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font: $font_cell_title;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
padding: $gap;
|
||||
text-transform: uppercase;
|
||||
}
|
18
src/components/flow/FlowCellImage/index.tsx
Normal file
18
src/components/flow/FlowCellImage/index.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React, { FC } from 'react';
|
||||
import LazyLoad from 'react-lazyload';
|
||||
import { IMGProps } from '~/utils/types';
|
||||
import styles from './styles.module.scss';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface Props extends IMGProps {
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const FlowCellImage: FC<Props> = ({ className, children, ...rest }) => (
|
||||
<LazyLoad once className={classNames(styles.wrapper, className)}>
|
||||
<img {...rest} src={rest.src} alt="" />
|
||||
{children}
|
||||
</LazyLoad>
|
||||
);
|
||||
|
||||
export { FlowCellImage };
|
15
src/components/flow/FlowCellImage/styles.module.scss
Normal file
15
src/components/flow/FlowCellImage/styles.module.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
transform: translate(-50%, -50%);
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
23
src/components/flow/FlowCellText/index.tsx
Normal file
23
src/components/flow/FlowCellText/index.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React, { FC } from 'react';
|
||||
import { Markdown } from '~/components/containers/Markdown';
|
||||
import { DivProps } from '~/utils/types';
|
||||
import classNames from 'classnames';
|
||||
import styles from './styles.module.scss';
|
||||
import { formatText } from '~/utils/dom';
|
||||
|
||||
interface Props extends DivProps {
|
||||
children: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const FlowCellText: FC<Props> = ({ children, title, ...rest }) => (
|
||||
<div {...rest} className={classNames(styles.text, rest.className)}>
|
||||
{title && <h4 className={styles.title}>{title}</h4>}
|
||||
<Markdown
|
||||
className={styles.description}
|
||||
dangerouslySetInnerHTML={{ __html: formatText(children) }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { FlowCellText };
|
12
src/components/flow/FlowCellText/styles.module.scss
Normal file
12
src/components/flow/FlowCellText/styles.module.scss
Normal file
|
@ -0,0 +1,12 @@
|
|||
@import "~/styles/variables";
|
||||
|
||||
.text {
|
||||
padding: $gap;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
.title {
|
||||
font: $font_cell_title;
|
||||
margin-bottom: $gap;
|
||||
text-transform: uppercase;
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
import React, { FC, Fragment, useCallback } from 'react';
|
||||
import { Cell } from '~/components/flow/Cell';
|
||||
import React, { FC, Fragment } from 'react';
|
||||
|
||||
import { IFlowState } from '~/redux/flow/reducer';
|
||||
import { INode } from '~/redux/types';
|
||||
import { canEditNode } from '~/utils/node';
|
||||
import { IUser } from '~/redux/auth/types';
|
||||
import { useHistory } from 'react-router';
|
||||
import { URLS } from '~/constants/urls';
|
||||
import { PRESETS, URLS } from '~/constants/urls';
|
||||
import { FlowCell } from '~/components/flow/FlowCell';
|
||||
import classNames from 'classnames';
|
||||
import styles from './styles.module.scss';
|
||||
import { getURLFromString } from '~/utils/dom';
|
||||
|
||||
type IProps = Partial<IFlowState> & {
|
||||
user: Partial<IUser>;
|
||||
|
@ -14,9 +15,6 @@ type IProps = Partial<IFlowState> & {
|
|||
};
|
||||
|
||||
export const FlowGrid: FC<IProps> = ({ user, nodes, onChangeCellView }) => {
|
||||
const history = useHistory();
|
||||
const onSelect = useCallback((id: INode['id']) => history.push(URLS.NODE_URL(id)), [history]);
|
||||
|
||||
if (!nodes) {
|
||||
return null;
|
||||
}
|
||||
|
@ -24,13 +22,16 @@ export const FlowGrid: FC<IProps> = ({ user, nodes, onChangeCellView }) => {
|
|||
return (
|
||||
<Fragment>
|
||||
{nodes.map(node => (
|
||||
<Cell
|
||||
key={node.id}
|
||||
node={node}
|
||||
onSelect={onSelect}
|
||||
can_edit={canEditNode(node, user)}
|
||||
onChangeCellView={onChangeCellView}
|
||||
/>
|
||||
<div className={classNames(styles.cell, styles[node.flow.display])} key={node.id}>
|
||||
<FlowCell
|
||||
color={node.flow.dominant_color}
|
||||
to={URLS.NODE_URL(node.id)}
|
||||
image={getURLFromString(node.thumbnail, PRESETS.cover)}
|
||||
display={node.flow.display}
|
||||
text={node.flow.show_description ? node.description : ''}
|
||||
title={node.title}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
@import "~/styles/variables";
|
||||
|
||||
@mixin mobile {
|
||||
@media (max-width: $cell * 2) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
&.horizontal,
|
||||
&.quadro {
|
||||
grid-column-end: span 2;
|
||||
}
|
||||
|
||||
&.vertical,
|
||||
&.quadro {
|
||||
grid-row-end: span 2;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import SwiperClass from 'swiper/types/swiper-class';
|
|||
import { modalShowPhotoswipe } from '~/redux/modal/actions';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { ImagePreloader } from '~/components/media/ImagePreloader';
|
||||
import { normalizeBrightColor } from '~/utils/color';
|
||||
|
||||
SwiperCore.use([Navigation, Pagination, Keyboard]);
|
||||
|
||||
|
@ -97,7 +98,7 @@ const NodeImageSwiperBlock: FC<IProps> = ({ node }) => {
|
|||
onLoad={updateSwiper}
|
||||
onClick={() => onOpenPhotoSwipe(i)}
|
||||
className={styles.image}
|
||||
color={file?.metadata?.dominant_color}
|
||||
color={normalizeBrightColor(file?.metadata?.dominant_color)}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue