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

editor scrollbars

This commit is contained in:
muerwre 2019-07-29 18:51:10 +07:00
parent e687a3f5f3
commit 14c5f67d49
15 changed files with 349 additions and 18 deletions

51
package-lock.json generated
View file

@ -2820,6 +2820,11 @@
} }
} }
}, },
"add-px-to-style": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz",
"integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo="
},
"adjust-sourcemap-loader": { "adjust-sourcemap-loader": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz",
@ -6632,6 +6637,16 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"dom-css": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz",
"integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=",
"requires": {
"add-px-to-style": "1.0.0",
"prefix-style": "2.0.1",
"to-camel-case": "1.0.0"
}
},
"dom-helpers": { "dom-helpers": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
@ -13106,6 +13121,11 @@
"uniqs": "^2.0.0" "uniqs": "^2.0.0"
} }
}, },
"prefix-style": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz",
"integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY="
},
"prelude-ls": { "prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@ -15963,12 +15983,25 @@
"integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
"dev": true "dev": true
}, },
"to-camel-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz",
"integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=",
"requires": {
"to-space-case": "^1.0.0"
}
},
"to-fast-properties": { "to-fast-properties": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
"integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
"dev": true "dev": true
}, },
"to-no-case": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
"integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo="
},
"to-object-path": { "to-object-path": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@ -16011,6 +16044,14 @@
"repeat-string": "^1.6.1" "repeat-string": "^1.6.1"
} }
}, },
"to-space-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
"integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=",
"requires": {
"to-no-case": "^1.0.0"
}
},
"toposort": { "toposort": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
@ -16082,6 +16123,16 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true "dev": true
}, },
"tt-react-custom-scrollbars": {
"version": "4.2.1-tt2",
"resolved": "https://registry.npmjs.org/tt-react-custom-scrollbars/-/tt-react-custom-scrollbars-4.2.1-tt2.tgz",
"integrity": "sha512-gMEVHHOClNJXM1d/p4PrLdXtCU2JzWRtcZdzUkXgck8sgzkxwFwSDNc3scnTk21sSKG2GSgf7G54sboXwsMVlg==",
"requires": {
"dom-css": "^2.0.0",
"prop-types": "^15.5.10",
"raf": "^3.1.0"
}
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",

View file

@ -89,6 +89,7 @@
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"sass-resources-loader": "^2.0.0", "sass-resources-loader": "^2.0.0",
"scrypt": "^6.0.3", "scrypt": "^6.0.3",
"throttle-debounce": "^2.1.0" "throttle-debounce": "^2.1.0",
"tt-react-custom-scrollbars": "latest"
} }
} }

View file

@ -0,0 +1,25 @@
import React, { FC, HTMLAttributes, ReactChild, ReactChildren } from 'react';
import * as styles from './styles.scss';
import classNames = require("classnames");
type IProps = HTMLAttributes<HTMLDivElement> & {
children: any;
size: number;
}
const CellGrid: FC<IProps> = ({
children,
size,
className,
...props
}) => (
<div
className={classNames(styles.grid, className)}
style={{ gridTemplateColumns: `repeat(auto-fit, minmax(${size}px, 1fr))` }}
{...props}
>
{children}
</div>
);
export { CellGrid };

View file

@ -0,0 +1,6 @@
.grid {
display: grid;
grid-auto-rows: 1fr;
grid-column-gap: $gap;
grid-row-gap: $gap;
}

View file

@ -7,6 +7,7 @@ type IProps = React.HTMLAttributes<HTMLDivElement> & {
top?: boolean; top?: boolean;
bottom?: boolean; bottom?: boolean;
wrap?: boolean; wrap?: boolean;
seamless?: boolean;
}; };
const Group: FC<IProps> = ({ const Group: FC<IProps> = ({
@ -16,6 +17,7 @@ const Group: FC<IProps> = ({
top = false, top = false,
bottom = false, bottom = false,
wrap = false, wrap = false,
seamless = false,
...props ...props
}) => ( }) => (
<div <div
@ -27,6 +29,7 @@ const Group: FC<IProps> = ({
[styles.top]: top, [styles.top]: top,
[styles.bottom]: bottom, [styles.bottom]: bottom,
[styles.wrap]: wrap, [styles.wrap]: wrap,
[styles.seamless]: seamless,
}, },
className, className,
)} )}

