mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-28 14:16:41 +07:00
Compare commits
5 commits
f0606a894a
...
9e79cba7bf
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9e79cba7bf | ||
![]() |
24c66ccfdb | ||
![]() |
b257e9b5d9 | ||
![]() |
69c61acc41 | ||
![]() |
7924c2bdd9 |
21 changed files with 251 additions and 244 deletions
|
@ -27,8 +27,8 @@
|
||||||
&.new {
|
&.new {
|
||||||
&::after {
|
&::after {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
width: 12px;
|
width: 8px;
|
||||||
height: 12px;
|
height: 8px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
background: $color_danger;
|
background: $color_danger;
|
||||||
box-shadow: $content_bg 0 0 0 5px;
|
box-shadow: $content_bg 0 0 0 5px;
|
||||||
|
|
|
@ -13,9 +13,11 @@ interface Props extends DivProps {
|
||||||
|
|
||||||
const SubTitle: FC<Props> = ({ isLoading, children, ...rest }) => (
|
const SubTitle: FC<Props> = ({ isLoading, children, ...rest }) => (
|
||||||
<div {...rest} className={classNames(styles.title, rest.className)}>
|
<div {...rest} className={classNames(styles.title, rest.className)}>
|
||||||
|
<span className={styles.name}>
|
||||||
<Placeholder active={isLoading} loading>
|
<Placeholder active={isLoading} loading>
|
||||||
{children}
|
{children}
|
||||||
</Placeholder>
|
</Placeholder>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,25 @@
|
||||||
@import "src/styles/variables.scss";
|
@import 'src/styles/variables.scss';
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font: $font_12_semibold;
|
font: $font_12_semibold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
opacity: 0.3;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: $gap / 2;
|
||||||
|
color: var(--gray_75);
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: ' ';
|
||||||
|
display: flex;
|
||||||
|
height: 2px;
|
||||||
|
background-color: var(--gray_90);
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,6 @@
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
|
|
@ -5,3 +5,5 @@ export const isTablet = () => {
|
||||||
|
|
||||||
return window.innerWidth < 599;
|
return window.innerWidth < 599;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const headerHeight = 64; // px
|
||||||
|
|
|
@ -23,6 +23,7 @@ interface Props {
|
||||||
|
|
||||||
const FlowCellMenu: FC<Props> = ({
|
const FlowCellMenu: FC<Props> = ({
|
||||||
onClose,
|
onClose,
|
||||||
|
currentView,
|
||||||
hasDescription,
|
hasDescription,
|
||||||
toggleViewDescription,
|
toggleViewDescription,
|
||||||
descriptionEnabled,
|
descriptionEnabled,
|
||||||
|
@ -59,7 +60,7 @@ const FlowCellMenu: FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasDescription && (
|
{hasDescription && currentView !== 'single' && (
|
||||||
<Group
|
<Group
|
||||||
className={styles.description}
|
className={styles.description}
|
||||||
horizontal
|
horizontal
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { FC, ReactElement } from 'react';
|
import { FC, ReactElement } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { transparentize, darken, desaturate, getLuminance } from 'color2k';
|
||||||
|
|
||||||
import { Markdown } from '~/components/common/Markdown';
|
import { Markdown } from '~/components/common/Markdown';
|
||||||
import { formatText } from '~/utils/dom';
|
import { formatText } from '~/utils/dom';
|
||||||
|
@ -11,13 +12,29 @@ import styles from './styles.module.scss';
|
||||||
interface Props extends DivProps {
|
interface Props extends DivProps {
|
||||||
children: string;
|
children: string;
|
||||||
heading: string | ReactElement;
|
heading: string | ReactElement;
|
||||||
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowCellText: FC<Props> = ({ children, heading, ...rest }) => (
|
const FlowCellText: FC<Props> = ({ children, heading, color, ...rest }) => {
|
||||||
<div {...rest} className={classNames(styles.text, rest.className)}>
|
const colorIsBright = !!color && getLuminance(color) > 0.4;
|
||||||
|
|
||||||
|
const textColor = colorIsBright
|
||||||
|
? desaturate(darken(color, 0.5), 0.1)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...rest}
|
||||||
|
className={classNames(styles.text, rest.className)}
|
||||||
|
style={{
|
||||||
|
backgroundColor: color && transparentize(color, 0.5),
|
||||||
|
color: textColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{heading && <div className={styles.heading}>{heading}</div>}
|
{heading && <div className={styles.heading}>{heading}</div>}
|
||||||
<Markdown className={styles.description}>{formatText(children)}</Markdown>
|
<Markdown className={styles.description}>{formatText(children)}</Markdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export { FlowCellText };
|
export { FlowCellText };
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
@import "src/styles/variables";
|
@import 'src/styles/variables';
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
padding: $gap;
|
@include blur;
|
||||||
|
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 1) 50%,
|
||||||
|
rgba(0, 0, 0, 0) 95%
|
||||||
|
);
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
margin-bottom: 0.4em;
|
margin-bottom: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FC, useMemo } from 'react';
|
import { FC, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||||
import { useFlowCellControls } from '~/hooks/flow/useFlowCellControls';
|
import { useFlowCellControls } from '~/hooks/flow/useFlowCellControls';
|
||||||
import { FlowDisplay, INode } from '~/types';
|
import { FlowDisplay, INode } from '~/types';
|
||||||
|
|
||||||
|
import { isFullyVisible } from '../../../../../utils/dom';
|
||||||
|
|
||||||
import { CellShade } from './components/CellShade';
|
import { CellShade } from './components/CellShade';
|
||||||
import { FlowCellImage } from './components/FlowCellImage';
|
import { FlowCellImage } from './components/FlowCellImage';
|
||||||
import { FlowCellMenu } from './components/FlowCellMenu';
|
import { FlowCellMenu } from './components/FlowCellMenu';
|
||||||
|
@ -25,7 +27,7 @@ interface Props {
|
||||||
text?: string;
|
text?: string;
|
||||||
flow: FlowDisplay;
|
flow: FlowDisplay;
|
||||||
canEdit?: boolean;
|
canEdit?: boolean;
|
||||||
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void;
|
onChange: (id: INode['id'], flow: FlowDisplay) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowCell: FC<Props> = ({
|
const FlowCell: FC<Props> = ({
|
||||||
|
@ -37,7 +39,7 @@ const FlowCell: FC<Props> = ({
|
||||||
text,
|
text,
|
||||||
title,
|
title,
|
||||||
canEdit = false,
|
canEdit = false,
|
||||||
onChangeCellView,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { isTablet } = useWindowSize();
|
const { isTablet } = useWindowSize();
|
||||||
|
|
||||||
|
@ -45,6 +47,30 @@ const FlowCell: FC<Props> = ({
|
||||||
((!!flow.display && flow.display !== 'single') || !image) &&
|
((!!flow.display && flow.display !== 'single') || !image) &&
|
||||||
flow.show_description &&
|
flow.show_description &&
|
||||||
!!text;
|
!!text;
|
||||||
|
|
||||||
|
const {
|
||||||
|
isActive: isMenuActive,
|
||||||
|
activate,
|
||||||
|
ref,
|
||||||
|
deactivate,
|
||||||
|
} = useClickOutsideFocus();
|
||||||
|
|
||||||
|
const onChangeWithScroll = useCallback<typeof onChange>(
|
||||||
|
(...args) => {
|
||||||
|
onChange(...args);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!isFullyVisible(ref.current)) {
|
||||||
|
ref.current?.scrollIntoView({
|
||||||
|
behavior: 'auto',
|
||||||
|
block: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
[onChange, ref],
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hasDescription,
|
hasDescription,
|
||||||
setViewHorizontal,
|
setViewHorizontal,
|
||||||
|
@ -52,13 +78,7 @@ const FlowCell: FC<Props> = ({
|
||||||
setViewQuadro,
|
setViewQuadro,
|
||||||
setViewSingle,
|
setViewSingle,
|
||||||
toggleViewDescription,
|
toggleViewDescription,
|
||||||
} = useFlowCellControls(id, text, flow, onChangeCellView);
|
} = useFlowCellControls(id, text, flow, onChangeWithScroll);
|
||||||
const {
|
|
||||||
isActive: isMenuActive,
|
|
||||||
activate,
|
|
||||||
ref,
|
|
||||||
deactivate,
|
|
||||||
} = useClickOutsideFocus();
|
|
||||||
|
|
||||||
const shadeSize = useMemo(() => {
|
const shadeSize = useMemo(() => {
|
||||||
const min = isTablet ? 10 : 15;
|
const min = isTablet ? 10 : 15;
|
||||||
|
@ -111,8 +131,9 @@ const FlowCell: FC<Props> = ({
|
||||||
<FlowCellText
|
<FlowCellText
|
||||||
className={styles.text}
|
className={styles.text}
|
||||||
heading={<h4 className={styles.title}>{title}</h4>}
|
heading={<h4 className={styles.title}>{title}</h4>}
|
||||||
|
color={color}
|
||||||
>
|
>
|
||||||
{text!}
|
{text}
|
||||||
</FlowCellText>
|
</FlowCellText>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -124,7 +145,7 @@ const FlowCell: FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!!title && (
|
{!!title && !withText && (
|
||||||
<CellShade
|
<CellShade
|
||||||
color={color}
|
color={color}
|
||||||
className={styles.shade}
|
className={styles.shade}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@import 'src/styles/variables';
|
@import 'src/styles/variables';
|
||||||
|
|
||||||
|
$compact_size: 200px;
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
@include inner_shadow;
|
@include inner_shadow;
|
||||||
|
|
||||||
|
@ -9,6 +11,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: $content_bg;
|
background: $content_bg;
|
||||||
|
container: cell / inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
|
@ -33,20 +36,17 @@
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
|
||||||
left: 5px;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
max-height: calc(100% - 10px);
|
|
||||||
max-width: calc(100% - 10px);
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font: $font_16_regular;
|
inset: 50% 0 0 0;
|
||||||
|
padding: $gap $gap * 1.5 0 $gap * 1.5;
|
||||||
|
font: $font_14_medium;
|
||||||
|
line-height: 1.25em;
|
||||||
|
|
||||||
@include tablet {
|
@container (max-width: $compact_size) {
|
||||||
font: $font_14_regular;
|
padding: $gap / 2 $gap 0 $gap;
|
||||||
left: 5px;
|
|
||||||
bottom: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& :global(.grey) {
|
& :global(.grey) {
|
||||||
|
@ -54,14 +54,34 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quadro &,
|
@container (max-width: #{$compact_size}) {
|
||||||
.horizontal & {
|
padding: $gap / 2 $gap 0 $gap;
|
||||||
max-width: calc(50% - 15px);
|
}
|
||||||
|
|
||||||
|
.horizontal &,
|
||||||
|
.quadro & {
|
||||||
|
@container (max-width: #{$compact_size * 2}) {
|
||||||
|
padding: $gap / 2 $gap 0 $gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal & {
|
||||||
|
inset: 0 calc(50% + $gap / 2) 0 0;
|
||||||
|
border-radius: $radius 0 0 $radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quadro &,
|
|
||||||
.vertical & {
|
.vertical & {
|
||||||
max-height: calc(50% - 15px);
|
inset: calc(50% + $gap / 2) 0 0 0;
|
||||||
|
border-radius: 0 0 $radius $radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quadro & {
|
||||||
|
inset: calc(50% + $gap / 2) calc(50% + $gap / 2) 0 0;
|
||||||
|
border-radius: 0 $radius 0 $radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0.1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,11 +96,21 @@
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font: $font_cell_title;
|
font: $font_cell_title;
|
||||||
|
line-height: 1.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
color: inherit;
|
||||||
|
margin-bottom: -0.125em;
|
||||||
|
|
||||||
@include tablet {
|
@container (max-width: #{$compact_size}) {
|
||||||
font: $font_18_semibold;
|
font: $font_cell_title_compact;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal &,
|
||||||
|
.quadro & {
|
||||||
|
@container (max-width: #{$compact_size * 2}) {
|
||||||
|
font: $font_cell_title_compact;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,12 +137,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.display_modal {
|
.display_modal {
|
||||||
@include appear;
|
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
inset: 0;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ export const FlowGrid: FC<Props> = observer(
|
||||||
text={node.description}
|
text={node.description}
|
||||||
title={node.title}
|
title={node.title}
|
||||||
canEdit={fetched && isUser && canEditNode(node, user)}
|
canEdit={fetched && isUser && canEditNode(node, user)}
|
||||||
onChangeCellView={onChangeCellView}
|
onChange={onChangeCellView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
@import 'src/styles/variables';
|
@import 'src/styles/variables';
|
||||||
|
|
||||||
@mixin mobile {
|
|
||||||
@media (max-width: $cell * 2) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
&.horizontal,
|
&.horizontal,
|
||||||
&.quadro {
|
&.quadro {
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
import { NodeHorizontalCard } from '~/components/common/NodeHorizontalCard';
|
|
||||||
import { IFlowNode } from '~/types';
|
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
recent: IFlowNode[];
|
|
||||||
updated: IFlowNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const FlowRecent: FC<Props> = ({ recent, updated }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.updates}>
|
|
||||||
{updated &&
|
|
||||||
updated.map((node) => (
|
|
||||||
<NodeHorizontalCard node={node} key={node.id} hasNew />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.recent}>
|
|
||||||
{recent &&
|
|
||||||
recent.map((node) => (
|
|
||||||
<NodeHorizontalCard node={node} key={node.id} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { FlowRecent };
|
|
|
@ -1,7 +0,0 @@
|
||||||
@import 'src/styles/variables';
|
|
||||||
|
|
||||||
.recent {
|
|
||||||
@container sizer (width < #{$flow_hide_recents}) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,17 +2,15 @@ import { FC, FormEvent, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { Group } from '~/components/common/Group';
|
import { Card } from '~/components/common/Card';
|
||||||
import { Icon } from '~/components/common/Icon';
|
import { Icon } from '~/components/common/Icon';
|
||||||
import { Superpower } from '~/components/common/Superpower';
|
import { NodeHorizontalCard } from '~/components/common/NodeHorizontalCard';
|
||||||
|
import { SubTitle } from '~/components/common/SubTitle';
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
import { Toggle } from '~/components/input/Toggle';
|
|
||||||
import { experimentalFeatures } from '~/constants/features';
|
|
||||||
import styles from '~/containers/flow/FlowStamp/styles.module.scss';
|
import styles from '~/containers/flow/FlowStamp/styles.module.scss';
|
||||||
import { useFlowContext } from '~/utils/providers/FlowProvider';
|
import { useFlowContext } from '~/utils/providers/FlowProvider';
|
||||||
import { useSearchContext } from '~/utils/providers/SearchProvider';
|
import { useSearchContext } from '~/utils/providers/SearchProvider';
|
||||||
|
|
||||||
import { FlowRecent } from './components/FlowRecent';
|
|
||||||
import { FlowSearchResults } from './components/FlowSearchResults';
|
import { FlowSearchResults } from './components/FlowSearchResults';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -64,7 +62,8 @@ const FlowStamp: FC<Props> = ({ isFluid, onToggleLayout }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<form className={styles.search} onSubmit={onSearchSubmit}>
|
<Card className={styles.search}>
|
||||||
|
<form onSubmit={onSearchSubmit}>
|
||||||
<InputText
|
<InputText
|
||||||
title="Поиск"
|
title="Поиск"
|
||||||
value={searchText}
|
value={searchText}
|
||||||
|
@ -73,14 +72,11 @@ const FlowStamp: FC<Props> = ({ isFluid, onToggleLayout }) => {
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{searchText ? (
|
{searchText ? (
|
||||||
<div className={styles.search_results}>
|
<Card className={styles.grid}>
|
||||||
<div className={styles.grid}>
|
<SubTitle>Результаты поиска</SubTitle>
|
||||||
<div className={styles.label}>
|
|
||||||
<span className={styles.label_text}>Результаты поиска</span>
|
|
||||||
<span className="line" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.items}>
|
<div className={styles.items}>
|
||||||
<FlowSearchResults
|
<FlowSearchResults
|
||||||
|
@ -90,34 +86,31 @@ const FlowStamp: FC<Props> = ({ isFluid, onToggleLayout }) => {
|
||||||
onLoadMore={onSearchLoadMore}
|
onLoadMore={onSearchLoadMore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.grid}>
|
<Card
|
||||||
<div className={classNames(styles.label, styles.whatsnew)}>
|
className={classNames(styles.grid, {
|
||||||
<span className={styles.label_text}>Что нового?</span>
|
[styles.noUpdates]: !updates.length,
|
||||||
<span className="line" />
|
})}
|
||||||
</div>
|
>
|
||||||
|
<SubTitle>Что нового?</SubTitle>
|
||||||
|
|
||||||
<div className={styles.items}>
|
{updates.length > 0 && (
|
||||||
<FlowRecent updated={updates} recent={recent} />
|
<div className={classNames(styles.items, styles.updates)}>
|
||||||
</div>
|
{updates.map((node) => (
|
||||||
|
<NodeHorizontalCard node={node} key={node.id} hasNew />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{experimentalFeatures.liquidFlow && (
|
{recent.length > 0 && (
|
||||||
<Superpower>
|
<div className={classNames(styles.items, styles.recent)}>
|
||||||
<div className={styles.toggles}>
|
{recent.map((node) => (
|
||||||
<Group
|
<NodeHorizontalCard node={node} key={node.id} />
|
||||||
horizontal
|
))}
|
||||||
onClick={onToggleLayout}
|
|
||||||
className={styles.fluid_toggle}
|
|
||||||
>
|
|
||||||
<Toggle value={isFluid} />
|
|
||||||
<div className={styles.toggles__label}>Жидкое течение</div>
|
|
||||||
</Group>
|
|
||||||
</div>
|
</div>
|
||||||
</Superpower>
|
)}
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
@import '../../../styles/variables';
|
@import '~/styles/variables';
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: $radius;
|
gap: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
background-color: var(--content_bg_lighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
@include outer_shadow();
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: $radius;
|
|
||||||
position: relative;
|
|
||||||
background: $content_bg;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
gap: $gap;
|
||||||
|
padding: $gap;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -33,49 +35,25 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.noUpdates {
|
||||||
|
@container sizer (width < #{$flow_hide_recents}) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items.recent {
|
||||||
|
@container sizer (width < #{$flow_hide_recents}) {
|
||||||
|
display: none;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.items {
|
.items {
|
||||||
padding: 0 $gap 0 $gap;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
min-width: 0;
|
|
||||||
padding: $gap;
|
|
||||||
border-radius: $radius;
|
|
||||||
|
|
||||||
@include title_with_line();
|
|
||||||
|
|
||||||
color: transparentize(white, $amount: 0.8);
|
|
||||||
|
|
||||||
&_search {
|
|
||||||
color: white;
|
|
||||||
padding-left: $gap * 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > :global(.line) {
|
|
||||||
margin-right: $gap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label_text {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
@include outer_shadow();
|
|
||||||
|
|
||||||
background: $content_bg_lighter;
|
|
||||||
padding: $gap;
|
|
||||||
border-radius: $radius;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search_icon {
|
.search_icon {
|
||||||
|
@ -89,34 +67,3 @@
|
||||||
stroke-width: 0.5;
|
stroke-width: 0.5;
|
||||||
transition: opacity 0.25s;
|
transition: opacity 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggles {
|
|
||||||
& > div {
|
|
||||||
padding: $gap;
|
|
||||||
font: $font_14_semibold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fluid_toggle {
|
|
||||||
@include desktop {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.whatsnew {
|
|
||||||
@container sizer (width < #{$flow_hide_recents}) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search_results {
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
@include tablet {
|
|
||||||
margin-top: $gap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ $cols: math.div($content_width, $cell);
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
|
||||||
@include flow_grid() {
|
@include flow_grid() {
|
||||||
grid-template-rows: 40vh;
|
grid-template-rows: min(50vh, 33cqw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@include flow_breakpoint(5);
|
@include flow_breakpoint(5);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
$bold: 700;
|
$bold: 700;
|
||||||
$semibold: 600;
|
$semibold: 600;
|
||||||
$regular: 400;
|
$regular: 400;
|
||||||
|
@ -6,9 +5,19 @@ $medium: 500;
|
||||||
$light: 300;
|
$light: 300;
|
||||||
$extra_light: 200;
|
$extra_light: 200;
|
||||||
|
|
||||||
|
$font:
|
||||||
$font: Montserrat, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
Montserrat,
|
||||||
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
'Helvetica Neue',
|
||||||
|
Arial,
|
||||||
|
'Noto Sans',
|
||||||
|
sans-serif,
|
||||||
|
'Apple Color Emoji',
|
||||||
|
'Segoe UI Emoji',
|
||||||
|
'Segoe UI Symbol',
|
||||||
'Noto Color Emoji';
|
'Noto Color Emoji';
|
||||||
|
|
||||||
$font_48_semibold: $semibold 48px $font;
|
$font_48_semibold: $semibold 48px $font;
|
||||||
|
@ -39,5 +48,6 @@ $font_8_regular: $regular 8px $font;
|
||||||
$font_8_semibold: $semibold 8px $font;
|
$font_8_semibold: $semibold 8px $font;
|
||||||
|
|
||||||
$font_cell_title: $font_24_semibold;
|
$font_cell_title: $font_24_semibold;
|
||||||
|
$font_cell_title_compact: $font_18_semibold;
|
||||||
$font_hero_title: $bold 40px $font;
|
$font_hero_title: $bold 40px $font;
|
||||||
$font_boris: $bold 72px $font;
|
$font_boris: $bold 72px $font;
|
||||||
|
|
|
@ -63,10 +63,8 @@ $_brown: #23201f;
|
||||||
--content_bg_success: #{transparentize($_wisegreen, 0.7)};
|
--content_bg_success: #{transparentize($_wisegreen, 0.7)};
|
||||||
--content_bg_info: #{transparentize($_blue, 0.5)};
|
--content_bg_info: #{transparentize($_blue, 0.5)};
|
||||||
--content_bg_danger: #{transparentize($_red, 0.5)};
|
--content_bg_danger: #{transparentize($_red, 0.5)};
|
||||||
--content_bg_backdrop: url('/images/noise.png') #{transparentize(
|
--content_bg_backdrop: url('/images/noise.png')
|
||||||
$_brown,
|
#{transparentize($_brown, 0.3)};
|
||||||
0.3
|
|
||||||
)};
|
|
||||||
--content_bg_hero: url('/images/noise.png') #{transparentize($_brown, 0.6)};
|
--content_bg_hero: url('/images/noise.png') #{transparentize($_brown, 0.6)};
|
||||||
|
|
||||||
// white shades (move to --vars)
|
// white shades (move to --vars)
|
||||||
|
|
|
@ -85,10 +85,8 @@ $_ocean: #25b0bc;
|
||||||
--gray_90: #{transparentize(white, 0.95)};
|
--gray_90: #{transparentize(white, 0.95)};
|
||||||
|
|
||||||
// page background
|
// page background
|
||||||
--page-background: 50% 50% / cover no-repeat url('/images/horizon_bg.svg') #{darken(
|
--page-background: 50% 50% / cover no-repeat url('/images/horizon_bg.svg')
|
||||||
$_cold,
|
#{darken($_cold, 4%)} fixed;
|
||||||
4%
|
|
||||||
)} fixed;
|
|
||||||
--page-background-top: linear-gradient(
|
--page-background-top: linear-gradient(
|
||||||
#{$_accent} -150%,
|
#{$_accent} -150%,
|
||||||
#{transparentize($_ocean, 0.99)} 100px,
|
#{transparentize($_ocean, 0.99)} 100px,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
COMMENT_BLOCK_TYPES,
|
COMMENT_BLOCK_TYPES,
|
||||||
ICommentBlock,
|
ICommentBlock,
|
||||||
} from '~/constants/comment';
|
} from '~/constants/comment';
|
||||||
|
import { headerHeight } from '~/constants/dom';
|
||||||
import { IFile, ValueOf } from '~/types';
|
import { IFile, ValueOf } from '~/types';
|
||||||
import { CONFIG } from '~/utils/config';
|
import { CONFIG } from '~/utils/config';
|
||||||
import {
|
import {
|
||||||
|
@ -213,3 +214,14 @@ export const sizeOf = (bytes: number): string => {
|
||||||
(bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'B'
|
(bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'B'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Tells if element is in view */
|
||||||
|
export const isFullyVisible = (element?: HTMLElement): boolean => {
|
||||||
|
if (!element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
|
||||||
|
return rect?.top > headerHeight && rect?.bottom < window.innerHeight;
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue