) => {
42 | const color = e.target.value;
43 | const shadowColors: ShadowColors = {
44 | ...this.state[position],
45 | [property]: color
46 | };
47 |
48 | this.setState({
49 | [position]: shadowColors
50 | } as any);
51 | }
52 |
53 | onShadowSizeChange = (e: any) => {
54 | const shadowSize = e.target.value;
55 | this.setState({shadowSize});
56 | }
57 |
58 | render() {
59 | const {bottomShadowColors, topShadowColors, shadowSize} = this.state;
60 |
61 | return (
62 |
63 |
64 |
65 |
66 | 🕸 react-scroll-shadow 🕸
67 |
68 |
69 | Top shadow active
70 |
71 |
72 | Top shadow inactive
73 |
74 |
75 | Bottom shadow active
76 |
77 |
78 | Bottom shadow inactive
79 |
80 | Shadow size
81 |
82 |
83 |
84 | Header
85 |
90 | {items}
91 |
92 | Footer
93 |
94 |
95 | );
96 | }
97 | }
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import App from './app';
4 |
5 | const app = document.getElementById('app');
6 |
7 | ReactDOM.render(, app);
--------------------------------------------------------------------------------
/example/styled.ts:
--------------------------------------------------------------------------------
1 | import styled, {injectGlobal} from 'styled-components';
2 |
3 | injectGlobal`
4 | body, html {
5 | height: 100%;
6 | margin: 0;
7 | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
8 | font-size: 14px;
9 | }
10 |
11 | body {
12 | background: #69b7eb;
13 | }
14 |
15 | #app {
16 | height: 100%;
17 | }
18 | `;
19 |
20 | export const Item = styled.div`
21 | border-bottom: 1px solid #ccc;
22 | padding: 15px;
23 | margin: 0 20px;
24 | `;
25 |
26 | export const AppWrapper = styled.div`
27 | height: 100%;
28 | width: 800px;
29 | margin: 0 auto;
30 | display: flex;
31 | max-height: 80vh;
32 | max-width: 90vw;
33 | width: 100%;
34 | height: 100%;
35 | position: absolute;
36 | top: 50%;
37 | left: 50%;
38 | transform: translate(-50%, -50%);
39 | border-radius: 3px;
40 | overflow: hidden;
41 | background-color: white;
42 | box-shadow: 1px 5px 5px 1px rgba(0,0,0,.3);
43 | `;
44 |
45 | export const AppHeader = styled.div`
46 | flex-shrink: 0;
47 | height: 40px;
48 | width: 100%;
49 | text-align: center;
50 | display: flex;
51 | align-items: center;
52 | justify-content: center;
53 | font-size: 20px;
54 | `;
55 |
56 | export const AppFooter = styled.div`
57 | flex-shrink: 0;
58 | height: 40px;
59 | width: 100%;
60 | text-align: center;
61 | display: flex;
62 | align-items: center;
63 | justify-content: center;
64 | font-size: 20px;
65 | `;
66 |
67 | export const ColorWrapper = styled.div`
68 | display: flex;
69 | justify-content: space-between;
70 | height: 50px;
71 | border-bottom: 1px solid #ccc;
72 | align-items: center;
73 |
74 | input {
75 | margin-left: 10px;
76 | }
77 | `;
78 |
79 | export const ColorsWrapper = styled.div`
80 | overflow: auto;
81 | padding: 20px;
82 | border-right: 1px solid #e2e2e2;
83 | `;
84 |
85 | export const ScrollWrapper = styled.div`
86 | flex: 1;
87 | display: flex;
88 | flex-direction: column;
89 | margin: 0 30px;
90 | `;
91 |
92 | export const Title = styled.a`
93 | border-bottom: 2px dashed;
94 | color: #f4d6db;
95 | margin: 0;
96 | font-size: 23px;
97 | margin-bottom: 10px;
98 | text-decoration: none;
99 | display: block;
100 |
101 | &:hover {
102 | border-bottom-style: solid;
103 | }
104 | `;
105 |
106 | export const ShadowSize = styled.input`
107 | outline: none;
108 | margin: 15px;
109 | width: 50px;
110 | text-align: center;
111 | font-size: 15px;
112 | border-radius: 5px;
113 | border: 1px solid #ccc;
114 | `;
--------------------------------------------------------------------------------
/example/utils.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'react';
2 |
3 | export class ToolboxApp extends Component
{
4 | onCheckboxChange = (propName: any) => () => {
5 | const currentValue = (this.state as any)[propName];
6 | this.setState({ [propName]: !currentValue } as any);
7 | };
8 |
9 | onFieldTextChange = (propName: any) => (e: any) => {
10 | const value = e.target.value;
11 |
12 | (this as any).setState({
13 | [propName]: value
14 | });
15 | }
16 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-scroll-shadow",
3 | "version": "2.0.2",
4 | "description": "Pure CSS shadow to indicate more content in scrollable area",
5 | "main": "dist/es5/index.js",
6 | "scripts": {
7 | "bootstrap": "ts-react-toolbox init",
8 | "dev": "ts-react-toolbox dev",
9 | "test": "ts-react-toolbox test",
10 | "test:ci": "ts-react-toolbox test --runInBand --coverage",
11 | "build": "ts-react-toolbox build",
12 | "release": "ts-react-toolbox release",
13 | "lint": "ts-react-toolbox lint",
14 | "static": "ts-react-toolbox publish",
15 | "format": "ts-react-toolbox format",
16 | "analyze": "ts-react-toolbox analyze"
17 | },
18 | "repository": "git@github.com:zzarcon/react-scroll-shadow.git",
19 | "author": "Hector Leon Zarco Garcia ",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "ts-react-toolbox": "^0.1.22"
23 | },
24 | "dependencies": {},
25 | "engines": {
26 | "node": ">=8.5.0"
27 | },
28 | "peerDependencies": {
29 | "react": "^16.3.0",
30 | "styled-components": "^3.4.10"
31 | },
32 | "types": "dist/es5/index.d.ts",
33 | "files": [
34 | "dist"
35 | ],
36 | "keywords": [
37 | "react",
38 | "scroll",
39 | "shadow",
40 | "overflow",
41 | "height",
42 | "css",
43 | "pure-css",
44 | "scrollable",
45 | "scrollbar"
46 | ],
47 | "jsnext:main": "dist/es2015/index.js",
48 | "module": "dist/es2015/index.js"
49 | }
50 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './scroll-shadow';
2 | export {ScrollShadow as default} from './scroll-shadow';
--------------------------------------------------------------------------------
/src/scroll-shadow.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Component} from 'react';
3 | import {ScrollableContent, ScrollableWrapper} from './styled';
4 |
5 | export interface ShadowColors {
6 | inactive: string;
7 | active: string;
8 | }
9 |
10 | export interface ScrollShadowProps {
11 | height?: string;
12 | bottomShadowColors?: ShadowColors;
13 | topShadowColors?: ShadowColors;
14 | shadowSize?: number;
15 | }
16 |
17 | export class ScrollShadow extends Component {
18 | static defaultProps = {
19 | shadowSize: 2,
20 | bottomShadowColors: {
21 | inactive: 'white',
22 | active: 'gray'
23 | },
24 | topShadowColors: {
25 | inactive: 'white',
26 | active: 'gray'
27 | }
28 | };
29 | render() {
30 | const {
31 | children,
32 | height,
33 | bottomShadowColors,
34 | topShadowColors,
35 | shadowSize
36 | } = this.props;
37 | const style = {height};
38 |
39 | return (
40 |
45 |
51 | {children}
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | export default ScrollShadow;
--------------------------------------------------------------------------------
/src/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | // @ts-ignore: unused variable
3 | // prettier-ignore
4 | import { HTMLAttributes, ClassAttributes } from 'react';
5 |
6 | export interface ScrollableWrapperProps {
7 | size: number;
8 | topShadowActiveColor: string;
9 | bottomShadowActiveColor: string;
10 | }
11 |
12 | export interface ScrollableContentProps {
13 | size: number;
14 | topShadowInactiveColor: string;
15 | bottomShadowInactiveColor: string;
16 | }
17 |
18 | export const ScrollableWrapper = styled.div`
19 | display: flex;
20 | flex: 1 1 auto;
21 | width: 100%;
22 | position: relative;
23 | // Next line is important hack/fix for Firefox
24 | // https://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexbox
25 | min-height:0;
26 |
27 | &::before{
28 | content: '';
29 | position: absolute;
30 | top:0;
31 | left:0;
32 | right:0;
33 | height: ${({size}: ScrollableWrapperProps) => size}px;
34 | background: ${({topShadowActiveColor}: ScrollableWrapperProps) => topShadowActiveColor};
35 | z-index: 10;
36 | }
37 | &::after{
38 | content: '';
39 | position: absolute;
40 | bottom:0;
41 | left:0;
42 | right:0;
43 | height: ${({size}: ScrollableWrapperProps) => size}px;
44 | background: ${({bottomShadowActiveColor}: ScrollableWrapperProps) => bottomShadowActiveColor};
45 | z-index: 10;
46 | }
47 | `;
48 |
49 | export const ScrollableContent = styled.div`
50 | flex: 1 1 auto;
51 | overflow-y: auto;
52 | overflow-x: hidden;
53 |
54 | display: flex;
55 | flex-direction: column;
56 |
57 | &::before{
58 | content: '';
59 | height: ${({size}: ScrollableContentProps) => size}px;
60 | width: 100%;
61 | background: ${({topShadowInactiveColor}: ScrollableContentProps) => topShadowInactiveColor};
62 | flex-shrink: 0;
63 |
64 | z-index: 11;
65 | // Next line is important hack/fix for Safari
66 | // https://stackoverflow.com/questions/40895387/z-index-not-working-on-safari-fine-on-firefox-and-chrome
67 | transform: translate3d(0,0,0);
68 | }
69 | &::after{
70 | content: '';
71 | height: ${({size}: ScrollableContentProps) => size}px;;
72 | width: 100%;
73 | background: ${({bottomShadowInactiveColor}: ScrollableContentProps) => bottomShadowInactiveColor};
74 | flex-grow: 1;
75 | flex-shrink: 0;
76 |
77 | z-index: 11;
78 | // Next line is important hack/fix for Safari
79 | // https://stackoverflow.com/questions/40895387/z-index-not-working-on-safari-fine-on-firefox-and-chrome
80 | transform: translate3d(0,0,0);
81 | }
82 | `;
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "removeComments": true,
5 | "target": "es5",
6 | "lib": [
7 | "dom",
8 | "es5",
9 | "scripthost",
10 | "es2015.collection",
11 | "es2015.symbol",
12 | "es2015.iterable",
13 | "es2015.promise"
14 | ],
15 | "jsx": "react"
16 | }
17 | }
--------------------------------------------------------------------------------