View file

@ -39,4 +39,8 @@
&.wrap { &.wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }
&.seamless > * {
margin: 0;
}
} }

View file

@ -0,0 +1,19 @@
import React, { FC, HTMLAttributes } from 'react';
import * as styles from './styles.scss';
import classNames = require("classnames");
type IProps = HTMLAttributes<HTMLDivElement> & {
}
const Panel: FC<IProps> = ({
className,
children,
...props
}) => (
<div className={classNames(styles.panel, className)} {...props}>
{children}
</div>
);
export { Panel };

View file

@ -0,0 +1,3 @@
.panel {
@include outer_shadow();
}

View file

@ -0,0 +1,45 @@
import React, { MouseEventHandler, useEffect, useState } from 'react';
import * as styles from './styles.scss';
import { Scrollbars } from 'tt-react-custom-scrollbars';
import classNames from 'classnames';
interface IProps {
children: Element | React.ReactChild;
style?: React.CSSProperties;
className?: string;
autoHeight?: boolean;
autoHeightMax?: number;
onRef?: (el: any) => void;
onScroll?: MouseEventHandler;
onScrollStop?: MouseEventHandler;
}
export const Scroll = ({
children,
className = '',
onRef = null,
...props
}: IProps) => {
const [ref, setRef] = useState(null);
useEffect(() => {
if (onRef && ref) return onRef(ref);
}, [ref, onRef]);
return (
<Scrollbars
className={classNames(styles.container, className)}
renderTrackHorizontal={data => <div className={styles.track_horizontal} {...data} />}
renderTrackVertical={data => <div className={styles.track_vertical} {...data} />}
renderThumbHorizontal={data => <div className={styles.thumb_horizontal} {...data} />}
renderThumbVertical={data => <div className={styles.thumb_vertical} {...data} />}
renderView = {data => <div className={styles.view} {...data} />}
hideTracksWhenNotNeeded
universal
ref={setRef}
{ ...props }
>
{children}
</Scrollbars>
);
};

View file

@ -0,0 +1,107 @@
.container {
min-height: 50px;
}
.track_vertical {
height: 100%;
width: 20px !important;
position: absolute;
right: 0;
top: 0;
opacity: $scroll_inactive_opacity;
transition: opacity 0.25s;
padding: $gap 0;
box-sizing: border-box;
z-index: 1;
&:hover, &:active {
opacity: 0.7;
}
&::after {
content: ' ';
width: 1px;
background: $scroll_color;
position: absolute;
left: 12px;
top: 0;
height: 100%;
opacity: 0.5;
z-index: 1;
}
}
.thumb_vertical {
position: absolute;
left: 0;
top: 0;
width: 100%;
display: flex;
align-items: stretch;
justify-content: center;
cursor: grab;
&::after {
position: absolute;
content: ' ';
width: 5px;
top: 0;
left: 10px;
height: 100%;
border-radius: 3px;
background: $scroll_color;
z-index: 2;
}
}
.track_horizontal {
height: 20px !important;
width: 100% !important;
position: absolute;
left: 0;
bottom: 0;
opacity: 0.3;
transition: opacity 0.25s;
padding: 0 $gap;
box-sizing: border-box;
z-index: 10;
&:hover, &:active {
opacity: 0.7;
}
&::after {
content: ' ';
height: 1px;
background: $scroll_color;
position: absolute;
top: 12px;
left: 0;
width: 100%;
opacity: 0.5;
z-index: 1;
}
}
.thumb_horizontal {
position: absolute;
left: 0;
top: 0;
width: 100%;
display: flex;
align-items: stretch;
justify-content: center;
cursor: grab;
&::after {
position: absolute;
content: ' ';
height: 5px;
top: 10px;
left: 0;
width: 100%;
border-radius: 3px;
background: $scroll_color;
z-index: 2;
}
}

View file

@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import * as styles from './styles.scss';
const style = require('./style.scss');
interface ITextInputProps { interface ITextInputProps {
type?: 'text' | 'password', type?: 'text' | 'password',
@ -19,16 +18,16 @@ export const TextInput: React.FunctionComponent<ITextInputProps> = ({
value='', value='',
}) => ( }) => (
<div <div
className={style.wrapper} className={styles.wrapper}
> >
<div className={styles.container}>
{ {
label && label &&
<div className={style.label}>{label}</div> <div className={styles.label}>{label}</div>
} }
<div className={style.container}>
<input <input
placeholder={placeholder} placeholder={placeholder}
className={style.input} className={styles.input}
type={type} type={type}
onChange={onChange} onChange={onChange}
value={value} value={value}

View file

@ -1,15 +1,22 @@
.wrapper { .wrapper {
height: $input_height;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
height: $input_height;
} }
.label { .label {
background: $input_bg_color; background: transparentize(black, 0.8);
font-size: 10px; font: $font_14_medium;
// color: transparentize(white, 0.5);
text-transform: uppercase; text-transform: uppercase;
font-weight: 600;
padding: 2px $gap; padding: 2px $gap;
display: flex;
align-items: center;
justify-content: flex-start;
border-radius: $radius 0 0 $radius;
@include input_shadow();
} }
.container { .container {
@ -20,7 +27,7 @@
flex: 1 0; flex: 1 0;
display: flex; display: flex;
align-self: stretch; align-self: stretch;
align-items: center; align-items: stretch;
justify-content: center; justify-content: center;
@include input_shadow(); @include input_shadow();
} }

View file

@ -2,18 +2,47 @@ import React, { FC } from 'react';
import { Card } from "~/components/containers/Card"; import { Card } from "~/components/containers/Card";
import * as styles from './styles.scss'; import * as styles from './styles.scss';
import { Group } from "~/components/containers/Group"; import { Group } from "~/components/containers/Group";
import { Padder } from "~/components/containers/Padder";
import { CellGrid } from "~/components/containers/CellGrid";
import { Panel } from "~/components/containers/Panel";
import { TextInput } from "~/components/input/TextInput";
import classNames = require("classnames");
import { Scroll } from "~/components/containers/Scroll";
interface IProps {} interface IProps {}
const EditorExample: FC<IProps> = () => ( const EditorExample: FC<IProps> = () => (
<Card className={styles.wrap}> <Card className={styles.wrap}>
<Group horizontal className={styles.group}> <Group horizontal className={styles.group} seamless>
<div className={styles.editor}> <div className={styles.editor}>
editor <Panel className={styles.editor_panel}>
<TextInput onChange={console.log} label="Название" />
</Panel>
<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 className={styles.editor_panel}>
Cover panel
</Panel>
</div> </div>
<div className={styles.panel}> <div className={styles.panel}>
<Group>
<Card>
<Padder>
panel panel
</Padder>
</Card>
</Group>
</div> </div>
</Group> </Group>
</Card> </Card>

View file

@ -3,6 +3,7 @@
align-items: stretch; align-items: stretch;
justify-content: center; justify-content: center;
display: flex; display: flex;
background: $editor_bg;
} }
.group { .group {
@ -13,10 +14,35 @@
} }
.panel { .panel {
background: red; background: $editor_panel_bg;
flex: 0 0 33%; flex: 0 0 33%;
border-radius: 0 $radius $radius 0;
padding: $gap;
box-sizing: border-box;
@include outer_shadow();
} }
.editor { .editor {
flex: 1; flex: 1;
display: flex;
align-items: stretch;
flex-direction: column;
}
.editor_panel {
padding: $gap;
}
.editor_image_panel {
flex: 1;
}
.editor_image_container {
flex: 1;
}
.editor_image {
background: lighten($main_bg_color, 20%);
padding-bottom: 100%;
border-radius: $radius;
} }

View file

@ -36,3 +36,9 @@ $panel_bg: #191919;
$node_bg: #111111; $node_bg: #111111;
$node_image_bg: #131313; $node_image_bg: #131313;
$node_title_background: #191919; $node_title_background: #191919;
$editor_panel_bg: lighten($main_bg_color, 5%);
$editor_bg: lighten($main_bg_color, 10%);
$scroll_color: $red_gradient;
$scroll_inactive_opacity: 0.5;