├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.js
├── .vscode
└── settings.json
├── README.md
├── demo.gif
├── package.json
├── packages
└── react-drag-sizing
│ ├── .gitignore
│ ├── .npmrc
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ ├── DragHandler.tsx
│ ├── DragSizing.tsx
│ ├── index.ts
│ ├── module.d.ts
│ ├── types.ts
│ └── util.ts
│ └── tsconfig.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.tsx
├── Home.module.css
├── Home.tsx
├── __tests__
│ └── react-drag-sizing
│ │ └── basic.tsx
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
└── serviceWorker.ts
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser
3 | extends: [
4 | 'react-app',
5 | // 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
6 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
7 | 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
8 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
9 | ],
10 | parserOptions: {
11 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
12 | sourceType: 'module', // Allows for the use of imports
13 | ecmaFeatures: {
14 | jsx: true, // Allows for the parsing of JSX
15 | },
16 | },
17 | rules: {
18 | 'prefer-const': 'error',
19 | // 'prefer-const': 'off',
20 | 'react/display-name': 'off',
21 | 'react/prop-types': 'off',
22 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
23 | '@typescript-eslint/explicit-function-return-type': 'off',
24 | '@typescript-eslint/no-use-before-define': 'off',
25 | '@typescript-eslint/prefer-interface': 'off',
26 | },
27 | settings: {
28 | react: {
29 | version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
30 | },
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 | engine-strict = true
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/dist
3 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'es5',
4 | singleQuote: true,
5 | // printWidth: 120,
6 | tabWidth: 2,
7 | };
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "node_modules": true
4 | },
5 | "files.associations": {
6 | ".npmrc": "dotenv"
7 | },
8 | // "editor.formatOnSave": true,
9 | "editor.codeActionsOnSave": {
10 | "source.organizeImports": true,
11 | "source.fixAll.eslint": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-drag-sizing
2 |
3 |
4 |
5 |
6 |
7 | - [x] "Drag to resize" (sizing) as React Component
8 | - [x] Rewritten with TS & React-hooks
9 | - [ ] Polyfill workaround with React < 16.8
10 | - [x] Support both mouse & touch
11 | - [x] Rollup bundle for both esm & umd
12 | - [x] Default handlerWidth=16, handlerOffset=-w/2, handlerZIndex=10
13 | - [x] Legacy branch: https://github.com/fritx/react-drag-sizing/tree/legacy
14 | - [x] Live Demo: https://fritx.github.io/react-drag-sizing
15 |
16 | ```sh
17 | npm i -S react-drag-sizing
18 | ```
19 |
20 | ```tsx
21 | import DragSizing from 'react-drag-sizing'
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ```
38 |
39 | #### Props
40 |
41 | ```tsx
42 | export type MEvent = MouseEvent | TouchEvent;
43 | export type RdsMEvent =
44 | | MouseEvent
45 | | (TouchEvent & {
46 | clientX: number;
47 | clientY: number;
48 | });
49 |
50 | export interface DragSizingProps {
51 | border: 'top' | 'bottom' | 'left' | 'right';
52 | onStart?: (e: RdsMEvent) => void;
53 | onEnd?: (e: RdsMEvent) => void;
54 | onUpdate?: (e: RdsMEvent) => void;
55 | id?: string;
56 | className?: string;
57 | style?: React.CSSProperties;
58 | handlerClassName?: string;
59 | handlerStyle?: React.CSSProperties;
60 | handlerWidth?: number;
61 | handlerOffset?: number;
62 | handlerZIndex?: number;
63 | }
64 | ```
65 |
66 | **hooking event listeners**
67 |
68 | ```tsx
69 | handleEditorSizingStart = () => {
70 | // const nearBottom = scrollTop > ...
71 | setShouldStickToBottom(nearBottom);
72 | };
73 | handleEditorSizingEnd = () => {
74 | if (shouldStickToBottom) {
75 | scrollToBottom();
76 | }
77 | };
78 |
79 |
84 |
85 | ;
86 | ```
87 |
88 | **for umd / \
92 |
93 |
94 |
95 | ```
96 |
97 | ```js
98 | // myapp.js
99 | let React = window.React;
100 | let ReactDOM = window.ReactDOM;
101 | let { DragSizing } = window.ReactDragSizing;
102 |
103 | ReactDOM.render(
104 |
105 | Left bar
106 |
111 | Main content
112 |
113 |
,
114 | mountNode
115 | );
116 | ```
117 |
118 | **for react < 16.8 we need hooks polyfill workaround**
119 |
120 | ```tsx
121 | // todo
122 | ```
123 |
124 | ---
125 |
126 | This project was bootstrapped with [create-react-library](https://github.com/transitive-bullshit/create-react-library) & [react-ts-demo](https://github.com/fritx/react-ts-demo).
127 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fritx/react-drag-sizing/152732df9f9c945fbad017250cd054965f7d2da2/demo.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-drag-sizing-repo",
3 | "version": "0.0.0",
4 | "private": true,
5 | "engines": {
6 | "pnpm": "please-use-yarn",
7 | "npm": "please-use-yarn",
8 | "yarn": "*"
9 | },
10 | "scripts": {
11 | "postinstall": "cd ./packages/react-drag-sizing && yarn && npm run build",
12 | "lib:build": "cd ./packages/react-drag-sizing && npm run build",
13 | "lib:watch": "cd ./packages/react-drag-sizing && npm run watch",
14 | "lib:publish": "cd ./packages/react-drag-sizing && npm publish",
15 | "deploy": "npm run build && gh-pages -d ./build",
16 | "start": "react-scripts start",
17 | "build": "npm run lib:build && PUBLIC_URL=. react-scripts build",
18 | "coverage": "CI=true npm test -- --coverage",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "dependencies": {
23 | "react": "^16.8.6",
24 | "react-dom": "^16.8.6",
25 | "react-drag-sizing": "file:packages/react-drag-sizing",
26 | "react-editor": "^2.0.0-alpha.5",
27 | "react-router-dom": "^5.1.2"
28 | },
29 | "devDependencies": {
30 | "@types/jest": "^25.1.3",
31 | "@types/node": "^13.7.4",
32 | "@types/react": "^16.9.22",
33 | "@types/react-dom": "^16.9.5",
34 | "@types/react-router-dom": "^5.1.3",
35 | "eslint-config-prettier": "^6.10.0",
36 | "eslint-plugin-prettier": "^3.1.2",
37 | "eslint-plugin-react": "^7.18.3",
38 | "gh-pages": "^2.2.0",
39 | "prettier": "^1.19.1",
40 | "react-scripts": "^3.4.0",
41 | "typescript": "^3.8.2"
42 | },
43 | "jest": {
44 | "collectCoverageFrom": [
45 | "/packages/react-drag-sizing/dist/*.{js,jsx,ts,tsx}",
46 | "!**/node_modules/**"
47 | ]
48 | },
49 | "eslintConfig": {
50 | "extends": "react-app"
51 | },
52 | "browserslist": {
53 | "production": [
54 | ">0.2%",
55 | "not dead",
56 | "not op_mini all"
57 | ],
58 | "development": [
59 | "last 1 chrome version",
60 | "last 1 firefox version",
61 | "last 1 safari version"
62 | ]
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-drag-sizing",
3 | "version": "0.2.1",
4 | "description": "",
5 | "author": "fritx",
6 | "license": "MIT",
7 | "repository": "fritx/react-drag-sizing",
8 | "main": "dist/index.js",
9 | "module": "dist/index.es.js",
10 | "jsnext:main": "dist/index.es.js",
11 | "scripts": {
12 | "prepublishOnly": "git stash -u && cp ../../README.md ./ && npm run build",
13 | "postpublish": "rm README.md && git stash apply",
14 | "build": "rollup -c",
15 | "watch": "rollup -c -w"
16 | },
17 | "peerDependencies": {
18 | "react": ">= 16.8",
19 | "react-dom": ">= 16.8"
20 | },
21 | "devDependencies": {
22 | "@rollup/plugin-commonjs": "^11.0.2",
23 | "@rollup/plugin-node-resolve": "^7.1.1",
24 | "@rollup/plugin-url": "^4.0.2",
25 | "rollup": "^1.31.1",
26 | "rollup-plugin-peer-deps-external": "^2.2.2",
27 | "rollup-plugin-postcss": "^2.1.1",
28 | "rollup-plugin-typescript2": "^0.26.0"
29 | },
30 | "dependencies": {},
31 | "files": [
32 | "dist"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjs from '@rollup/plugin-commonjs';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import external from 'rollup-plugin-peer-deps-external';
4 | // import postcss from 'rollup-plugin-postcss-modules'
5 | import postcss from 'rollup-plugin-postcss';
6 | import typescript from 'rollup-plugin-typescript2';
7 | import pkg from './package.json';
8 |
9 | const outputOptions = {
10 | exports: 'named',
11 | sourcemap: true,
12 | };
13 |
14 | export default {
15 | input: 'src/index.ts',
16 | output: [
17 | {
18 | ...outputOptions,
19 | file: pkg.module,
20 | format: 'es',
21 | },
22 | {
23 | ...outputOptions,
24 | file: pkg.main,
25 | format: 'umd',
26 | name: 'ReactDragSizing',
27 | globals: {
28 | react: 'React',
29 | },
30 | },
31 | ],
32 | plugins: [
33 | external(),
34 | postcss({
35 | modules: true,
36 | }),
37 | resolve(),
38 | typescript({
39 | rollupCommonJSResolveHack: true,
40 | clean: true,
41 | }),
42 | commonjs(),
43 | ],
44 | };
45 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/DragHandler.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useRef, useState } from 'react';
2 | import { DragHandlerData, DragHandlerProps } from './types';
3 |
4 | export const DragHandler: React.FC = props => {
5 | const { dir, onStart, onEnd, onUpdate, className, style, children } = props;
6 |
7 | const [isDragging, setIsDragging] = useState(false);
8 | const listenersRef = useRef(null);
9 |
10 | const handleMouseMove = useCallback(
11 | e => {
12 | onUpdate(e);
13 | },
14 | [onUpdate]
15 | );
16 |
17 | const cleanMouseListeners = useCallback(() => {
18 | const oldRef = listenersRef.current;
19 | if (oldRef) {
20 | window.removeEventListener('mousemove', oldRef.handleMouseMove);
21 | window.removeEventListener('touchmove', oldRef.handleMouseMove);
22 | window.removeEventListener('mouseup', oldRef.handleMouseUp);
23 | window.removeEventListener('touchend', oldRef.handleMouseUp);
24 | }
25 | }, []);
26 |
27 | const handleMouseUp = useCallback(
28 | e => {
29 | setIsDragging(false);
30 | cleanMouseListeners();
31 | onEnd(e);
32 | },
33 | [cleanMouseListeners, onEnd]
34 | );
35 |
36 | const handleMouseDown = useCallback(
37 | e => {
38 | setIsDragging(true);
39 | cleanMouseListeners();
40 |
41 | listenersRef.current = {
42 | handleMouseMove,
43 | handleMouseUp,
44 | };
45 | window.addEventListener('mousemove', handleMouseMove);
46 | window.addEventListener('touchmove', handleMouseMove);
47 | window.addEventListener('mouseup', handleMouseUp);
48 | window.addEventListener('touchend', handleMouseUp);
49 |
50 | onStart(e);
51 | },
52 | [cleanMouseListeners, handleMouseMove, handleMouseUp, onStart]
53 | );
54 |
55 | useEffect(() => {
56 | return () => {
57 | cleanMouseListeners();
58 | };
59 | // eslint-disable-next-line react-hooks/exhaustive-deps
60 | }, []);
61 |
62 | return (
63 |
72 | {isDragging && (
73 |
79 | )}
80 | {children}
81 |
82 | );
83 | };
84 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/DragSizing.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useRef, useState } from 'react';
2 | import { DragHandler } from './DragHandler';
3 | import { DragSizingData, DragSizingProps, MEvent } from './types';
4 | import {
5 | getContainerInfo,
6 | getContainerMeta,
7 | getHandlerInfo,
8 | isNil,
9 | normalizeMEvent,
10 | } from './util';
11 |
12 | export const DragSizing: React.FC = props => {
13 | const {
14 | border,
15 | onStart,
16 | onEnd,
17 | onUpdate,
18 | id,
19 | className,
20 | style,
21 | handlerClassName,
22 | handlerStyle: _handlerStyle,
23 | handlerWidth: _handlerWidth,
24 | handlerOffset: _handlerOffset,
25 | handlerZIndex: _handlerZIndex,
26 | children,
27 | } = props;
28 |
29 | const handlerWidth = isNil(_handlerWidth) ? 16 : (_handlerWidth as number);
30 | const handlerOffset = (isNil(_handlerOffset)
31 | ? -handlerWidth / 2
32 | : _handlerOffset) as number;
33 | const handlerZIndex = (isNil(_handlerZIndex) ? 10 : _handlerZIndex) as number;
34 |
35 | const [diffCoord, setDiffCoord] = useState(0);
36 | const [oldSize, setOldSize] = useState(null);
37 | const oldCoordRef = useRef(null);
38 | const boxRef = useRef(null);
39 |
40 | const containerMeta = getContainerMeta({ border });
41 |
42 | const { style: containerStyle } = getContainerInfo({
43 | style,
44 | containerMeta,
45 | diffCoord,
46 | oldSize,
47 | });
48 |
49 | const { dir, style: handlerStyle } = getHandlerInfo({
50 | border,
51 | handlerWidth,
52 | handlerOffset,
53 | handlerStyle: _handlerStyle,
54 | });
55 |
56 | const handleStart = useCallback(
57 | (_e: MEvent) => {
58 | const e = normalizeMEvent(_e);
59 |
60 | const { wh, xy } = containerMeta;
61 | const el = boxRef.current;
62 | if (!el) return;
63 |
64 | const px = window.getComputedStyle(el)[wh] as string;
65 |
66 | setDiffCoord(0);
67 | setOldSize(parseInt(px, 10));
68 | oldCoordRef.current = e[xy];
69 |
70 | if (onStart) onStart(e);
71 | },
72 | [containerMeta, onStart]
73 | );
74 |
75 | const handleEnd = useCallback(
76 | (_e: MEvent) => {
77 | const e = normalizeMEvent(_e);
78 | if (onEnd) onEnd(e);
79 | },
80 | [onEnd]
81 | );
82 |
83 | const handleUpdate = useCallback(
84 | (_e: MEvent) => {
85 | const e = normalizeMEvent(_e);
86 |
87 | const { xy } = containerMeta;
88 | if (oldCoordRef.current === null) return;
89 |
90 | setDiffCoord(e[xy] - oldCoordRef.current);
91 |
92 | if (onUpdate) onUpdate(e);
93 | },
94 | [containerMeta, onUpdate]
95 | );
96 |
97 | return (
98 |
107 |
119 | {children}
120 |
121 | );
122 | };
123 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DragSizing';
2 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/module.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Default CSS definition for typescript,
3 | * will be overridden with file-specific definitions by rollup
4 | */
5 | declare module '*.css' {
6 | const content: { [className: string]: string };
7 | export default content;
8 | }
9 |
10 | // eslint-disable-next-line @typescript-eslint/no-empty-interface
11 | interface SvgrComponent
12 | extends React.StatelessComponent> {}
13 |
14 | declare module '*.svg' {
15 | const svgUrl: string;
16 | const svgComponent: SvgrComponent;
17 | export default svgUrl;
18 | export { svgComponent as ReactComponent };
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/types.ts:
--------------------------------------------------------------------------------
1 | export type MEvent = MouseEvent | TouchEvent;
2 |
3 | export type RdsMEvent =
4 | | MouseEvent
5 | | (TouchEvent & {
6 | clientX: number;
7 | clientY: number;
8 | });
9 |
10 | export interface DragHandlerProps {
11 | dir: 'ew' | 'ns';
12 | onStart: (e: MEvent) => void;
13 | onEnd: (e: MEvent) => void;
14 | onUpdate: (e: MEvent) => void;
15 | className?: string;
16 | style?: React.CSSProperties;
17 | children?: React.ReactNode | undefined;
18 | }
19 |
20 | export interface DragHandlerData {
21 | listenersRef: {
22 | handleMouseMove: (e: MEvent) => void;
23 | handleMouseUp: (e: MEvent) => void;
24 | } | null;
25 | }
26 |
27 | export interface DragSizingProps {
28 | border: 'top' | 'bottom' | 'left' | 'right';
29 | onStart?: DragHandlerProps['onStart'];
30 | onEnd?: DragHandlerProps['onEnd'];
31 | onUpdate?: DragHandlerProps['onUpdate'];
32 | id?: string;
33 | className?: string;
34 | style?: React.CSSProperties;
35 | handlerClassName?: string;
36 | handlerStyle?: React.CSSProperties;
37 | handlerWidth?: number;
38 | handlerOffset?: number;
39 | handlerZIndex?: number;
40 | children?: React.ReactNode | undefined;
41 | }
42 |
43 | export interface DragSizingData {
44 | diffCoord: number;
45 | oldCorrd: number | null;
46 | oldSize: number | null;
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/src/util.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DragHandlerProps,
3 | DragSizingData,
4 | DragSizingProps,
5 | MEvent,
6 | RdsMEvent,
7 | } from './types';
8 |
9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
10 | export const isNil = (v: any) => v === null || v === undefined;
11 |
12 | export const normalizeMEvent = (e: MEvent): RdsMEvent => {
13 | if ((e as TouchEvent).touches && (e as TouchEvent).touches[0]) {
14 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
15 | (e as any).clientX = Math.round((e as TouchEvent).touches[0].clientX);
16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 | (e as any).clientY = Math.round((e as TouchEvent).touches[0].clientY);
18 | }
19 | return e as RdsMEvent;
20 | };
21 |
22 | export const getContainerMeta = ({
23 | border,
24 | }: {
25 | border: DragSizingProps['border'];
26 | }) => {
27 | let wh: 'width' | 'height';
28 | let xy: 'clientX' | 'clientY';
29 | let sn: 1 | -1;
30 |
31 | if (/^(left|right)$/.test(border)) {
32 | wh = 'width';
33 | xy = 'clientX';
34 | sn = border === 'right' ? 1 : -1;
35 | } else {
36 | wh = 'height';
37 | xy = 'clientY';
38 | sn = border === 'bottom' ? 1 : -1;
39 | }
40 | return { wh, xy, sn };
41 | };
42 |
43 | export const getContainerInfo = ({
44 | style,
45 | containerMeta,
46 | diffCoord,
47 | oldSize,
48 | }: {
49 | style: DragSizingProps['style'];
50 | containerMeta: ReturnType;
51 | diffCoord: DragSizingData['diffCoord'];
52 | oldSize: DragSizingData['oldSize'];
53 | }) => {
54 | const { wh, sn } = containerMeta;
55 | let retStyle: React.CSSProperties = {};
56 |
57 | if (oldSize != null) {
58 | retStyle[wh] = oldSize + diffCoord * sn;
59 | }
60 | retStyle = {
61 | ...style,
62 | ...retStyle,
63 | };
64 | return { style: retStyle };
65 | };
66 |
67 | export const getHandlerInfo = ({
68 | border,
69 | handlerWidth,
70 | handlerOffset,
71 | handlerStyle,
72 | }: {
73 | border: DragSizingProps['border'];
74 | handlerWidth: DragSizingProps['handlerWidth'];
75 | handlerOffset: DragSizingProps['handlerOffset'];
76 | handlerStyle: DragSizingProps['handlerStyle'];
77 | }) => {
78 | let dir: DragHandlerProps['dir'];
79 | let style: React.CSSProperties = {};
80 |
81 | if (/^(left|right)$/.test(border)) {
82 | dir = 'ew';
83 | style.width = handlerWidth;
84 | style.top = 0;
85 | style.bottom = 0;
86 | } else {
87 | dir = 'ns';
88 | style.height = handlerWidth;
89 | style.left = 0;
90 | style.right = 0;
91 | }
92 | style[border] = handlerOffset;
93 |
94 | style = { ...style, ...handlerStyle };
95 | return { dir, style };
96 | };
97 |
--------------------------------------------------------------------------------
/packages/react-drag-sizing/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "build",
4 | "moduleResolution": "node",
5 | "module": "esnext",
6 | "target": "es5",
7 | "lib": [
8 | "es6",
9 | "dom",
10 | "es2016",
11 | "es2017"
12 | ],
13 | "sourceMap": true,
14 | "declaration": true,
15 | "allowJs": false,
16 | "allowSyntheticDefaultImports": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "jsx": "react"
19 | },
20 | "include": [
21 | "src"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fritx/react-drag-sizing/152732df9f9c945fbad017250cd054965f7d2da2/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | // import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
2 | import React from 'react';
3 | import { HashRouter as Router, Route, Switch } from 'react-router-dom';
4 | import { Home } from './Home';
5 |
6 | export const App: React.FC = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/src/Home.module.css:
--------------------------------------------------------------------------------
1 | .Home {
2 | padding: 0 0 60px;
3 | min-height: 100vh;
4 | background-color: #282c34;
5 | display: flex;
6 | flex-direction: column;
7 | align-items: center;
8 | color: white;
9 | font-size: calc(10px + 2vmin);
10 | }
11 |
12 | .HomeLogo {
13 | animation: Home-logo-spin infinite 20s linear;
14 | height: 20vmin;
15 | /* pointer-events: none; */
16 | }
17 |
18 | .HomeHeader {
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | text-align: center;
23 | }
24 |
25 | .HomeLink {
26 | color: #61dafb;
27 | z-index: 1;
28 | /* fix covered by rotating logo */
29 | }
30 |
31 | .Editor {
32 | padding: 8px 16px;
33 | }
34 |
35 | .InputControl {
36 | box-sizing: border-box;
37 | width: 70px;
38 | height: 31px;
39 | border: none;
40 | vertical-align: bottom;
41 | font-size: 50%;
42 | padding: 0 12px;
43 | }
44 |
45 | .Editor img {
46 | height: 40px;
47 | vertical-align: bottom;
48 | }
49 |
50 | .EditorButton {
51 | padding: 6px 12px;
52 | font-size: 50%;
53 | }
54 |
55 | .EditorBlock3 {
56 | margin-top: 40px;
57 | width: 70vw;
58 | display: flex;
59 | flex-direction: column;
60 | }
61 |
62 | .EditorWrap3 {
63 | border: solid 1px #666;
64 | height: 160px;
65 | overflow: auto;
66 | }
67 |
68 | @keyframes Home-logo-spin {
69 | from {
70 | transform: rotate(0deg);
71 | }
72 |
73 | to {
74 | transform: rotate(360deg);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Home.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useRef, useState } from 'react';
2 | import { DragSizing } from 'react-drag-sizing';
3 | import { Editor, EditorRefAttrs } from 'react-editor';
4 | import styles from './Home.module.css';
5 | import logo from './logo.svg';
6 |
7 | const processText = (s: string) => s; // noop
8 | const processHTML = (s: string) => s; // todo limit
9 |
10 | export const Home: React.FC = () => {
11 | const [value, setValue] = useState('');
12 | const [imgHeight, setImgHeight] = useState('40px');
13 | const ref = useRef(null);
14 |
15 | const setRandomValue = useCallback(() => {
16 | setValue(String(Math.random()));
17 |
18 | // wait for editor render
19 | setTimeout(() => {
20 | const editor = ref.current;
21 | if (editor) editor.focus();
22 | });
23 | }, []);
24 |
25 | const insertEmoji = useCallback(() => {
26 | const editor = ref.current;
27 | if (editor) editor.insertText('😁');
28 | }, []);
29 |
30 | const insertHTML = useCallback(() => {
31 | const editor = ref.current;
32 | if (editor)
33 | editor.insertHTML(
34 | // ' @fritx '
35 | '
'
36 | );
37 | }, []);
38 |
39 | const handleImgHeight = useCallback(e => {
40 | setImgHeight(e.target.value);
41 | }, []);
42 |
43 | useEffect(() => {
44 | document.title = 'react-drag-sizing';
45 | }, []);
46 |
47 | return (
48 |
49 |
50 |
51 |
62 |
63 |
64 |
65 |
68 |
71 |
74 |
80 |
81 |
82 |
86 |
91 |
92 |
102 |
103 |
104 |
105 |
106 |
107 | );
108 | };
109 |
--------------------------------------------------------------------------------
/src/__tests__/react-drag-sizing/basic.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { act } from 'react-dom/test-utils';
4 | // import { DragSizing } from 'react-drag-sizing';
5 | import { DragSizing } from '../../../packages/react-drag-sizing';
6 |
7 | let container: HTMLDivElement;
8 |
9 | beforeEach(() => {
10 | container = document.createElement('div');
11 | document.body.appendChild(container);
12 | });
13 |
14 | afterEach(() => {
15 | document.body.removeChild(container as HTMLDivElement);
16 | });
17 |
18 | it('border left', () => {
19 | act(() => {
20 | ReactDOM.render(
21 |
22 | content
23 | ,
24 | container
25 | );
26 | });
27 | const sizing = container.querySelector('#sizing') as HTMLDivElement;
28 | expect(sizing.innerHTML).toBe(
29 | 'content'
30 | );
31 | });
32 |
33 | it('border top', () => {
34 | act(() => {
35 | ReactDOM.render(
36 |
37 | content
38 | ,
39 | container
40 | );
41 | });
42 | const sizing = container.querySelector('#sizing') as HTMLDivElement;
43 | expect(sizing.innerHTML).toBe(
44 | 'content'
45 | );
46 | });
47 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
15 | .atwho-item {
16 | color: #61dafb;
17 | }
18 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { App } from './App';
4 | import './index.css';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener('load', () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | 'This web app is being served cache-first by a service ' +
54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === 'installed') {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | 'New content is available and will be used when all ' +
82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log('Content is cached for offline use.');
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error('Error during service worker registration:', error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl)
112 | .then(response => {
113 | // Ensure service worker exists, and that we really are getting a JS file.
114 | const contentType = response.headers.get('content-type');
115 | if (
116 | response.status === 404 ||
117 | (contentType != null && contentType.indexOf('javascript') === -1)
118 | ) {
119 | // No service worker found. Probably a different app. Reload the page.
120 | navigator.serviceWorker.ready.then(registration => {
121 | registration.unregister().then(() => {
122 | window.location.reload();
123 | });
124 | });
125 | } else {
126 | // Service worker found. Proceed as normal.
127 | registerValidSW(swUrl, config);
128 | }
129 | })
130 | .catch(() => {
131 | console.log(
132 | 'No internet connection found. App is running in offline mode.'
133 | );
134 | });
135 | }
136 |
137 | export function unregister() {
138 | if ('serviceWorker' in navigator) {
139 | navigator.serviceWorker.ready.then(registration => {
140 | registration.unregister();
141 | });
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "module": "esnext",
5 | "target": "es5",
6 | "lib": [
7 | "dom",
8 | "dom.iterable",
9 | "esnext"
10 | ],
11 | "allowJs": true,
12 | "skipLibCheck": true,
13 | "esModuleInterop": true,
14 | "allowSyntheticDefaultImports": true,
15 | "strict": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------