mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 21:06:42 +07:00
several bug fixes
This commit is contained in:
parent
f23b9166e1
commit
11348aa411
13 changed files with 281 additions and 110 deletions
|
@ -228,12 +228,14 @@ const NodeImageSlideBlock: FC<IProps> = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<ImageSwitcher
|
||||
total={images.length}
|
||||
current={current}
|
||||
onChange={changeCurrent}
|
||||
loaded={loaded}
|
||||
/>
|
||||
{!is_loading && (
|
||||
<ImageSwitcher
|
||||
total={images.length}
|
||||
current={current}
|
||||
onChange={changeCurrent}
|
||||
loaded={loaded}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={styles.image_container}
|
||||
|
@ -253,7 +255,7 @@ const NodeImageSlideBlock: FC<IProps> = ({
|
|||
is_active: index === current && loaded[index]
|
||||
})}
|
||||
ref={setRef(index)}
|
||||
key={file.id}
|
||||
key={node.updated_at + file.id}
|
||||
>
|
||||
<img
|
||||
className={styles.image}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { FC } from 'react';
|
||||
import * as styles from './styles.scss';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import classNames from 'classnames';
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { ERRORS } from '~/constants/errors';
|
||||
import { t } from '~/utils/trans';
|
||||
import React, { FC } from "react";
|
||||
import * as styles from "./styles.scss";
|
||||
import { Group } from "~/components/containers/Group";
|
||||
import classNames from "classnames";
|
||||
import { Filler } from "~/components/containers/Filler";
|
||||
import { ERRORS } from "~/constants/errors";
|
||||
import { t } from "~/utils/trans";
|
||||
|
||||
interface IProps {
|
||||
is_loading?: boolean;
|
||||
|
@ -17,8 +17,6 @@ const NodeNoComments: FC<IProps> = ({ is_loading = false }) => (
|
|||
<div className={styles.card}>{!is_loading && t(ERRORS.NO_COMMENTS)}</div>
|
||||
<div className={styles.card} />
|
||||
</Group>
|
||||
|
||||
<Filler />
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
@keyframes fade {
|
||||
0% {
|
||||
opacity: 0.25;
|
||||
// opacity: 0.25;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
// opacity: 0.1;
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
user-select: none;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
@include after_shade($node_bg);
|
||||
// @include after_shade($node_bg);
|
||||
|
||||
&:global(.is_loading) {
|
||||
opacity: 1;
|
||||
|
||||
.card {
|
||||
background: $placeholder_bg;
|
||||
animation: fade 0.5s infinite alternate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
opacity: 0.2;
|
||||
opacity: 0.3;
|
||||
border-radius: $radius;
|
||||
height: 96px;
|
||||
background: $comment_bg;
|
||||
background: $placeholder_bg;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -33,7 +37,7 @@
|
|||
color: transparentize(white, 0.5);
|
||||
flex: 0 0 $comment_height;
|
||||
|
||||
@include outer_shadow();
|
||||
// @include outer_shadow();
|
||||
|
||||
&:nth-child(2) {
|
||||
// animation-delay: -300ms !important;
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import React, { FC, useCallback, useEffect, useRef, useState, memo } from 'react';
|
||||
import * as styles from './styles.scss';
|
||||
import { INode } from '~/redux/types';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { NodePanelInner } from '~/components/node/NodePanelInner';
|
||||
import pick from 'ramda/es/pick';
|
||||
import React, {
|
||||
FC,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
memo
|
||||
} from "react";
|
||||
import * as styles from "./styles.scss";
|
||||
import { INode } from "~/redux/types";
|
||||
import { createPortal } from "react-dom";
|
||||
import { NodePanelInner } from "~/components/node/NodePanelInner";
|
||||
import pick from "ramda/es/pick";
|
||||
|
||||
interface IProps {
|
||||
node: Partial<INode>;
|
||||
|
@ -13,13 +20,25 @@ interface IProps {
|
|||
can_like: boolean;
|
||||
can_star: boolean;
|
||||
|
||||
is_loading?: boolean;
|
||||
|
||||
onEdit: () => void;
|
||||
onLike: () => void;
|
||||
onStar: () => void;
|
||||
}
|
||||
|
||||
const NodePanel: FC<IProps> = memo(
|
||||
({ node, layout, can_edit, can_like, can_star, onEdit, onLike, onStar }) => {
|
||||
({
|
||||
node,
|
||||
layout,
|
||||
can_edit,
|
||||
can_like,
|
||||
can_star,
|
||||
is_loading,
|
||||
onEdit,
|
||||
onLike,
|
||||
onStar
|
||||
}) => {
|
||||
const [stack, setStack] = useState(false);
|
||||
|
||||
const ref = useRef(null);
|
||||
|
@ -35,12 +54,12 @@ const NodePanel: FC<IProps> = memo(
|
|||
|
||||
useEffect(() => {
|
||||
getPlace();
|
||||
window.addEventListener('scroll', getPlace);
|
||||
window.addEventListener('resize', getPlace);
|
||||
window.addEventListener("scroll", getPlace);
|
||||
window.addEventListener("resize", getPlace);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', getPlace);
|
||||
window.removeEventListener('resize', getPlace);
|
||||
window.removeEventListener("scroll", getPlace);
|
||||
window.removeEventListener("resize", getPlace);
|
||||
};
|
||||
}, [layout]);
|
||||
|
||||
|
@ -57,6 +76,7 @@ const NodePanel: FC<IProps> = memo(
|
|||
can_edit={can_edit}
|
||||
can_like={can_like}
|
||||
can_star={can_star}
|
||||
is_loading={is_loading}
|
||||
/>,
|
||||
document.body
|
||||
)
|
||||
|
@ -69,6 +89,7 @@ const NodePanel: FC<IProps> = memo(
|
|||
can_edit={can_edit}
|
||||
can_like={can_like}
|
||||
can_star={can_star}
|
||||
is_loading={is_loading}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React, { FC } from 'react';
|
||||
import * as styles from './styles.scss';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { INode } from '~/redux/types';
|
||||
import classNames from 'classnames';
|
||||
import React, { FC } from "react";
|
||||
import * as styles from "./styles.scss";
|
||||
import { Group } from "~/components/containers/Group";
|
||||
import { Filler } from "~/components/containers/Filler";
|
||||
import { Icon } from "~/components/input/Icon";
|
||||
import { INode } from "~/redux/types";
|
||||
import classNames from "classnames";
|
||||
import { Placeholder } from "~/components/placeholders/Placeholder";
|
||||
|
||||
interface IProps {
|
||||
node: Partial<INode>;
|
||||
|
@ -13,6 +14,9 @@ interface IProps {
|
|||
can_edit: boolean;
|
||||
can_like: boolean;
|
||||
can_star: boolean;
|
||||
|
||||
is_loading: boolean;
|
||||
|
||||
onEdit: () => void;
|
||||
onLike: () => void;
|
||||
onStar: () => void;
|
||||
|
@ -21,20 +25,34 @@ interface IProps {
|
|||
const NodePanelInner: FC<IProps> = ({
|
||||
node: { title, user, is_liked, is_heroic },
|
||||
stack,
|
||||
|
||||
can_star,
|
||||
can_edit,
|
||||
can_like,
|
||||
|
||||
is_loading,
|
||||
|
||||
onStar,
|
||||
onEdit,
|
||||
onLike,
|
||||
onLike
|
||||
}) => {
|
||||
return (
|
||||
<div className={classNames(styles.wrap, { stack })}>
|
||||
<div className={styles.content}>
|
||||
<Group horizontal className={styles.panel}>
|
||||
<Filler>
|
||||
<div className={styles.title}>{title || '...'}</div>
|
||||
{user && user.username && <div className={styles.name}>~{user.username}</div>}
|
||||
<div className={styles.title}>
|
||||
{is_loading ? <Placeholder width="40%" /> : title || "..."}
|
||||
</div>
|
||||
{user && user.username && (
|
||||
<div className={styles.name}>
|
||||
{is_loading ? (
|
||||
<Placeholder width="100px" />
|
||||
) : (
|
||||
`~${user.username}`
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Filler>
|
||||
</Group>
|
||||
|
||||
|
|
31
src/components/node/NodeRelated/placeholder.tsx
Normal file
31
src/components/node/NodeRelated/placeholder.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React, { FC, memo } from "react";
|
||||
import styles from "./styles.scss";
|
||||
import cell_style from "~/components/node/NodeRelatedItem/styles.scss";
|
||||
import { Group } from "~/components/containers/Group";
|
||||
import { Placeholder } from "~/components/placeholders/Placeholder";
|
||||
import range from "ramda/es/range";
|
||||
import classNames from "classnames";
|
||||
|
||||
interface IProps {}
|
||||
|
||||
const NodeRelatedPlaceholder: FC<IProps> = memo(() => {
|
||||
return (
|
||||
<Group className={classNames(styles.wrap, styles.placeholder)}>
|
||||
<div className={styles.title}>
|
||||
<div className={styles.line} />
|
||||
<div className={styles.text}>
|
||||
<Placeholder />
|
||||
</div>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
{range(0, 6).map(el => (
|
||||
<div className={cell_style.item} key={el} />
|
||||
))}
|
||||
</div>
|
||||
</Group>
|
||||
);
|
||||
});
|
||||
|
||||
export { NodeRelatedPlaceholder };
|
|
@ -36,3 +36,15 @@
|
|||
.text {
|
||||
margin: 0 $gap;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
.text {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.grid {
|
||||
div {
|
||||
background: $placeholder_bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
17
src/components/node/NodeTags/placeholder.tsx
Normal file
17
src/components/node/NodeTags/placeholder.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React, { FC, memo } from "react";
|
||||
import { Tags } from "../Tags";
|
||||
import { ITag } from "~/redux/types";
|
||||
|
||||
interface IProps {
|
||||
is_editable?: boolean;
|
||||
tags: ITag[];
|
||||
onChange?: (tags: string[]) => void;
|
||||
}
|
||||
|
||||
const NodeTagsPlaceholder: FC<IProps> = memo(
|
||||
({ is_editable, tags, onChange }) => (
|
||||
<Tags tags={tags} is_editable={is_editable} onTagsChange={onChange} />
|
||||
)
|
||||
);
|
||||
|
||||
export { NodeTagsPlaceholder };
|
|
@ -6,13 +6,12 @@ import React, {
|
|||
useEffect,
|
||||
KeyboardEvent,
|
||||
ChangeEvent,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { TagField } from '~/components/containers/TagField';
|
||||
import { ITag } from '~/redux/types';
|
||||
import { Tag } from '~/components/node/Tag';
|
||||
import uniq from 'ramda/es/uniq';
|
||||
import assocPath from 'ramda/es/assocPath';
|
||||
useRef
|
||||
} from "react";
|
||||
import { TagField } from "~/components/containers/TagField";
|
||||
import { ITag } from "~/redux/types";
|
||||
import { Tag } from "~/components/node/Tag";
|
||||
import uniq from "ramda/es/uniq";
|
||||
|
||||
type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||
tags: Partial<ITag>[];
|
||||
|
@ -20,8 +19,13 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
|
|||
onTagsChange?: (tags: string[]) => void;
|
||||
};
|
||||
|
||||
export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props }) => {
|
||||
const [input, setInput] = useState('');
|
||||
export const Tags: FC<IProps> = ({
|
||||
tags,
|
||||
is_editable,
|
||||
onTagsChange,
|
||||
...props
|
||||
}) => {
|
||||
const [input, setInput] = useState("");
|
||||
const [data, setData] = useState([]);
|
||||
const timer = useRef(null);
|
||||
|
||||
|
@ -35,17 +39,17 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props })
|
|||
|
||||
const onKeyUp = useCallback(
|
||||
({ key }: KeyboardEvent) => {
|
||||
if (key === 'Backspace' && input === '' && data.length) {
|
||||
if (key === "Backspace" && input === "" && data.length) {
|
||||
setData(data.slice(0, data.length - 1));
|
||||
setInput(data[data.length - 1].title);
|
||||
}
|
||||
|
||||
if (key === 'Enter' || key === ',' || key === 'Comma') {
|
||||
if (key === "Enter" || key === "," || key === "Comma") {
|
||||
setData(
|
||||
uniq([
|
||||
...data,
|
||||
...input
|
||||
.split(',')
|
||||
.split(",")
|
||||
.map((title: string) =>
|
||||
title
|
||||
.trim()
|
||||
|
@ -55,11 +59,11 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props })
|
|||
.filter(el => el.length > 0)
|
||||
.filter(el => !tags.some(tag => tag.title.trim() === el.trim()))
|
||||
.map(title => ({
|
||||
title,
|
||||
})),
|
||||
title
|
||||
}))
|
||||
])
|
||||
);
|
||||
setInput('');
|
||||
setInput("");
|
||||
}
|
||||
},
|
||||
[input, setInput, data, setData]
|
||||
|
@ -71,12 +75,16 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props })
|
|||
|
||||
if (!items.length) return;
|
||||
setData(items);
|
||||
setInput('');
|
||||
setInput("");
|
||||
onTagsChange(uniq([...tags, ...items]).map(tag => tag.title));
|
||||
}, [tags, data, onTagsChange, input, setInput]);
|
||||
|
||||
useEffect(() => {
|
||||
setData(data.filter(({ title }) => !tags.some(tag => tag.title.trim() === title.trim())));
|
||||
setData(
|
||||
data.filter(
|
||||
({ title }) => !tags.some(tag => tag.title.trim() === title.trim())
|
||||
)
|
||||
);
|
||||
}, [tags]);
|
||||
|
||||
return (
|
||||
|
@ -90,7 +98,12 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props })
|
|||
))}
|
||||
|
||||
{is_editable && (
|
||||
<Tag tag={{ title: input }} onInput={onInput} onKeyUp={onKeyUp} onBlur={onSubmit} />
|
||||
<Tag
|
||||
tag={{ title: input }}
|
||||
onInput={onInput}
|
||||
onKeyUp={onKeyUp}
|
||||
onBlur={onSubmit}
|
||||
/>
|
||||
)}
|
||||
</TagField>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.placeholder {
|
||||
height: 1em;
|
||||
width: 120px;
|
||||
background: transparentize(white, 0.95);
|
||||
background: $placeholder_bg;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue