/packages/\1'
6 | include_warnings=true
7 |
8 | [lints]
9 | deprecated-utility=warn
10 |
--------------------------------------------------------------------------------
/.github/workflows/pull_request.yml:
--------------------------------------------------------------------------------
1 | on: pull_request
2 | jobs:
3 | ci:
4 | runs-on: ubuntu-latest
5 | steps:
6 | - uses: actions/checkout@v2
7 | - uses: actions/setup-node@v2
8 | - uses: actions/cache@v2
9 | with:
10 | path: |
11 | node_modules
12 | packages/*/node_modules
13 | key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
14 | - uses: ./.github/actions/ci
--------------------------------------------------------------------------------
/codecov.js:
--------------------------------------------------------------------------------
1 | import spawn from 'cross-spawn';
2 |
3 | import { projectList } from './project-list';
4 |
5 | const spawnOptions = { stdio: 'inherit' };
6 |
7 | const scopeRegex = /^@(.+\/)+/;
8 |
9 | projectList.forEach(({ name, location }) => {
10 | spawn.sync('yarn', ['test', '--coverage', '--projects', location], spawnOptions);
11 | spawn.sync('codecov', ['--clear', `--flags=${name.replace(scopeRegex, '')}`, ...process.argv.slice(2)], spawnOptions);
12 | });
13 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | __mocks__
2 | .storybook
3 | .vscode
4 | coverage
5 | !/dist
6 | flow-typed
7 | packages
8 | shared
9 | stories
10 | storybook-static
11 | .eslintignore
12 | .eslintrc.json
13 | .flowconfig
14 | .stylelintrc
15 | babel.config.js
16 | codecov.js
17 | DEVELOPMENT.md
18 | flow-typed-update.js
19 | jest.config.js
20 | jsconfig.json
21 | lerna.json
22 | project-list.js
23 | RAWFlowStub.js.flow
24 | rollup.config.*.js
25 | test-config.js
26 | yarn.lock
27 | *.log
28 | *.yml
--------------------------------------------------------------------------------
/shared/jest.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | const ROOT_RESOLVE = path.resolve();
4 |
5 | module.exports = {
6 | setupFiles: [
7 | `${ROOT_RESOLVE}/test-config.js`,
8 | ],
9 | collectCoverageFrom: ['src/**/*.js'],
10 | moduleNameMapper: {
11 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `${ROOT_RESOLVE}/__mocks__/file.mock.js`,
12 | '\\.(css|less)$': `${ROOT_RESOLVE}/__mocks__/style.mock.js`,
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/hoc/PaperScope/PaperScope.hoc.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const PaperScopeContext = React.createContext();
4 |
5 | export const renderWithPaperScope = render => (
6 |
7 | {({ paper }) => render(paper)}
8 |
9 | );
10 |
11 | export default WrappedComponent => props => (
12 |
13 | {context => }
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/.github/actions/ci/action.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 | description: Runs linting, code coverage, and dry build
3 | runs:
4 | using: "composite"
5 | steps:
6 | - run: yarn bootstrap
7 | shell: bash
8 | - run: yarn lint
9 | shell: bash
10 | - run: yarn flow-typed-install
11 | shell: bash
12 | - run: yarn flow
13 | shell: bash
14 | - run: yarn codecov
15 | shell: bash
16 | - run: yarn build
17 | shell: bash
18 | - run: yarn build-storybook
19 | shell: bash
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | strict_yaml_branch: master
3 |
4 | coverage:
5 | status:
6 | project:
7 | default:
8 | target: auto
9 | react-paperjs:
10 | target: auto
11 | flags:
12 | - react-paperjs
13 | react-paperjs-editor:
14 | target: auto
15 | flags:
16 | - react-paperjs-editor
17 | flags:
18 | react-paperjs:
19 | paths:
20 | - src/
21 | react-paperjs-editor:
22 | paths:
23 | - packages/react-paperjs-editor/
--------------------------------------------------------------------------------
/stories/Core/Setup/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Resizable from './Resizable';
4 |
5 | export default {
6 | title: 'Core/Setup',
7 | };
8 |
9 | export const DynamicContainer = () => (
10 | <>
11 | Drag borders to resize the canvas.
12 |
13 | >
14 | );
15 |
16 | DynamicContainer.parameters = {
17 | docs: {
18 | source: {
19 | code: require('!!raw-loader!./Resizable/Resizable.component').default, // eslint-disable-line global-require
20 | },
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@psychobolt/react-paperjs-editor",
3 | "version": "0.0.16",
4 | "description": "A library of common editor components for React Paper.js",
5 | "main": "./dist/index.js",
6 | "repository": "https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor",
7 | "author": "psychobolt",
8 | "license": "MIT",
9 | "publishConfig": {
10 | "access": "public"
11 | },
12 | "dependencies": {
13 | "@psychobolt/react-paperjs": "^1.0.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react-cache/README.md:
--------------------------------------------------------------------------------
1 | # react-cache
2 |
3 | A basic cache for React applications. It also serves as a reference for more
4 | advanced caching implementations.
5 |
6 | This package is meant to be used alongside yet-to-be-released, experimental
7 | React features. It's unlikely to be useful in any other context.
8 |
9 | **Do not use in a real application.** We're publishing this early for
10 | demonstration purposes.
11 |
12 | **Use it at your own risk.**
13 |
14 | # No, Really, It Is Unstable
15 |
16 | The API ~~may~~ will change wildly between versions.
17 |
--------------------------------------------------------------------------------
/packages/react-cache/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "react-cache",
4 | "description": "A basic cache for React applications",
5 | "version": "2.0.0-alpha.0",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/facebook/react.git",
9 | "directory": "packages/react-cache"
10 | },
11 | "files": [
12 | "LICENSE",
13 | "README.md",
14 | "build-info.json",
15 | "index.js",
16 | "cjs/",
17 | "umd/"
18 | ],
19 | "peerDependencies": {
20 | "react": "^16.3.0-alpha.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | import { configs } from './rollup.config.common';
4 |
5 | function getConfig(pathname, base) {
6 | const dist = path.resolve(pathname, 'dist');
7 | return {
8 | ...base,
9 | output: {
10 | dir: dist,
11 | entryFileNames: '[name].dev.js',
12 | chunkFileNames: '[name]-[hash].dev.js',
13 | format: 'cjs',
14 | exports: 'named',
15 | sourcemap: 'inline',
16 | },
17 | };
18 | }
19 |
20 | export default configs.map(([pathname, config]) => getConfig(pathname, config));
21 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 |
3 | import config from 'shared/jest.config';
4 |
5 | import { projectList } from './project-list';
6 | import pkg from './package.json';
7 |
8 | module.exports = {
9 | ...config,
10 | projects: projectList.reduce((paths, { location }) => {
11 | const configPath = `${location}/jest.config.js`;
12 | return fs.existsSync(configPath) ? [...paths, configPath] : paths;
13 | }, []),
14 |
15 | // root config
16 | displayName: pkg.name,
17 | testPathIgnorePatterns: [
18 | '/node_modules/',
19 | '/packages/',
20 | ],
21 | };
22 |
--------------------------------------------------------------------------------
/stories/Core/Examples/Text/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { PointText } from '@psychobolt/react-paperjs';
4 |
5 | import { Mountable as PaperContainer, ref } from '../../shared';
6 |
7 | export default () => (
8 |
9 |
17 | The contents of the point text
18 |
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as FreeformPathTool } from './FreeformPathTool';
2 | export { default as Grid } from './Grid';
3 | export { default as LineTool } from './LineTool';
4 | export { default as PolygonTool } from './PolygonTool';
5 | export { default as RectangleTool } from './RectangleTool';
6 | export { default as CircleTool } from './CircleTool';
7 | export { default as SegmentPathTool } from './SegmentPathTool';
8 | export { default as PanAndZoom } from './PanAndZoom';
9 | export { default as EllipseTool } from './EllipseTool';
10 | export { default as PathTool } from './shared/PathTool';
11 |
--------------------------------------------------------------------------------
/stories/packages/react-paperjs-editor/Setup/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import PanAndZoomReadme from 'packages/react-paperjs-editor/src/components/PanAndZoom/PanAndZoom.md';
4 | import PanAndZoomApp from './PanAndZoom';
5 |
6 | export default {
7 | title: 'packages/react-paperjs-editor/Setup',
8 | };
9 |
10 | export const PanAndZoom = () => (
11 | <>
12 | Drag + Space bar to pan the view. Mouse wheel scroll to zoom.
13 |
14 | >
15 | );
16 |
17 | PanAndZoom.storyName = 'with Pan and Zoom';
18 | PanAndZoom.parameters = {
19 | docs: {
20 | page: PanAndZoomReadme,
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/rollup.config.prod.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { terser } from 'rollup-plugin-terser';
3 |
4 | import { configs } from './rollup.config.common';
5 |
6 | function getConfig(pathname, base) {
7 | const dist = path.resolve(pathname, 'dist');
8 | return {
9 | ...base,
10 | output: {
11 | dir: dist,
12 | entryFileNames: '[name].prod.js',
13 | chunkFileNames: '[name]-[hash].prod.js',
14 | format: 'cjs',
15 | exports: 'named',
16 | },
17 | plugins: [
18 | ...base.plugins,
19 | terser(),
20 | ],
21 | };
22 | }
23 |
24 | export default configs.map(([pathname, config]) => getConfig(pathname, config));
25 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | jobs:
6 | main:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: actions/setup-node@v2
11 | - uses: actions/cache@v2
12 | with:
13 | path: |
14 | node_modules
15 | packages/*/node_modules
16 | key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
17 | - uses: ./.github/actions/ci
18 | - uses: peaceiris/actions-gh-pages@v3
19 | with:
20 | github_token: ${{ secrets.GITHUB_TOKEN }}
21 | publish_branch: gh-pages
22 | publish_dir: storybook-static
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Tests",
6 | "type": "node",
7 | "request": "launch",
8 | "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js",
9 | "stopOnEntry": false,
10 | "args": [
11 | "--runInBand",
12 | "--no-cache"
13 | ],
14 | "cwd": "${workspaceRoot}",
15 | "runtimeArgs": ["--nolazy"],
16 | "console": "integratedTerminal",
17 | "sourceMaps": true,
18 | "env": {
19 | "BABEL_ENV": "test",
20 | "NODE_ENV": "development"
21 | },
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/shared/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import Paper from 'paper';
3 |
4 | import type { MouseEventHandler } from '@psychobolt/react-paperjs';
5 |
6 | type PathEventHandler = (path: typeof Paper.Path) => any;
7 |
8 | export type ToolDefaultProps = {
9 | onMouseDown: MouseEventHandler,
10 | onMouseDrag: MouseEventHandler,
11 | onMouseUp: MouseEventHandler,
12 | onPathAdd: PathEventHandler,
13 | onSegmentAdd: PathEventHandler,
14 | onSegmentRemove: PathEventHandler
15 | }
16 |
17 | export const toolDefaultProps = {
18 | onMouseDown: () => {},
19 | onMouseDrag: () => {},
20 | onMouseUp: () => {},
21 | onPathAdd: () => {},
22 | onSegmentAdd: () => {},
23 | onSegmentRemove: () => {},
24 | };
25 |
--------------------------------------------------------------------------------
/stories/Core/Examples/Tool/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CircleTool } from '@psychobolt/react-paperjs-editor';
3 | import styled from 'styled-components';
4 |
5 | import code from 'raw-loader!/packages/react-paperjs-editor/src/components/CircleTool/CircleTool.component';
6 | import { Mountable } from '../../shared';
7 |
8 | const PaperContainer = styled(Mountable)`
9 | canvas {
10 | border: 1px solid black;
11 | }
12 | `;
13 |
14 | export default {
15 | title: 'Core/Examples/Tool',
16 | };
17 |
18 | module.exports.CircleTool = () => (
19 |
20 |
21 |
22 | );
23 |
24 | module.exports.CircleTool.parameters = {
25 | docs: {
26 | source: {
27 | code,
28 | },
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@psychobolt/react-paperjs@^0.0.58":
6 | version "0.0.58"
7 | resolved "https://registry.yarnpkg.com/@psychobolt/react-paperjs/-/react-paperjs-0.0.58.tgz#2eec09dcc355367c1f5ee597a5612fe01491d5c0"
8 | integrity sha512-CQxYY3eCYuXRDxy8gGjfnJSqhBGDwdpG6X4bP5BVYK9hUFLW/XvsbEQPrsAupcmNplOnzN97GE2iPka/xfOrlQ==
9 | dependencies:
10 | react-is "^16.13.1"
11 |
12 | react-is@^16.13.1:
13 | version "16.13.1"
14 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
15 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
16 |
--------------------------------------------------------------------------------
/stories/Core/Examples/Layer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { PaperContainer, Circle, Layer } from '@psychobolt/react-paperjs';
4 |
5 | import { ref } from '../../shared';
6 |
7 | export default () => {
8 | const Shapes = () => (
9 |
10 | );
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/LineTool/LineTool.md:
--------------------------------------------------------------------------------
1 | # LineTool
2 |
3 | Paper tool that allows drawing of lines on the canvas by mouse click and drag.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { LineTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/EllipseTool/EllipseTool.md:
--------------------------------------------------------------------------------
1 | # EllipseTool
2 |
3 | Paper tool that allows drawing of ellipse shapes on the canvas.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { EllipseTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
28 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/RectangleTool/RectangleTool.md:
--------------------------------------------------------------------------------
1 | # RectangleTool
2 |
3 | Paper tool that allows drawing of rectangle shapes on the canvas.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { RectangleTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/FreeformPathTool/FreeformPathTool.md:
--------------------------------------------------------------------------------
1 | # FreeformPathTool
2 |
3 | Paper tool that allows freeform drawing of paths on the canvas.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { FreeformPathTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/CircleTool/CircleTool.md:
--------------------------------------------------------------------------------
1 | # CircleTool
2 |
3 | Paper tool that allows dynamic drawing of circle paths on the canvas. Mouse drag to specified size.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { CircleTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/PolygonTool/PolygonTool.md:
--------------------------------------------------------------------------------
1 | # PolygonTool
2 |
3 | Paper tool that allows creating closed polygon shapes on the canvas by shift click. Close shape by clicking on a existing point.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { PolygonTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/SegmentPathTool/SegmentPathTool.md:
--------------------------------------------------------------------------------
1 | # SegmentPathTool
2 |
3 | Paper tool that allows drawing connected segmented path on the canvas. Shift click and then release shift key to complete path.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { SegmentPathTool } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { pathProps, rest } from './path';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `pathProps: {}`
22 |
23 | Props for path. See [reference](http://paperjs.org/reference/path/).
24 |
25 | ### `...rest: {}`
26 |
27 | Props for Paper Tool. See [reference](http://paperjs.org/reference/tool/).
--------------------------------------------------------------------------------
/stories/Core/Examples/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { isValidElementType } from 'react-is';
3 |
4 | const reqExample = require.context('./', true, /^\.\/[\w-]+\/index\.js$/);
5 | const reqSource = require.context('!!raw-loader!./', true, /^\.\/[\w-]+\/index\.js$/);
6 | const reqReadme = require.context('./', true, /^\.\/[\w-]+\/README\.mdx?$/);
7 |
8 | export default {
9 | title: 'Core/Examples',
10 | };
11 |
12 | reqExample.keys().forEach(folder => {
13 | const name = folder.match(/^\.\/([\w-]+)\/index\.js$/)[1];
14 | const { default: Component } = reqExample(folder);
15 | if (isValidElementType(Component)) {
16 | const readmePath = reqReadme.keys().find(path => path.indexOf(name) > -1);
17 | const sourcePath = reqSource.keys().find(path => path.indexOf(name) > -1);
18 | module.exports[name] = () => ;
19 | module.exports[name].parameters = {
20 | docs: {
21 | ...(readmePath ? { page: reqReadme(readmePath).default } : undefined),
22 | ...(sourcePath ? { source: { code: reqSource(sourcePath).default } } : undefined),
23 | },
24 | };
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/project-list.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const { isMatch } = require('micromatch');
4 | const { getPackagesSync } = require('@lerna/project');
5 |
6 | const ROOT_RESOLVE = path.resolve();
7 |
8 | const PACKAGES = (process.env.PACKAGES || `${fs.readFileSync('.projectlist', 'utf8')}`).trim();
9 |
10 | const EXCLUDES = [];
11 | const INCLUDES = [];
12 |
13 | if (PACKAGES) {
14 | Array.prototype.push.apply(INCLUDES, PACKAGES.split(/\s*(?:,|\n|\s)+\s*/).filter(pattern => {
15 | if (pattern.startsWith('!')) {
16 | EXCLUDES.push(pattern.substring(1));
17 | return false;
18 | }
19 | return true;
20 | }));
21 | }
22 |
23 | const match = (strings, pattern) => strings.some(string => isMatch(string, pattern));
24 |
25 | module.exports = {
26 | EXCLUDES,
27 | INCLUDES,
28 | projectList: getPackagesSync().filter(pkg => {
29 | const { name, location } = pkg;
30 | const strings = [name, location.replace(`${ROOT_RESOLVE}/`, '')];
31 | return INCLUDES.some(pattern => match(strings, pattern))
32 | && !EXCLUDES.some(pattern => match(strings, pattern));
33 | }),
34 | };
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 psychobolt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/PanAndZoom/PanAndZoom.md:
--------------------------------------------------------------------------------
1 | # Pan And Zoom
2 |
3 | Add pan and zoom controls to Paper's view. By default, space + mouse drag to pan and mouse scroll to zoom. The component will also append attribute 'drag-state' to the canvas element.
4 |
5 | Example usage:
6 | ```jsx
7 | import { PaperContainer, PanAndZoom } from '@psychobolt/react-paperjs'
8 |
9 | import Scene, { options } from './Scene';
10 |
11 | export default () => (
12 |
13 |
14 |
15 |
16 |
17 | );
18 | ```
19 |
20 | ## Props
21 |
22 | ### `onPanEnabled?: () => any`
23 |
24 | Callback when pan is enabled.
25 |
26 | ### `onPanDisabled?: () => any`
27 |
28 | Callback when pan is disabled.
29 |
30 | ### `onZoom?: (level: number) => any`
31 |
32 | Callback when zoom leveling the canvas
33 |
34 | ### `zoomLevel?: number`
35 |
36 | Starting zoom level. Default is 1. See [reference](http://paperjs.org/reference/view/#zoom).
37 |
38 | ### `center?: object | array`
39 |
40 | The center of the view. See [reference](http://paperjs.org/reference/view/#center).
--------------------------------------------------------------------------------
/stories/Core/Examples/Path/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { renderWithPaperScope, PaperContainer, Path, Rectangle } from '@psychobolt/react-paperjs';
4 |
5 | export default {
6 | title: 'Core/Examples/Path',
7 | };
8 |
9 | export const PathData = () => (
10 |
11 |
15 |
16 | );
17 |
18 | PathData.storyName = 'with pathData';
19 |
20 | export const RectangleExample = () => {
21 | const [size, toggleSize] = React.useState([90, 60]);
22 | return (
23 | <>
24 |
25 | toggleSize([size[1], size[0]])}>Switch Size
26 |
27 |
28 | {renderWithPaperScope(paper => (
29 |
34 | ))}
35 |
36 | >
37 | );
38 | };
39 |
40 | RectangleExample.storyName = 'Rectangle';
41 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 psychobolt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/react-cache/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Facebook, Inc. and its affiliates.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.storybook/utils.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const glob = require('glob');
3 | const _ = require('lodash');
4 | const isClass = require('is-class');
5 | const slash = require('slash');
6 |
7 | const isTreeLike = input => input && !_.isFunction(input) && !_.isString(input) && !isClass(input);
8 |
9 | function flatten(tree, dir = __dirname) {
10 | if (_.isArray(tree)) return tree.map(pattern => flatten(pattern, path.resolve(dir, pattern)));
11 | if (isTreeLike(tree)) {
12 | return Object.entries(tree).reduce((paths, [current, patterns]) => {
13 | const directory = current === 'default' ? dir : path.resolve(dir, current);
14 | return paths.concat(isTreeLike(patterns) ? flatten(patterns, directory) : directory);
15 | }, []);
16 | }
17 | return slash(dir);
18 | }
19 |
20 | module.exports.getStories = (patterns, dir = __dirname) => patterns.reduce((result, pattern) => {
21 | let paths = [];
22 | glob.sync(path.resolve(dir, pattern)).forEach(storyPath => {
23 | const m = storyPath.match(/\..+$/) ? null : require(storyPath); // eslint-disable-line global-require,import/no-dynamic-require
24 | paths = paths.concat(flatten(m, storyPath));
25 | });
26 | return result.concat(paths);
27 | }, []);
28 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/shared/PathTool/PathTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import type { ToolEventHandler } from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | type Path = typeof Paper.Path;
7 | type KeyEventHandler = typeof Paper.KeyEvent => any
8 | type PathEventHandler = Path => any
9 | type SegmentEventHandler = typeof Paper.Segment => any;
10 |
11 | type Props = {
12 | paper: typeof Paper.PaperScope,
13 | onKeyDown: KeyEventHandler,
14 | onKeyUp: KeyEventHandler,
15 | onMouseDown: ToolEventHandler,
16 | onMouseDrag: ToolEventHandler,
17 | onMouseUp: ToolEventHandler,
18 | onPathInit: PathEventHandler,
19 | onPathAdd: PathEventHandler,
20 | onSegmentAdd: SegmentEventHandler,
21 | onSegmentRemove: SegmentEventHandler
22 | };
23 |
24 | export default class PathTool extends React.Component
{
25 | static defaultProps: Props = {
26 | paper: null,
27 | onKeyDown: () => {},
28 | onKeyUp: () => {},
29 | onMouseDown: () => {},
30 | onMouseDrag: () => {},
31 | onMouseUp: () => {},
32 | onPathInit: () => {},
33 | onPathAdd: () => {},
34 | onSegmentAdd: () => {},
35 | onSegmentRemove: () => {},
36 | }
37 |
38 | path: Path
39 | }
40 |
--------------------------------------------------------------------------------
/stories/Core/shared/Mountable/Mountable.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 |
5 | const { PaperContainer } = ReactPaperJS;
6 |
7 | type DefaultProps = {
8 | mount?: boolean
9 | };
10 |
11 | type Props = {
12 | mount?: boolean,
13 | children: React.Node,
14 | className: string,
15 | };
16 |
17 | type State = DefaultProps;
18 |
19 | export default class Mountable extends React.Component {
20 | static defaultProps: DefaultProps = {
21 | mount: true,
22 | }
23 |
24 | constructor(props: Props) {
25 | super(props);
26 | this.state = {
27 | mount: props.mount,
28 | };
29 | }
30 |
31 | onClick: SyntheticMouseEvent<'div'> => void = () => this.setState(state => ({ mount: !state.mount }));
32 |
33 | render(): React.Node {
34 | const { className, mount, children, ...props } = this.props;
35 | const { mount: mounted } = this.state;
36 | return (
37 |
38 |
39 | Attach/Detach
40 |
41 |
42 | {mounted ? children : null}
43 |
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "parserOptions": {
4 | "ecmaFeatures": {
5 | "legacyDecorators": true
6 | }
7 | },
8 | "extends": ["airbnb", "plugin:flowtype/recommended"],
9 | "plugins": ["flowtype", "jest"],
10 | "rules": {
11 | "import/no-extraneous-dependencies": 0,
12 | "import/no-webpack-loader-syntax": 0,
13 | "no-confusing-arrow": ["error", {"allowParens": true}],
14 | "arrow-parens": ["error", "as-needed"],
15 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
16 | "react/jsx-props-no-spreading": 0,
17 | "react/static-property-placement": [1, "static public field"],
18 | "react/no-multi-comp": 0,
19 | "import/prefer-default-export": 0,
20 | "object-curly-newline": ["error", { "consistent": true }],
21 | "no-bitwise": ["error", { "int32Hint": true }],
22 | "no-mixed-operators": 0
23 | },
24 | "env": {
25 | "jest/globals": true,
26 | "browser": true
27 | },
28 | "settings": {
29 | "import/resolver": {
30 | "babel-module": {
31 | "root": ["./"],
32 | "cwd": "./"
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/rollup.config.common.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import alias from '@rollup/plugin-alias';
4 | import resolve from '@rollup/plugin-node-resolve';
5 | import commonjs from '@rollup/plugin-commonjs';
6 | import babel from '@rollup/plugin-babel';
7 |
8 | const { projectList, INCLUDES } = require('./project-list');
9 |
10 | const ROOT_RESOLVE = path.resolve();
11 |
12 | const config = {
13 | input: path.resolve(ROOT_RESOLVE, 'src', 'index.js'),
14 | plugins: [
15 | alias({
16 | entries: {
17 | paper: 'paper/dist/paper-core',
18 | },
19 | }),
20 | resolve(),
21 | commonjs({
22 | include: /node_modules/,
23 | }),
24 | babel({
25 | exclude: /node_modules/,
26 | babelHelpers: 'bundled',
27 | }),
28 | ],
29 | external: [
30 | ...projectList.map(({ name }) => name),
31 | 'paper/dist/paper-core',
32 | 'react',
33 | 'react-dom',
34 | 'react-is',
35 | 'styled-components',
36 | ],
37 | };
38 |
39 | export const configs = INCLUDES.length === 0 && fs.statSync(config.input).isFile()
40 | ? [ROOT_RESOLVE, config]
41 | : Object.entries(projectList.reduce((cfg, { location }) => ({
42 | ...cfg,
43 | [location]: {
44 | ...config,
45 | input: `${location}/src/index.js`,
46 | },
47 | }), {}));
48 |
49 | export default config;
50 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/Grid/Grid.md:
--------------------------------------------------------------------------------
1 | # Grid
2 |
3 | Renders a grid onto the canvas.
4 |
5 | ```jsx
6 | import { PaperContainer } from '@psychobolt/react-paperjs';
7 | import { Grid } from '@psychobolt/react-paperjs-editor';
8 |
9 | import { viewProps, canvasProps } from './Scene';
10 | import { gridProps } from './grid';
11 |
12 | export () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ## Props
20 |
21 | ### `top?: number`
22 |
23 | Starting top position of the grid relative to the canvas. Default is 0 pixels (top-most).
24 |
25 | ### `left?: number`
26 |
27 | Starting left position of the grid relative to the canvas. Default is 0 pixels (left-most).
28 |
29 | ### `right?: number`
30 |
31 | Ending right position of the grid relative to the canvas. Default value is left + width.
32 |
33 | ### `bottom?: number`
34 |
35 | Ending bottom position of the grid relative to the canvas. Default value is top + height.
36 |
37 | ### `width?: number`
38 |
39 | Width of the grid.
40 |
41 | ### `height?: number`
42 |
43 | Height of the grid.
44 |
45 | ### `cellSize?: number`
46 |
47 | Size of each cell in the grid. The default value is 50 pixels.
48 |
49 | ### `strokeColor?: string`
50 |
51 | Color of each grid line.
52 |
53 | ### `strokeWidth?: number`
54 |
55 | Width of each grid line.
56 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # dist
61 | **/dist/*
62 | !**/dist/index.js
63 | !**/dist/*.js.flow
64 |
65 | # flow-typed
66 | flow-typed/npm
67 |
68 | # Storybook
69 | storybook-static
70 |
71 | # only support Yarn
72 | package-lock.json
73 |
74 | # Mac files
75 | .DS_Store
76 |
77 | # Cache files
78 | .cache
79 |
80 | # Node Version Manager
81 | .nvmrc
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const { getStories } = require('./utils');
2 |
3 | module.exports = {
4 | stories: getStories(['../stories']),
5 | features: {
6 | postcss: false,
7 | },
8 | addons: [
9 | '@storybook/addon-links',
10 | '@storybook/addon-essentials',
11 | ],
12 | // See https://github.com/storybookjs/storybook/blob/master/addons/docs/src/frameworks/common/preset.ts, to configure
13 | webpackFinal: async config => ({
14 | ...config,
15 | module: {
16 | ...config.module,
17 | rules: [
18 | ...config.module.rules.map(rule => {
19 | if (rule.test.test('.md')) {
20 | return {};
21 | }
22 | if (rule.exclude && rule.exclude.test('.stories.mdx')) {
23 | return { ...rule, test: /\.md$/ };
24 | }
25 | if (rule.test.test('.stories.mdx')) {
26 | return { ...rule, test: /\.mdx$/ };
27 | }
28 | if (rule.test.test('.stories.js')) {
29 | return {
30 | ...rule,
31 | test: /\.jsx?$/,
32 | include: /stories/,
33 | };
34 | }
35 | return rule;
36 | }),
37 | {
38 | test: /\.jsx?$/,
39 | exclude: /node_modules/,
40 | loader: 'source-map-loader',
41 | enforce: 'pre',
42 | },
43 | ],
44 | },
45 | resolve: {
46 | ...config.resolve,
47 | alias: {
48 | ...(config.resolve ? config.resolve.alias : undefined),
49 | react: require.resolve('react'),
50 | 'react-dom': require.resolve('react-dom'),
51 | 'styled-components': require.resolve('styled-components'),
52 | },
53 | },
54 | }),
55 | };
56 |
--------------------------------------------------------------------------------
/src/Paper.types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import Paper from 'paper';
4 |
5 | export type Types = {
6 | [type: string]: (props: {}, paper: typeof Paper.PaperScope, children?: Node) => Object
7 | };
8 |
9 | export type Components = {
10 | [key: string]: React.AbstractComponent
11 | };
12 |
13 | const PAPER = {
14 | Tool: 'Tool',
15 | Layer: 'Layer',
16 | Group: 'Group',
17 | Path: 'Path',
18 | Line: 'Line',
19 | Raster: 'Raster',
20 | Rectangle: 'Rectangle',
21 | Circle: 'Circle',
22 | PointText: 'PointText',
23 | };
24 |
25 | export const CONSTANTS = {
26 | PaperScope: 'PaperScope',
27 | ...PAPER,
28 | };
29 |
30 | const TYPES: Types = {
31 | [CONSTANTS.PaperScope]: (props, paper) => new paper.PaperScope(),
32 | [CONSTANTS.Tool]: (props, paper) => new paper.Tool(props),
33 | [CONSTANTS.Layer]: (props, paper) => new paper.Layer(props),
34 | [CONSTANTS.Group]: (props, paper) => new paper.Group(props),
35 | [CONSTANTS.Path]: (props, paper) => new paper.Path(props),
36 | [CONSTANTS.Raster]: (props, paper) => new paper.Raster(props),
37 | [CONSTANTS.Line]: (props, paper) => new paper.Path.Line(props),
38 | [CONSTANTS.Rectangle]: (props, paper) => new paper.Path.Rectangle(props),
39 | [CONSTANTS.Circle]: (props, paper) => new paper.Path.Circle(props),
40 | [CONSTANTS.PointText]: (props, paper, children) => new paper.PointText({
41 | ...props,
42 | content: children,
43 | }),
44 | };
45 |
46 | export default TYPES;
47 |
48 | export const components: Components = Object.entries(PAPER).reduce(
49 | (types: Components, [key, Type]) => ({
50 | ...types,
51 | // $FlowFixMe
52 | [key]: React.forwardRef((props, ref) => ),
53 | }),
54 | {},
55 | );
56 |
57 | export const {
58 | Tool,
59 | Layer,
60 | Group,
61 | Path,
62 | Line,
63 | Raster,
64 | Rectangle,
65 | Circle,
66 | PointText,
67 | } = components;
68 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/Grid/Grid.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | const { Layer, Group, Line } = ReactPaperJS;
7 |
8 | type LayerType = typeof Paper.Layer;
9 |
10 | type Props = {
11 | top: number,
12 | left: number,
13 | right: number,
14 | bottom: number,
15 | width: number,
16 | height: number,
17 | cellSize: number,
18 | strokeColor: string,
19 | strokeWidth: number,
20 | innerRef: React.Ref
21 | };
22 |
23 | const Grid = ({ width, height, top = 0, left = 0, right = left + width, bottom = top + height, cellSize = 50, strokeColor = '#D0D0D0', strokeWidth = 1, innerRef }: Props) => {
24 | const x = Math.ceil(left / cellSize) * cellSize;
25 | const y = Math.ceil(top / cellSize) * cellSize;
26 | const cols = Math.ceil((right - left) / cellSize);
27 | const rows = Math.ceil((bottom - top) / cellSize);
28 | const verticalLines = [];
29 | const horizontalLines = [];
30 | for (let i = 0; i <= cols; i += 1) {
31 | const position = x + (i * cellSize);
32 | verticalLines.push( );
39 | }
40 | for (let i = 0; i <= rows; i += 1) {
41 | const position = y + (i * cellSize);
42 | horizontalLines.push( );
49 | }
50 | return (
51 |
52 |
53 | {verticalLines}
54 |
55 |
56 | {horizontalLines}
57 |
58 |
59 | );
60 | };
61 |
62 | export default (React.forwardRef(
63 | (props, ref) => ,
64 | ): React.AbstractComponent);
65 |
--------------------------------------------------------------------------------
/stories/packages/react-paperjs-editor/Setup/PanAndZoom/PanAndZoom.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PaperContainer, Layer, Circle, renderWithPaperScope } from '@psychobolt/react-paperjs';
3 | import { Grid, PanAndZoom } from '@psychobolt/react-paperjs-editor';
4 | import styled from 'styled-components';
5 |
6 | import * as styles from './PanAndZoom.style';
7 | import { ref } from '../../shared';
8 |
9 | const width = 500;
10 | const height = 500;
11 |
12 | const Container = styled(PaperContainer)`
13 | ${styles.container}
14 | `;
15 |
16 | export default () => (
17 | paper.view.element.focus()}
24 | >
25 | {
34 | console.log('Pan enabled'); // eslint-disable-line no-console
35 | }}
36 | onPanDisabled={() => {
37 | console.log('Pan disabled'); // eslint-disable-line no-console
38 | }}
39 | onZoom={level => {
40 | console.log(`Zoom: ${level}`); // eslint-disable-line no-console
41 | }}
42 | >
43 | {renderWithPaperScope(paper => {
44 | const { top, left, right, bottom } = paper.view.bounds;
45 | return (
46 |
56 | );
57 | })}
58 |
59 |
65 |
66 |
67 |
68 | );
69 |
--------------------------------------------------------------------------------
/stories/Core/Setup/Resizable/Resizable.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Resizable } from 're-resizable';
3 | import styled from 'styled-components';
4 |
5 | import { renderWithPaperScope, PaperContainer, Circle } from '@psychobolt/react-paperjs';
6 |
7 | import { ref } from '../../shared';
8 | import * as styles from './Resizable.style';
9 |
10 | const Container = styled(Resizable)`
11 | ${styles.container}
12 | `;
13 |
14 | export default class extends React.Component {
15 | constructor(props) {
16 | super(props);
17 | this.state = {
18 | width: styles.canvas.width,
19 | height: styles.canvas.height,
20 | };
21 | this.container = React.createRef();
22 | }
23 |
24 | onResizeStop = (event, direction, refToElement) => {
25 | const width = refToElement.clientWidth;
26 | const height = refToElement.clientHeight;
27 | Object.assign(this.container.current.props.paper.view.viewSize, { width, height });
28 | }
29 |
30 | render() {
31 | const { width, height } = this.state;
32 | return (
33 |
34 | ({
41 | onResize: () => {
42 | const { width: vWidth, height: vHeight } = paper.view.viewSize;
43 | this.setState({ width: `${vWidth}px`, height: `${vHeight}px` });
44 | },
45 | })}
46 | >
47 | {renderWithPaperScope(paper => {
48 | const { x, y } = paper.view.center;
49 | return (
50 |
56 | );
57 | })}
58 |
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Paper.component.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | function indexProps(dictionary, props) {
4 | Object.entries(props).forEach(([key, value]) => {
5 | if (key === 'children') return;
6 | const values = dictionary[key];
7 | if (values) {
8 | values.push(value);
9 | } else {
10 | dictionary[key] = [value]; // eslint-disable-line no-param-reassign
11 | }
12 | });
13 | }
14 |
15 | export function diffProps(oldProps, newProps) {
16 | const updatePayload = []; // schema: [propKey1, value2, propKey2, value2, ...]
17 | const propChanges = {}; // schema: { propKey1: [oldValue1, newValue1], ... }
18 | indexProps(propChanges, oldProps);
19 | indexProps(propChanges, newProps);
20 | Object.entries(propChanges).forEach(([key, values]) => {
21 | if (values.length === 1) {
22 | if (key in newProps) {
23 | const [value] = values;
24 | updatePayload.push(key, value);
25 | } else {
26 | updatePayload.push(key, null);
27 | }
28 | } else if (values.length === 2) {
29 | const [preValue, nextValue] = values;
30 | if (!_.isEqual(preValue, nextValue)) {
31 | updatePayload.push(key, nextValue);
32 | }
33 | }
34 | });
35 | return updatePayload.length ? updatePayload : null;
36 | }
37 |
38 | /* eslint no-param-reassign:
39 | ["error", { "props": true, "ignorePropertyModificationsFor": ["instance"] }]
40 | */
41 | export function updateProps(ref, updatePayload, type) {
42 | for (let i = 1; i < updatePayload.length; i += 2) {
43 | const key = updatePayload[i - 1];
44 | const value = updatePayload[i];
45 | let instance = ref;
46 | if (type === 'Rectangle') {
47 | instance = instance.bounds;
48 | }
49 | if (key === 'center') {
50 | instance.position = value;
51 | } else if (key === 'from') {
52 | instance.firstSegment.point = value;
53 | } else if (key === 'to') {
54 | instance.lastSegment.point = value;
55 | } else if (key in instance) {
56 | instance[key] = value;
57 | } else {
58 | console.log(`instance does not have property ${key}`, instance); // eslint-disable-line no-console
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/FreeformPathTool/FreeformPathTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | type ToolType = typeof Paper.Tool;
9 | type ToolEvent = typeof Paper.ToolEvent;
10 |
11 | const { Tool, PaperScope } = ReactPaperJS;
12 |
13 | type Props = {
14 | pathProps: {
15 | strokeColor: string,
16 | },
17 | innerRef: React.Ref
18 | };
19 |
20 | const MOUSE_LEFT_CODE = 0;
21 |
22 | @PaperScope
23 | class FreeformPathTool extends PathTool {
24 | static defaultProps = {
25 | ...PathTool.defaultProps,
26 | pathProps: {
27 | strokeColor: 'black',
28 | },
29 | }
30 |
31 | onMouseDown = (toolEvent: ToolEvent) => {
32 | const { pathProps, onMouseDown, onPathInit, paper } = this.props;
33 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
34 | const path = new paper.Path(pathProps);
35 | this.path = path;
36 | onPathInit(path);
37 | }
38 | onMouseDown(toolEvent);
39 | }
40 |
41 | onMouseDrag = (toolEvent: ToolEvent) => {
42 | const { onMouseDrag } = this.props;
43 | if (toolEvent.event.buttons === 1) {
44 | this.path.add(toolEvent.point);
45 | }
46 | onMouseDrag(toolEvent);
47 | }
48 |
49 | onMouseUp = (toolEvent: ToolEvent) => {
50 | const { path } = this;
51 | const { onMouseUp, onPathAdd } = this.props;
52 | if (path) {
53 | onPathAdd(path);
54 | this.path = null;
55 | }
56 | onMouseUp(toolEvent);
57 | }
58 |
59 | render() {
60 | const {
61 | pathProps, onMouseDown, onMouseDrag, onMouseUp, onPathAdd, paper, innerRef, ...rest
62 | } = this.props;
63 | return (
64 |
72 | );
73 | }
74 | }
75 |
76 | export default (React.forwardRef(
77 | (props, ref) => ,
78 | ): React.AbstractComponent);
79 |
--------------------------------------------------------------------------------
/packages/react-cache/cjs/react-cache.production.min.js:
--------------------------------------------------------------------------------
1 | /** @license React v16.6.1
2 | * react-cache.production.min.js
3 | *
4 | * Copyright (c) Facebook, Inc. and its affiliates.
5 | *
6 | * This source code is licensed under the MIT license found in the
7 | * LICENSE file in the root directory of this source tree.
8 | */
9 |
10 | 'use strict';Object.defineProperty(exports,"__esModule",{value:!0});var l=require("react"),m=require("scheduler"),n=l.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;function p(c,e){var f=n.current;if(null===f)throw Error("react-cache: read and preload may only be called from within a component's render. They are not supported in event handlers or lifecycle methods.");return f.readContext(c,e)}function q(c){return c}
11 | var r=function(c){function e(){!1===h&&k>g&&(h=!0,m.unstable_scheduleCallback(f))}function f(){h=!1;var d=g;if(null!==a)for(var b=a.previous;k>d&&null!==b;){var c=b.onDelete,e=b.previous;b.onDelete=null;b.previous=b.next=null;b===a?a=b=null:(a.previous=e,e.next=a,b=e);--k;c()}}var g=c,a=null,k=0,h=!1;return{add:function(d,b){d={value:d,onDelete:b,next:null,previous:null};null===a?d.previous=d.next=d:(b=a.previous,b.next=d,d.previous=b,a.previous=d,d.next=a);a=d;k+=1;return d},update:function(a,b){a.value=
12 | b},access:function(d){var b=d.next;if(null!==b){var c=a;if(a!==d){var f=d.previous;f.next=b;b.previous=f;b=c.previous;b.next=d;d.previous=b;c.previous=d;d.next=c;a=d}}e();return d.value},setLimit:function(a){g=a;e()}}}(500),t=new Map,u=l.createContext(null);
13 | function v(c,e,f,g){var a=t.get(c);void 0===a&&(a=new Map,t.set(c,a));var k=a.get(g);if(void 0===k){e=e(f);e.then(function(a){if(0===h.status){var b=h;b.status=1;b.value=a}},function(a){if(0===h.status){var b=h;b.status=2;b.value=a}});var h={status:0,value:e};c=r.add(h,w.bind(null,c,g));a.set(g,c);return h}return r.access(k)}function w(c,e){var f=t.get(c);void 0!==f&&(f.delete(e),0===f.size&&t.delete(c))}
14 | exports.unstable_createResource=function(c,e){var f=void 0!==e?e:q,g={read:function(a){p(u);var e=f(a);a=v(g,c,a,e);switch(a.status){case 0:throw a.value;case 1:return a.value;case 2:throw a.value;}},preload:function(a){p(u);var e=f(a);v(g,c,a,e)}};return g};exports.unstable_setGlobalCacheLimit=function(c){r.setLimit(c)};
15 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/LineTool/LineTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | const { Tool, PaperScope } = ReactPaperJS;
9 |
10 | type ToolType = typeof Paper.Tool;
11 | type ToolEvent = typeof Paper.ToolEvent;
12 |
13 | type Props = {
14 | pathProps: {
15 | strokeColor: string,
16 | },
17 | innerRef: React.Ref
18 | };
19 |
20 | const MOUSE_LEFT_CODE = 0;
21 |
22 | @PaperScope
23 | class LineTool extends PathTool {
24 | static defaultProps = {
25 | ...PathTool.defaultProps,
26 | pathProps: {
27 | strokeColor: 'black',
28 | },
29 | };
30 |
31 | onMouseDown = (toolEvent: ToolEvent) => {
32 | const { pathProps, onMouseDown, onPathInit, paper } = this.props;
33 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
34 | const path = new paper.Path(pathProps);
35 | path.add(toolEvent.point);
36 | this.path = path;
37 | onPathInit(path);
38 | }
39 | onMouseDown(toolEvent);
40 | }
41 |
42 | onMouseDrag = (toolEvent: ToolEvent) => {
43 | const { path } = this;
44 | const { onMouseDrag } = this.props;
45 | if (toolEvent.event.buttons === 1) {
46 | path.removeSegment(1);
47 | path.addSegment(toolEvent.point);
48 | path.selected = true;
49 | }
50 | onMouseDrag(toolEvent);
51 | }
52 |
53 | onMouseUp = (toolEvent: ToolEvent) => {
54 | const { path } = this;
55 | const { onMouseUp, onPathAdd } = this.props;
56 | if (path) {
57 | path.selected = false;
58 | onPathAdd(path);
59 | this.path = null;
60 | }
61 | onMouseUp(toolEvent);
62 | }
63 |
64 | render() {
65 | const {
66 | pathProps, onMouseDown, onMouseDrag, onMouseUp, onPathAdd, innerRef, ...rest
67 | } = this.props;
68 | return (
69 |
76 | );
77 | }
78 | }
79 |
80 | export default (React.forwardRef(
81 | (props, ref) => ,
82 | ): React.AbstractComponent);
83 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const { projectList } = require('./project-list');
2 |
3 | const aliases = process.env.BABEL_ENV === 'rollup'
4 | ? {}
5 | : projectList.reduce((projectAliases, { name, location }) => ({
6 | ...projectAliases,
7 | [name]: `${location}/${process.env.BABEL_ENV === 'test' ? 'src' : 'dist'}`,
8 | }), {});
9 |
10 | module.exports = {
11 | presets: [
12 | [
13 | '@babel/preset-env',
14 | {
15 | modules: false,
16 | },
17 | ],
18 | '@babel/preset-react',
19 | '@babel/preset-flow',
20 | ],
21 | plugins: [
22 | // Stage 1
23 | '@babel/plugin-proposal-export-default-from',
24 | '@babel/plugin-proposal-optional-chaining',
25 | '@babel/plugin-proposal-nullish-coalescing-operator',
26 | '@babel/plugin-proposal-do-expressions',
27 | // Stage 2
28 | [
29 | '@babel/plugin-proposal-decorators',
30 | {
31 | legacy: true,
32 | },
33 | ],
34 | '@babel/plugin-proposal-export-namespace-from',
35 | '@babel/plugin-proposal-numeric-separator',
36 | '@babel/plugin-proposal-throw-expressions',
37 | // Stage 3
38 | '@babel/plugin-syntax-dynamic-import',
39 | [
40 | '@babel/plugin-proposal-class-properties',
41 | {
42 | loose: true,
43 | },
44 | ],
45 | [
46 | '@babel/plugin-proposal-private-methods',
47 | {
48 | loose: true,
49 | },
50 | ],
51 | [
52 | '@babel/plugin-proposal-private-property-in-object',
53 | {
54 | loose: true,
55 | },
56 | ],
57 | '@babel/plugin-proposal-json-strings',
58 | // Custom
59 | [
60 | 'lodash',
61 | {
62 | id: ['lodash'],
63 | },
64 | ],
65 | [
66 | 'module-resolver',
67 | {
68 | root: ['./'],
69 | cwd: './',
70 | alias: {
71 | ...aliases,
72 | 'react-cache': './packages/react-cache', // See: https://github.com/facebook/react/issues/14780#issuecomment-461861948
73 | },
74 | },
75 | ],
76 | 'babel-plugin-styled-components',
77 | ],
78 | env: {
79 | commonjs: {
80 | plugins: [
81 | '@babel/plugin-transform-modules-commonjs',
82 | ],
83 | },
84 | test: {
85 | plugins: [
86 | '@babel/plugin-transform-modules-commonjs',
87 | 'dynamic-import-node',
88 | ],
89 | },
90 | },
91 | };
92 |
--------------------------------------------------------------------------------
/packages/react-cache/umd/react-cache.production.min.js:
--------------------------------------------------------------------------------
1 | /** @license React v16.6.1
2 | * react-cache.production.min.js
3 | *
4 | * Copyright (c) Facebook, Inc. and its affiliates.
5 | *
6 | * This source code is licensed under the MIT license found in the
7 | * LICENSE file in the root directory of this source tree.
8 | */
9 | 'use strict';(function(k,m){"object"===typeof exports&&"undefined"!==typeof module?m(exports,require("react"),require("scheduler")):"function"===typeof define&&define.amd?define(["exports","react","scheduler"],m):m(k.ReactCache={},k.React,k.Scheduler)})(this,function(k,m,u){function q(c,e){var f=v.current;if(null===f)throw Error("react-cache: read and preload may only be called from within a component's render. They are not supported in event handlers or lifecycle methods.");return f.readContext(c,
10 | e)}function w(c){return c}function r(c,e,f,g){var a=n.get(c);void 0===a&&(a=new Map,n.set(c,a));var l=a.get(g);if(void 0===l){e=e(f);e.then(function(a){if(0===h.status){var b=h;b.status=1;b.value=a}},function(a){if(0===h.status){var b=h;b.status=2;b.value=a}});var h={status:0,value:e};c=p.add(h,x.bind(null,c,g));a.set(g,c);return h}return p.access(l)}function x(c,e){var f=n.get(c);void 0!==f&&(f.delete(e),0===f.size&&n.delete(c))}var v=m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher,
11 | p=function(c){function e(){!1===h&&l>g&&(h=!0,u.unstable_scheduleCallback(f))}function f(){h=!1;var b=g;if(null!==a)for(var d=a.previous;l>b&&null!==d;){var c=d.onDelete,e=d.previous;d.onDelete=null;d.previous=d.next=null;d===a?a=d=null:(a.previous=e,e.next=a,d=e);--l;c()}}var g=c,a=null,l=0,h=!1;return{add:function(b,d){b={value:b,onDelete:d,next:null,previous:null};null===a?b.previous=b.next=b:(d=a.previous,d.next=b,b.previous=d,a.previous=b,b.next=a);a=b;l+=1;return b},update:function(a,d){a.value=
12 | d},access:function(b){var d=b.next;if(null!==d){var c=a;if(a!==b){var f=b.previous;f.next=d;d.previous=f;d=c.previous;d.next=b;b.previous=d;c.previous=b;b.next=c;a=b}}e();return b.value},setLimit:function(a){g=a;e()}}}(500),n=new Map,t=m.createContext(null);k.unstable_createResource=function(c,e){var f=void 0!==e?e:w,g={read:function(a){q(t);var e=f(a);a=r(g,c,a,e);switch(a.status){case 0:throw a.value;case 1:return a.value;case 2:throw a.value;}},preload:function(a){q(t);var e=f(a);r(g,c,a,e)}};
13 | return g};k.unstable_setGlobalCacheLimit=function(c){p.setLimit(c)};Object.defineProperty(k,"__esModule",{value:!0})});
14 |
--------------------------------------------------------------------------------
/src/Paper.container.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import Paper from 'paper';
4 |
5 | import PaperRenderer from './Paper.renderer';
6 | import PaperProvider, { type Props as ProviderProps } from './Paper.provider'; // eslint-disable-line no-unused-vars
7 | import { CONSTANTS } from './Paper.types';
8 |
9 | type PaperScope = typeof Paper.PaperScope;
10 |
11 | type Props = {
12 | paper: PaperScope,
13 | renderer: PaperRenderer,
14 | canvasProps: {},
15 | viewProps: {},
16 | onMount?: PaperScope => void,
17 | className: string,
18 | } & ProviderProps;
19 |
20 | // $FlowFixMe
21 | @PaperProvider
22 | class PaperContainer extends React.Component {
23 | static defaultProps = {
24 | onMount: () => {},
25 | };
26 |
27 | mountNode: any;
28 |
29 | canvas: { current: null | HTMLCanvasElement };
30 |
31 | constructor(props: Props) {
32 | super(props);
33 | const { renderer, paper } = this.props;
34 | this.mountNode = renderer.reconciler.createContainer(paper);
35 | this.canvas = React.createRef();
36 | }
37 |
38 | componentDidMount() {
39 | const { paper, onMount } = this.props;
40 | if (this.canvas.current) {
41 | paper.setup(this.canvas.current);
42 | const layer = this.newLayer({ name: '$$default' });
43 | this.newLayer({ name: '$$metadata' });
44 | layer.activate();
45 | this.update();
46 | }
47 | if (onMount) {
48 | onMount(paper);
49 | }
50 | }
51 |
52 | componentDidUpdate() {
53 | this.update();
54 | }
55 |
56 | componentWillUnmount() {
57 | const { renderer } = this.props;
58 | renderer.reconciler.updateContainer(null, this.mountNode, this);
59 | }
60 |
61 | update = () => {
62 | const { paper, viewProps, renderer, children } = this.props;
63 | Object.assign(paper.view, viewProps);
64 | renderer.reconciler.updateContainer(children, this.mountNode, this);
65 | };
66 |
67 | newLayer(options = {}) {
68 | const { paper, renderer } = this.props;
69 | return paper.project
70 | .addLayer(renderer.createInstance(CONSTANTS.Layer, options, paper));
71 | }
72 |
73 | render() {
74 | const { className, canvasProps } = this.props;
75 | return ;
76 | }
77 | }
78 |
79 | export default (React.forwardRef(
80 | (props: any, ref) => ,
81 | ): React.AbstractComponent);
82 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/CircleTool/CircleTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | type ToolType = typeof Paper.Tool;
9 | type ToolEvent = typeof Paper.ToolEvent;
10 |
11 | const { Tool, PaperScope } = ReactPaperJS;
12 |
13 | type Props = {
14 | pathProps: {
15 | fillColor: string,
16 | strokeColor: string,
17 | },
18 | innerRef: React.Ref
19 | };
20 |
21 | const MOUSE_LEFT_CODE = 0;
22 |
23 | @PaperScope
24 | class CircleTool extends PathTool {
25 | static defaultProps = {
26 | ...PathTool.defaultProps,
27 | pathProps: {
28 | fillColor: 'white',
29 | strokeColor: 'black',
30 | },
31 | }
32 |
33 | onMouseDown = (toolEvent: ToolEvent) => {
34 | const { pathProps, onMouseDown, onPathInit, paper } = this.props;
35 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
36 | const { Path, Color } = paper;
37 | const path = new Path.Circle({
38 | center: toolEvent.point,
39 | radius: 1,
40 | fillColor: pathProps.selectedFillColor || new Color(0.9, 0.9, 1, 0.75),
41 | selected: true,
42 | });
43 | this.path = path;
44 | onPathInit(path);
45 | }
46 | onMouseDown(toolEvent);
47 | }
48 |
49 | onMouseDrag = (toolEvent: ToolEvent) => {
50 | const { onMouseDrag } = this.props;
51 | if (toolEvent.event.buttons === 1) {
52 | const { path } = this;
53 | const scale = Math.abs(toolEvent.point.getDistance(path.position) / (path.bounds.width / 2));
54 | if (scale > 0.1) {
55 | path.scale(scale);
56 | }
57 | }
58 | onMouseDrag(toolEvent);
59 | }
60 |
61 | onMouseUp = (event: ToolEvent) => {
62 | const { path } = this;
63 | const { pathProps, onMouseUp, onPathAdd } = this.props;
64 | if (path) {
65 | Object.assign(path, {
66 | selected: false,
67 | ...pathProps,
68 | });
69 | onPathAdd(path);
70 | this.path = null;
71 | }
72 | onMouseUp(event);
73 | }
74 |
75 | render() {
76 | const { innerRef, ...rest } = this.props;
77 | return (
78 |
85 | );
86 | }
87 | }
88 |
89 | export default (React.forwardRef(
90 | (props, ref) => ,
91 | ): React.AbstractComponent);
92 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/SegmentPathTool/SegmentPathTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | const { Tool, PaperScope } = ReactPaperJS;
9 |
10 | type ToolEvent = typeof Paper.ToolEvent;
11 |
12 | type Props = {
13 | pathProps: {
14 | strokeColor: string,
15 | },
16 | pathData: string,
17 | innerRef: React.Ref
18 | };
19 |
20 | const MOUSE_LEFT_CODE = 0;
21 |
22 | @PaperScope
23 | class SegmentPathTool extends PathTool {
24 | static defaultProps = {
25 | ...PathTool.defaultProps,
26 | pathProps: {
27 | strokeColor: 'black',
28 | selected: true,
29 | },
30 | };
31 |
32 | onKeyUp = () => {
33 | const { path, onPathAdd } = this;
34 | if (path) {
35 | if (path.segments.length > 1) {
36 | onPathAdd();
37 | } else {
38 | path.remove();
39 | this.path = null;
40 | }
41 | }
42 | }
43 |
44 | onMouseDown = (toolEvent: ToolEvent) => {
45 | const { path } = this;
46 | if (toolEvent.event.button === MOUSE_LEFT_CODE && toolEvent.modifiers.shift) {
47 | if (!path) {
48 | this.pathInit();
49 | this.props.onPathInit(path);
50 | }
51 | this.onSegmentAdd(toolEvent);
52 | }
53 | this.props.onMouseDown(toolEvent);
54 | }
55 |
56 | pathInit() {
57 | const { pathProps, pathData, paper } = this.props;
58 | const { Path } = paper;
59 | const path = new Path(pathProps);
60 | this.path = path;
61 | this.setPathData(pathData);
62 | }
63 |
64 | setPathData(pathData: string) {
65 | this.path.pathData = pathData;
66 | }
67 |
68 | onSegmentAdd(toolEvent: ToolEvent) {
69 | const { path } = this;
70 | path.add(toolEvent.point);
71 | this.props.onSegmentAdd(path.lastSegment, path);
72 | }
73 |
74 | onPathAdd = () => {
75 | const { path } = this;
76 | const { onPathAdd } = this.props;
77 | path.selected = false;
78 | onPathAdd(path);
79 | this.path = null;
80 | }
81 |
82 | render() {
83 | const {
84 | pathProps,
85 | onKeyUp,
86 | onMouseDown,
87 | onPathAdd,
88 | onSegmentAdd,
89 | onSegmentRemove,
90 | paper,
91 | innerRef,
92 | ...rest
93 | } = this.props;
94 | return (
95 |
101 | );
102 | }
103 | }
104 |
105 | export default (React.forwardRef(
106 | (props, ref) => ,
107 | ): React.AbstractComponent);
108 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/README.md:
--------------------------------------------------------------------------------
1 | # React Paper.js Editor
2 |
3 | [](https://nodejs.org/api/documentation.html#documentation_stability_index)
4 | [](https://www.npmjs.com/package/@psychobolt/react-paperjs-editor)
5 | [](https://travis-ci.org/psychobolt/react-paperjs)
6 | [](https://codecov.io/gh/psychobolt/react-paperjs)
7 |
8 | [](https://david-dm.org/psychobolt/react-paperjs?path=packages/react-paperjs-editor)
9 | [](https://david-dm.org/psychobolt/react-paperjs?path=packages/react-paperjs-editor&type=dev)
10 | [](https://david-dm.org/psychobolt/react-paperjs?path=packages/react-paperjs-editor&type=peer)
11 |
12 | A library of common editor components for [React Paper.js](https://github.com/psychobolt/react-paperjs)
13 |
14 | ## Install
15 |
16 | ```sh
17 | npm install --save @psychobolt/react-paperjs-editor
18 | # or
19 | yarn add @psychobolt/react-paperjs-editor
20 | ```
21 |
22 | ## Examples
23 |
24 | There are several [demos](https://psychobolt.github.io/react-paperjs/?path=/story/packages-react-paperjs-editor-setup). Also check out their [sources](https://github.com/psychobolt/react-paperjs/blob/master/stories/packages/react-paperjs-editor).
25 |
26 | ## Components
27 |
28 | - [CircleTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/CircleTool/CircleTool.md)
29 | - [EllipseTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/EllipseTool/EllipseTool.md)
30 | - [FreeformPathTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/FreeformPathTool/FreeformPathTool.md)
31 | - [Grid](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/Grid/Grid.md)
32 | - [LineTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/LineTool/LineTool.md)
33 | - [PanAndZoom](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/PanAndZoom/PanAndZoom.md)
34 | - [PolygonTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/PolygonTool/PolygonTool.md)
35 | - [RectangleTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/RectangleTool/RectangleTool.md)
36 | - [SegmentPathTool](https://github.com/psychobolt/react-paperjs/blob/master/packages/react-paperjs-editor/src/components/SegmentPathTool/SegmentPathTool.md)
37 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/RectangleTool/RectangleTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | const { Tool, PaperScope } = ReactPaperJS;
9 |
10 | type ToolEvent = typeof Paper.ToolEvent;
11 |
12 | type Props = {
13 | pathProps: {
14 | fillColor: string,
15 | },
16 | innerRef: React.Ref
17 | };
18 |
19 | const MOUSE_LEFT_CODE = 0;
20 |
21 | @PaperScope
22 | class RectangleTool extends PathTool {
23 | static defaultProps = {
24 | ...PathTool.defaultProps,
25 | pathProps: {
26 | fillColor: 'white',
27 | strokeColor: 'black',
28 | },
29 | }
30 |
31 | onMouseDown = (toolEvent: ToolEvent) => {
32 | const { pathProps, onMouseDown, onPathInit, paper } = this.props;
33 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
34 | const { Path, Color } = paper;
35 | const start = toolEvent.point;
36 | const path = new Path.Rectangle({
37 | point: start,
38 | size: [1, 1],
39 | fillColor: pathProps.selectedFillColor || new Color(0.9, 0.9, 1, 0.75),
40 | selected: true,
41 | });
42 | this.path = path;
43 | this.start = start;
44 | onPathInit(path);
45 | }
46 | onMouseDown(toolEvent);
47 | }
48 |
49 | onMouseDrag = (toolEvent: ToolEvent) => {
50 | const { onMouseDrag } = this.props;
51 | if (toolEvent.event.buttons === 1) {
52 | const { path, start } = this;
53 | const { bounds } = path;
54 | const offset = toolEvent.point.subtract(start);
55 | const width = Math.abs(offset.x);
56 | const height = Math.abs(offset.y);
57 | if (offset.x < 0) {
58 | bounds.left = toolEvent.point.x;
59 | bounds.right = start.x;
60 | } else {
61 | bounds.left = start.x;
62 | }
63 | if (offset.y > 0) {
64 | bounds.top = start.y;
65 | bounds.bottom = toolEvent.point.y;
66 | } else {
67 | bounds.top = toolEvent.point.y;
68 | }
69 | if (width > 0) {
70 | bounds.width = width;
71 | }
72 | if (height > 0) {
73 | bounds.height = height;
74 | }
75 | }
76 | onMouseDrag(toolEvent);
77 | }
78 |
79 | onMouseUp = (event: ToolEvent) => {
80 | const { path } = this;
81 | const { pathProps, onMouseUp, onPathAdd } = this.props;
82 | if (path) {
83 | Object.assign(path, {
84 | selected: false,
85 | ...pathProps,
86 | });
87 | onPathAdd(path);
88 | this.path = null;
89 | this.start = null;
90 | }
91 | onMouseUp(event);
92 | }
93 |
94 | start: typeof Paper.Point;
95 |
96 | render() {
97 | const { innerRef, ...rest } = this.props;
98 | return (
99 |
106 | );
107 | }
108 | }
109 |
110 | export default (React.forwardRef(
111 | (props, ref) => ,
112 | ): React.AbstractComponent);
113 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/EllipseTool/EllipseTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | type ToolType = typeof Paper.Tool;
9 | type ToolEvent = typeof Paper.ToolEvent;
10 |
11 | const { Tool, PaperScope } = ReactPaperJS;
12 |
13 | type Props = {
14 | pathProps: {
15 | fillColor: string,
16 | },
17 | innerRef: React.Ref
18 | };
19 |
20 | const MOUSE_LEFT_CODE = 0;
21 |
22 | @PaperScope
23 | class EllipseTool extends PathTool {
24 | static defaultProps = {
25 | ...PathTool.defaultProps,
26 | pathProps: {
27 | fillColor: 'white',
28 | strokeColor: 'black',
29 | },
30 | }
31 |
32 | onMouseDown = (toolEvent: ToolEvent) => {
33 | const { pathProps, onMouseDown, onPathInit, paper } = this.props;
34 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
35 | const { Path, Color } = paper;
36 | const start = toolEvent.point;
37 | const path = new Path.Ellipse({
38 | point: start,
39 | size: [1, 1],
40 | fillColor: pathProps.selectedFillColor || new Color(0.9, 0.9, 1, 0.75),
41 | selected: true,
42 | });
43 |
44 | this.path = path;
45 | this.start = start;
46 | onPathInit(this.path);
47 | }
48 | onMouseDown(toolEvent);
49 | }
50 |
51 | onMouseDrag = (toolEvent: ToolEvent) => {
52 | const { onMouseDrag } = this.props;
53 | if (toolEvent.event.buttons === 1) {
54 | const { path, start } = this;
55 | const { bounds } = path;
56 | const offset = toolEvent.point.subtract(start);
57 | const width = Math.abs(offset.x);
58 | const height = Math.abs(offset.y);
59 | if (offset.x < 0) {
60 | bounds.left = toolEvent.point.x;
61 | bounds.right = start.x;
62 | } else {
63 | bounds.left = start.x;
64 | }
65 | if (offset.y > 0) {
66 | bounds.top = start.y;
67 | bounds.bottom = toolEvent.point.y;
68 | } else {
69 | bounds.top = toolEvent.point.y;
70 | }
71 | if (width > 0) {
72 | bounds.width = width;
73 | }
74 | if (height > 0) {
75 | bounds.height = height;
76 | }
77 | }
78 | onMouseDrag(toolEvent);
79 | }
80 |
81 | onMouseUp = (event: ToolEvent) => {
82 | const { path } = this;
83 | const { pathProps, onMouseUp, onPathAdd } = this.props;
84 | if (path) {
85 | Object.assign(path, {
86 | selected: false,
87 | ...pathProps,
88 | });
89 | onPathAdd(path);
90 | this.path = null;
91 | this.start = null;
92 | }
93 | onMouseUp(event);
94 | }
95 |
96 | start: typeof Paper.Point;
97 |
98 | render() {
99 | const { innerRef, ...rest } = this.props;
100 | return (
101 |
108 | );
109 | }
110 | }
111 |
112 | export default (React.forwardRef(
113 | (props, ref) => ,
114 | ): React.AbstractComponent);
115 |
--------------------------------------------------------------------------------
/src/Paper.provider.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import { defaultMemoize } from 'reselect';
4 | import Paper from 'paper';
5 |
6 | import PaperRenderer from './Paper.renderer';
7 | import { CONSTANTS } from './Paper.types';
8 | import { PaperScopeContext } from './hoc/PaperScope';
9 |
10 | type CanvasProps = {
11 | onWheel: (event: SyntheticWheelEvent) => any
12 | };
13 |
14 | export type EventHandler = (event: any) => any;
15 | export type KeyEventHandler = (event: typeof Paper.KeyEvent) => any;
16 | export type MouseEventHandler = (event: typeof Paper.MouseEvent) => any;
17 | export type ToolEventHandler = (event: typeof Paper.ToolEvent) => any;
18 |
19 | type ViewProps = {
20 | onKeyDown: KeyEventHandler,
21 | onKeyUp: KeyEventHandler,
22 | onMouseDown: MouseEventHandler,
23 | onMouseDrag: MouseEventHandler,
24 | onMouseUp: MouseEventHandler,
25 | zoom: number,
26 | center: {} | number[],
27 | };
28 |
29 | type ScopedProps = (paper: typeof Paper.PaperScope) => P;
30 |
31 | type NestedProps
= P | ScopedProps
;
32 |
33 | export type Props = {
34 | renderer?: typeof PaperRenderer,
35 | innerRef: Object,
36 | viewProps: NestedProps,
37 | canvasProps: NestedProps,
38 | mergeProps: () => any,
39 | children?: React.Node
40 | };
41 |
42 | type State = {
43 | paper: typeof Paper.PaperScope,
44 | viewProps?: NestedProps,
45 | canvasProps?: NestedProps
46 | };
47 |
48 | export function getProps(scope: typeof Paper.PaperScope, props: NestedProps): any {
49 | if (typeof props === 'function') {
50 | return props(scope);
51 | }
52 | return props || {};
53 | }
54 |
55 | const getMergeProps = () => (state, props, scope) => ({
56 | ...getProps(scope, props),
57 | ...getProps(scope, state),
58 | });
59 |
60 | export default (Container => class PaperProvider
61 | extends React.Component {
62 | mergeContainerProps = defaultMemoize(getMergeProps());
63 |
64 | mergeViewProps = defaultMemoize(getMergeProps());
65 |
66 | mergeCanvasProps = defaultMemoize(getMergeProps());
67 |
68 | static defaultProps = {
69 | renderer: PaperRenderer,
70 | children: null,
71 | }
72 |
73 | constructor(props: Props) {
74 | super(props);
75 | const { renderer: Renderer = PaperRenderer } = props;
76 | this.renderer = new Renderer();
77 | this.state = {
78 | paper: this.renderer.createInstance(CONSTANTS.PaperScope, {}, Paper),
79 | mergeProps: props.mergeProps // eslint-disable-line react/no-unused-state
80 | || (mergeProps => this.setState(state => mergeProps(state, props))),
81 | };
82 | }
83 |
84 | renderer: PaperRenderer;
85 |
86 | render() {
87 | const { innerRef, children, viewProps, canvasProps, ...rest } = this.props;
88 | const { viewProps: viewState, canvasProps: canvasState, ...state } = this.state;
89 | return (
90 |
97 |
98 | {children}
99 |
100 |
101 | );
102 | }
103 | }: React.ComponentType => React.AbstractComponent);
104 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Development Guide
2 |
3 | ## Setup
4 |
5 | Install the latest [Node JS LTS](https://nodejs.org/) and [Yarn](https://yarnpkg.com) and simply run ```yarn bootstrap``` command in the root project directory.
6 |
7 | ## Local development
8 |
9 | During development,
10 | ```sh
11 | yarn start # watch, build, and serves packages
12 | ```
13 |
14 | ## Including NPM packages
15 |
16 | ```sh
17 | yarn add --dev # for dev tools, story dependencies, libraries to be bundled
18 | yarn add [--peer] # for external dependencies (Note: Include in externals from rollup.config.common.js whenever update)
19 | yarn lerna add [--dev] packages/] # Add/link a package to all or specific local package(s). See section: Including local packages
20 | ```
21 |
22 | ## Including local packages
23 |
24 | This boilerplate supports [Monorepo](https://danluu.com/monorepo/) configurations out of the box and will watch, build, serve any local packages. Each package should have ```src/index.js``` entry file.
25 |
26 | By default, local packages are [independently](https://github.com/psychobolt/react-rollup-boilerplate/blob/master/lerna.json#L6) versioned. You may import your own repos with Lerna or create your own sub-packages using NPM:
27 |
28 | ```sh
29 | yarn lerna import # import a repository to packages/
30 | # or
31 | mkdir packages/ && cd && yarn init
32 | ```
33 |
34 | See Lerna's offical [readme](https://github.com/lerna/lerna#readme) for a configuration and usage guide.
35 |
36 | > You can also give alias to source files of the packages in order to work with Visual Studio Code's Intellisense. See [jsconfig.json](https://github.com/psychobolt/react-rollup-boilerplate/blob/master/jsconfig.json) and [usage](https://code.visualstudio.com/docs/languages/jsconfig#_using-webpack-aliases).
37 |
38 | ## Main Package
39 |
40 | By default, the ```lerna.json``` defines the main package at the [root](https://github.com/psychobolt/react-rollup-boilerplate/blob/master/lerna.json#L3). You may opt-out of this configuration manually, by removing its settings and any alias references to its directory or package.
41 |
42 | > Note, the main package has one limitation: it cannot include any non-published packages.
43 |
44 | ## Static Types
45 |
46 | ```sh
47 | yarn flow-typed-install # clean & install flow definitions
48 | yarn flow-typed-update # downloads and updates new flow definitions
49 | yarn flow # performs type checking on files
50 | ```
51 |
52 | ## Lint
53 |
54 | ```sh
55 | yarn lint # runs linter to detect any style issues (css & js)
56 | yarn lint:css # lint only css
57 | yarn lint:js # lint only js
58 | yarn lint:js --fix # attempts to fix js lint issues
59 | ```
60 |
61 | ## Test
62 |
63 | ```sh
64 | yarn test # runs functional/unit tests for all packages
65 | ```
66 |
67 | > Configurable with .project file, supports the [PACKAGES](#packages) variable. You can also inspect all tests in debug mode within Visual Studio Code.
68 |
69 | ## Coverage
70 |
71 | Coverage will be uploaded to your [codecov](https://codecov.io/) account, individually for packages by using each package's name as a [flag](https://docs.codecov.io/docs/flags). By default, coverage is configured to utilize a configuration from codecov-config branch (for [example](https://github.com/psychobolt/react-rollup-boilerplate/tree/codecov-config)). However, you may opt out that setting and configure codecov.yml in the master branch.
72 |
73 | ```sh
74 | yarn codecov # Runs tests and upload coverage for all packages
75 | ```
76 |
77 | > Configurable with .project file, supports the [PACKAGES](#packages) variable.
78 |
79 | ## Other scripts
80 |
81 | ```sh
82 |
83 | yarn build # builds sources for prod and dev
84 | yarn build:dev # builds sources for development
85 | yarn build:prod # builds sources for production
86 |
87 | yarn watch # watches dev builds
88 | yarn dist # builds all packages and publishes to npm
89 | ```
90 |
91 | > Configurable with .project file, supports the [PACKAGES](#packages) variable.
92 |
93 | ## Environment Variables
94 |
95 | ### PACKAGES
96 |
97 | Some scripts optionally allow the environment variable to specific local packages(s) (in Glob format) for running scripts e.g. ```PACKAGES=default-export,package-* yarn test``` This environment variable will override the .projects config.
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/PolygonTool/PolygonTool.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import Paper from 'paper';
5 |
6 | import PathTool from '../shared/PathTool';
7 |
8 | const { Tool, PaperScope } = ReactPaperJS;
9 |
10 | type ToolType = typeof Paper.Tool;
11 | type ToolEvent = typeof Paper.ToolEvent;
12 | type Segment = typeof Paper.Segment;
13 |
14 | type Props = {
15 | pathProps: {
16 | strokeColor: string,
17 | },
18 | pathData: string,
19 | innerRef: React.Ref
20 | };
21 |
22 | const MOUSE_LEFT_CODE = 0;
23 |
24 | @PaperScope
25 | class PolygonTool extends PathTool {
26 | static defaultProps = {
27 | ...PathTool.defaultProps,
28 | pathProps: {
29 | strokeColor: 'black',
30 | selected: true,
31 | },
32 | };
33 |
34 | componentDidUpdate() {
35 | const { path, points, props } = this;
36 | const { pathProps, pathData } = props;
37 | if (path) {
38 | this.setPathData(pathData);
39 | Object.assign(path, pathProps);
40 | } else if (points) {
41 | this.pathInit();
42 | }
43 | }
44 |
45 | onMouseDown = (toolEvent: ToolEvent) => {
46 | if (toolEvent.event.button === MOUSE_LEFT_CODE) {
47 | const { path } = this;
48 | if (!path) {
49 | this.pathInit();
50 | this.props.onPathInit(path);
51 | }
52 | if (this.selectedSegment == null) {
53 | this.onSegmentAdd(toolEvent);
54 | } else {
55 | this.onPathAdd();
56 | }
57 | }
58 | this.props.onMouseDown(toolEvent);
59 | }
60 |
61 | pathInit() {
62 | const { pathProps, pathData, paper } = this.props;
63 | const { Path } = paper;
64 | const path = new Path(pathProps);
65 | this.path = path;
66 | this.setPathData(pathData);
67 | }
68 |
69 | setPathData(pathData: string) {
70 | const { path } = this;
71 | this.removeBounds();
72 | path.pathData = pathData;
73 | path.segments.forEach(segment => this.createBounds(segment));
74 | }
75 |
76 | onSegmentAdd(toolEvent: ToolEvent) {
77 | const { path } = this;
78 | path.add(toolEvent.point);
79 | const segment = path.lastSegment;
80 | this.createBounds(segment);
81 | this.props.onSegmentAdd(segment, path);
82 | }
83 |
84 | onPathAdd() {
85 | const { selectedSegment, path, points } = this;
86 | const { onSegmentRemove, onPathAdd } = this.props;
87 | const { index } = selectedSegment;
88 | const segments = path.removeSegments(0, index);
89 | if (segments.length) {
90 | onSegmentRemove(segments, path);
91 | }
92 | path.closed = true;
93 | path.selected = false;
94 | onPathAdd(path);
95 | this.path = null;
96 | this.selectedSegment = null;
97 | if (points) {
98 | points.remove();
99 | }
100 | }
101 |
102 | createBounds(segment: Segment) {
103 | const { paper } = this.props;
104 | const { Path, Group, project } = paper;
105 | const { path, points } = this;
106 | if (!points) {
107 | this.points = new Group();
108 | project.layers.$$metadata.addChild(this.points);
109 | }
110 | const bounds = new Path.Circle({
111 | center: segment.point,
112 | radius: 7,
113 | fillColor: 'white',
114 | opacity: 0,
115 | });
116 | bounds.on('mousedown', () => {
117 | if (!path.closed
118 | && !path.lastSegment.point.equals(bounds.position)
119 | && path.contains(bounds.position)) {
120 | this.selectedSegment = segment;
121 | }
122 | });
123 | this.points.addChild(bounds);
124 | }
125 |
126 | removeBounds() {
127 | if (this.points) {
128 | this.points.remove();
129 | this.points = null;
130 | }
131 | }
132 |
133 | points: typeof Paper.Points;
134 |
135 | selectedSegment: Segment;
136 |
137 | render() {
138 | const {
139 | pathProps, onMouseDown, onPathAdd, onSegmentAdd, onSegmentRemove, paper, innerRef, ...rest
140 | } = this.props;
141 | return (
142 |
147 | );
148 | }
149 | }
150 |
151 | export default (React.forwardRef(
152 | (props, ref) => ,
153 | ): React.AbstractComponent);
154 |
--------------------------------------------------------------------------------
/stories/packages/react-paperjs-editor/Tool/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PaperContainer } from '@psychobolt/react-paperjs';
3 |
4 | import { LineTool, FreeformPathTool, PolygonTool, RectangleTool, CircleTool, SegmentPathTool, EllipseTool } from '@psychobolt/react-paperjs-editor';
5 | import LineToolReadme from 'packages/react-paperjs-editor/src/components/LineTool/LineTool.md';
6 | import FreeformPathToolReadme from 'packages/react-paperjs-editor/src/components/FreeformPathTool/FreeformPathTool.md';
7 | import SegmentPathToolReadme from 'packages/react-paperjs-editor/src/components/SegmentPathTool/SegmentPathTool.md';
8 | import RectangleToolReadme from 'packages/react-paperjs-editor/src/components/RectangleTool/RectangleTool.md';
9 | import CircleToolReadme from 'packages/react-paperjs-editor/src/components/CircleTool/CircleTool.md';
10 | import PolygonToolReadme from 'packages/react-paperjs-editor/src/components/PolygonTool/PolygonTool.md';
11 | import EllipseToolReadme from 'packages/react-paperjs-editor/src/components/EllipseTool/EllipseTool.md';
12 |
13 | import { ref } from '../shared';
14 | import styles from './Tool.styles';
15 |
16 | function onPathAdd(path) {
17 | console.log(path); // eslint-disable-line no-console
18 | }
19 |
20 | export default {
21 | title: 'packages/react-paperjs-editor/Tool',
22 | };
23 |
24 | export const Line = () => (
25 |
26 |
Click and drag to draw a line
27 |
28 |
29 |
30 |
31 | );
32 |
33 | Line.parameters = {
34 | docs: {
35 | page: LineToolReadme,
36 | },
37 | };
38 |
39 | export const FreeformPath = () => (
40 |
41 |
Click and drag to freeform lines.
42 |
43 |
44 |
45 |
46 | );
47 |
48 | FreeformPath.parameters = {
49 | docs: {
50 | page: FreeformPathToolReadme,
51 | },
52 | };
53 |
54 | export const SegmentPath = () => (
55 |
56 |
57 |
Click start to begin.
58 |
Start
59 |
Hold down shift, then left click to plot points to form a segment.
60 |
Release shift to complete segment path.
61 |
62 |
63 |
64 |
65 |
66 | );
67 |
68 | SegmentPath.parameters = {
69 | docs: {
70 | page: SegmentPathToolReadme,
71 | },
72 | };
73 |
74 | export const Rectangle = () => (
75 |
76 |
Click and drag to create rectangle shapes.
77 |
78 |
79 |
80 |
81 | );
82 |
83 | Rectangle.parameters = {
84 | docs: {
85 | page: RectangleToolReadme,
86 | },
87 | };
88 |
89 | export const Circle = () => (
90 |
91 |
Click and drag to create circle shapes.
92 |
93 |
94 |
95 |
96 | );
97 |
98 | Circle.parameters = {
99 | docs: {
100 | page: CircleToolReadme,
101 | },
102 | };
103 |
104 | export const Ellipse = () => (
105 |
106 |
Click and drag to create ellipse shapes.
107 |
108 |
109 |
110 |
111 | );
112 |
113 | Ellipse.parameters = {
114 | docs: {
115 | page: EllipseToolReadme,
116 | },
117 | };
118 |
119 | export const Polygon = () => (
120 |
121 |
122 |
123 | {'Click anywhere to plot points and to create a shape. '}
124 |
125 |
Click near points to close the path and prune dangling points.
126 |
127 |
128 |
129 |
130 |
131 | );
132 |
133 | Polygon.parameters = {
134 | docs: {
135 | page: PolygonToolReadme,
136 | },
137 | };
138 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@psychobolt/react-paperjs",
3 | "version": "1.0.3",
4 | "description": "React Fiber renderer and component container for Paper.js",
5 | "main": "./dist/index.js",
6 | "repository": "https://github.com/psychobolt/react-paperjs.git",
7 | "author": "psychobolt",
8 | "license": "MIT",
9 | "publishConfig": {
10 | "access": "public"
11 | },
12 | "scripts": {
13 | "flow-typed-update": "cross-env BABEL_ENV=commonjs babel-node flow-typed-update.js",
14 | "flow-typed-install": "rimraf flow-typed/npm && npm run flow-typed-update",
15 | "build:dev": "cross-env BABEL_ENV=rollup rollup -c rollup.config.dev.js",
16 | "build:prod": "cross-env BABEL_ENV=rollup rollup -c rollup.config.prod.js",
17 | "clean": "rimraf dist/index.*.js dist/*-*.*.js packages/*/dist/index.*.js packages/*/dist/*-*.*.js",
18 | "build": "npm run clean && npm run build:dev && npm run build:prod",
19 | "watch": "npm run build:dev -- --w",
20 | "start": "concurrently \"npm:watch\" \"npm:storybook\"",
21 | "test": "cross-env NODE_OPTIONS=\"-r @babel/register\" BABEL_ENV=test jest --passWithNoTests",
22 | "lint": "npm run lint:css && npm run lint:js",
23 | "lint:css": "stylelint ./src/**/*.js",
24 | "lint:js": "cross-env BABEL_ENV=commonjs eslint \"**/*.js\"",
25 | "storybook": "start-storybook",
26 | "build-storybook": "build-storybook",
27 | "bootstrap": "yarn install && lerna bootstrap",
28 | "dist": "npm run build && lerna publish",
29 | "codecov": "cross-env BABEL_ENV=commonjs babel-node codecov.js"
30 | },
31 | "devDependencies": {
32 | "@babel/core": "^7.14.0",
33 | "@babel/node": "^7.13.13",
34 | "@babel/plugin-proposal-class-properties": "^7.13.0",
35 | "@babel/plugin-proposal-decorators": "^7.13.15",
36 | "@babel/plugin-proposal-do-expressions": "^7.14.0",
37 | "@babel/plugin-proposal-export-default-from": "^7.12.13",
38 | "@babel/plugin-proposal-export-namespace-from": "^7.12.13",
39 | "@babel/plugin-proposal-json-strings": "^7.13.8",
40 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
41 | "@babel/plugin-proposal-numeric-separator": "^7.12.13",
42 | "@babel/plugin-proposal-optional-chaining": "^7.13.12",
43 | "@babel/plugin-proposal-private-methods": "^7.13.0",
44 | "@babel/plugin-proposal-private-property-in-object": "^7.14.0",
45 | "@babel/plugin-proposal-throw-expressions": "^7.12.13",
46 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
47 | "@babel/plugin-transform-modules-commonjs": "^7.14.0",
48 | "@babel/preset-env": "^7.14.0",
49 | "@babel/preset-flow": "^7.13.13",
50 | "@babel/preset-react": "^7.13.13",
51 | "@babel/register": "^7.13.16",
52 | "@lerna/project": "^4.0.0",
53 | "@rollup/plugin-alias": "^3.1.2",
54 | "@rollup/plugin-babel": "^5.3.0",
55 | "@rollup/plugin-commonjs": "^18.0.0",
56 | "@rollup/plugin-node-resolve": "^11.2.1",
57 | "@storybook/addon-actions": "^6.2.9",
58 | "@storybook/addon-console": "^1.2.3",
59 | "@storybook/addon-essentials": "^6.2.9",
60 | "@storybook/addon-links": "^6.2.9",
61 | "@storybook/cli": "^6.2.9",
62 | "@storybook/react": "^6.2.9",
63 | "@storybook/theming": "^6.2.9",
64 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
65 | "babel-core": "^7.0.0-bridge.0",
66 | "babel-eslint": "10.1.0",
67 | "babel-jest": "^26.6.3",
68 | "babel-loader": "^8.2.2",
69 | "babel-plugin-dynamic-import-node": "^2.3.3",
70 | "babel-plugin-lodash": "^3.3.4",
71 | "babel-plugin-module-resolver": "^4.1.0",
72 | "babel-plugin-styled-components": "^1.12.0",
73 | "cash-true": "^0.0.2",
74 | "codecov": "^3.8.1",
75 | "concurrently": "^6.0.2",
76 | "cross-env": "^7.0.3",
77 | "cross-spawn": "^7.0.3",
78 | "enzyme": "^3.11.0",
79 | "eslint": "^7.25.0",
80 | "eslint-config-airbnb": "^18.2.1",
81 | "eslint-config-react-app": "^6.0.0",
82 | "eslint-import-resolver-babel-module": "^5.3.1",
83 | "eslint-plugin-flowtype": "^5.7.2",
84 | "eslint-plugin-import": "^2.22.1",
85 | "eslint-plugin-jest": "^24.3.6",
86 | "eslint-plugin-jsx-a11y": "^6.4.1",
87 | "eslint-plugin-react": "^7.23.2",
88 | "flow-bin": "^0.150.0",
89 | "flow-typed": "^3.3.1",
90 | "glob": "^7.1.6",
91 | "is-class": "^0.0.9",
92 | "jest": "^26.6.3",
93 | "jest-styled-components": "^7.0.4",
94 | "lerna": "^4.0.0",
95 | "lodash": "^4.17.21",
96 | "micromatch": "^4.0.4",
97 | "paper": "^0.12.15",
98 | "prop-types": "^15.7.2",
99 | "raf": "^3.4.1",
100 | "raw-loader": "^4.0.2",
101 | "re-resizable": "^6.9.0",
102 | "react": "^17.0.2",
103 | "react-cache": "^2.0.0-alpha.1",
104 | "react-dom": "^17.0.2",
105 | "react-reconciler": "0.26.2",
106 | "rimraf": "^3.0.2",
107 | "rollup": "2.46.0",
108 | "rollup-plugin-terser": "^7.0.2",
109 | "slash": "3.0.0",
110 | "source-map-loader": "1.1.0",
111 | "styled-components": "^5.2.3",
112 | "stylelint": "^13.13.1",
113 | "stylelint-config-recommended": "^5.0.0",
114 | "stylelint-config-styled-components": "^0.1.1",
115 | "stylelint-processor-styled-components": "^1.10.0"
116 | },
117 | "dependencies": {
118 | "react-is": "^17.0.2"
119 | },
120 | "peerDependencies": {
121 | "paper": "^0.12.4",
122 | "react": "^17.0.2",
123 | "react-dom": "^17.0.2"
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Paper.js
2 |
3 | [](https://www.npmjs.com/package/@psychobolt/react-paperjs)
4 | [](https://travis-ci.com/psychobolt/react-paperjs)
5 | [](https://codecov.io/gh/psychobolt/react-paperjs)
6 |
7 | [](https://david-dm.org/psychobolt/react-paperjs)
8 | [](https://david-dm.org/psychobolt/react-paperjs?type=dev)
9 | [](https://david-dm.org/psychobolt/react-paperjs?type=peer)
10 |
11 | React fiber renderer and component container for [Paper.js](http://paperjs.org/).
12 |
13 | ## Install
14 |
15 | > Recommended: Paper 0.12.x, React, React DOM 17.x.
16 |
17 | ```sh
18 | npm install --save @psychobolt/react-paperjs
19 | # or
20 | yarn add @psychobolt/react-paperjs
21 | ```
22 |
23 | ## Examples
24 |
25 | There are several [demos](https://psychobolt.github.io/react-paperjs). Also check out their [sources](https://github.com/psychobolt/react-paperjs/blob/master/stories). Here is one to get you started:
26 |
27 | ```jsx
28 | import React from 'react';
29 | import { PaperContainer, Circle, Layer } from '@psychobolt/react-paperjs'
30 |
31 | const Shapes = () => ;
32 |
33 | const App = (props) => (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 |
44 | export default App;
45 | ```
46 |
47 | ## Components
48 |
49 | Common usage with [PaperContainer](#papercontainer) and its default [renderer](#paperrenderer).
50 |
51 | ### PaperContainer
52 |
53 | Provide and creates [Paper Scope](http://paperjs.org/reference/paperscope/) context. To access Paper Scope, you may use the provided [HOC](#paper-scope). All children are rendered into its canvas with [PaperRenderer](#paperrenderer) by default.
54 |
55 | #### Props
56 |
57 | ##### `renderer?: Renderer`
58 |
59 | The default is [PaperRenderer](#paperrenderer). Alternatively, you can [extend](#extension-example) and pass in your own.
60 |
61 | ##### `canvasProps?: {} | (paper) => ({})`
62 |
63 | Props to be passed to ``````. Alternatively, you can provide a function that returns new props.
64 |
65 | ##### `viewProps?: {} | (paper) => ({})`
66 |
67 | Props to be passed to the [View](http://paperjs.org/reference/view/). Alternatively, you can provide a function that returns new props.
68 |
69 | ##### `onMount?: (paper) => myCallback(paper)`
70 |
71 | Callback on container mount.
72 |
73 | ##### `className?: string`
74 |
75 | Canvas element class attribute.
76 |
77 | ### Paper
78 |
79 | Refer supported Paper [types](https://github.com/psychobolt/react-paperjs/blob/master/src/Paper.types.js). All props are passed to the type constructor.
80 |
81 | ## API
82 |
83 | ### PaperRenderer
84 |
85 | Currently a synchronous but extensible implementation.
86 |
87 | #### Members
88 |
89 | ##### `defaultHostConfig: {}`
90 |
91 | The host config that is passed into React Reconciler by default. __This should not be mutated.__ Instead, extend [PaperRenderer](#paperrenderer) with a ```getHostConfig``` function.
92 |
93 | ##### `defaultTypes: { [type: string]: (props: {}, paper: Paper) => Object}`
94 |
95 | A mapping of types with their instance factory method. __This should not be mutated.__ Instead, extend [PaperRenderer](#paperrenderer) with a ```getInstanceFactory``` function.
96 |
97 | #### Extension Example
98 |
99 | ```js
100 | import React from 'React';
101 | import { PaperContainer, PaperRenderer } from '@psychobolt/react-paperjs'
102 |
103 | import MyCustomStencil, { TYPE_NAME as MyCustomStencilComponent } from './MyCustomStencil';
104 |
105 | class MyPaperRenderer extends PaperRenderer {
106 | getInstanceFactory() {
107 | return {
108 | ...this.defaultTypes, /*
109 | refer to default types
110 | see https://github.com/psychobolt/react-paperjs/blob/master/src/Paper.types.js#L42
111 | */
112 | [MyCustomStencilComponent]: (props, paper) => new MyCustomStencil(props),
113 | };
114 | }
115 | }
116 |
117 | const App = (props) => (
118 |
119 |
120 |
121 | );
122 |
123 | export default App;
124 | ```
125 |
126 | The above code adds a custom Component Type to the renderer's instance factory. Then the component can be rendered inside the container.
127 |
128 | ## Higher-order Components
129 |
130 | #### Paper Scope
131 |
132 | Injects Paper Scope as component prop 'paper'.
133 |
134 | Example usage:
135 | ```jsx
136 | import React from 'react';
137 |
138 | import { PaperScope, Circle } from '@psychobolt/react-paperjs';
139 |
140 | export default @PaperScope class Scene {
141 | render() {
142 | const { paper } = this.props;
143 | return ;
144 | }
145 | }
146 | ```
147 |
148 | As an alternative, you can use a helper function:
149 | ```jsx
150 | import React from 'react';
151 |
152 | import { renderWithPaperScope, Circle } from '@psychobolt/react-paperjs';
153 |
154 | export default class Scene {
155 | render() {
156 | return renderWithPaperScope(paper => );
157 | }
158 | }
159 | ```
160 |
161 | ## Extensions
162 |
163 | If you're interested in editor components for React Paper JS, you can checkout another [library](https://www.npmjs.com/package/@psychobolt/react-paperjs-editor) that's work in progress.
164 |
165 | ## Development Guide
166 |
167 | Please see [DEVELOPMENT.md](DEVELOPMENT.md)
168 |
--------------------------------------------------------------------------------
/packages/react-paperjs-editor/src/components/PanAndZoom/PanAndZoom.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react';
3 | import * as ReactPaperJS from '@psychobolt/react-paperjs';
4 | import type { EventHandler } from '@psychobolt/react-paperjs';
5 | import Paper from 'paper';
6 |
7 | const { PaperScope, getProps } = ReactPaperJS;
8 |
9 | type PaperScopeType = typeof Paper.PaperScope;
10 | type KeyEvent = typeof Paper.KeyEvent;
11 |
12 | type DefaultProps = {
13 | onPanEnabled?: () => any,
14 | onPanDisabled?: () => any,
15 | onZoom?: (level: number) => any,
16 | zoomLevel?: number,
17 | };
18 |
19 | type Props = {
20 | center: Object | number[],
21 | paper: PaperScopeType,
22 | mergeProps: (state: {}, props?: {}) => {},
23 | children: any,
24 | } & DefaultProps;
25 |
26 | type State = {
27 | draggable: boolean,
28 | dragStart: ?Object,
29 | };
30 |
31 | function add(num1, num2) {
32 | return ((num1 * 10) + (num2 * 10)) / 10;
33 | }
34 |
35 | function callAllHandlers(handlers: EventHandler[] = []) {
36 | return event => handlers.forEach(handler => handler && handler(event));
37 | }
38 |
39 | export default @PaperScope class PanAndScroll extends React.Component {
40 | static defaultProps: DefaultProps = {
41 | zoomLevel: 1,
42 | onPanEnabled: () => {},
43 | onPanDisabled: () => {},
44 | onZoom: () => {},
45 | };
46 |
47 | constructor(props: Props) {
48 | super(props);
49 | this.state = {
50 | draggable: false,
51 | dragStart: null,
52 | };
53 | }
54 |
55 | componentDidMount() {
56 | const { paper, zoomLevel, center, mergeProps } = this.props;
57 | mergeProps((state, props) => {
58 | const { onWheel, ...canvasProps } = getProps(paper, props.canvasProps);
59 | const {
60 | onKeyDown, onKeyUp, onMouseDown, onMouseDrag, onMouseUp, ...viewProps
61 | } = getProps(paper, props.viewProps);
62 | return {
63 | canvasProps: {
64 | ...canvasProps,
65 | onWheel: callAllHandlers([onWheel, this.onWheel]),
66 | 'drag-state': 'disabled',
67 | },
68 | viewProps: {
69 | ...viewProps,
70 | onKeyDown: callAllHandlers([onKeyDown, this.onKeyDown]),
71 | onKeyUp: callAllHandlers([onKeyUp, this.onKeyUp]),
72 | onMouseDown: callAllHandlers([onMouseDown, this.onMouseDown]),
73 | onMouseDrag: callAllHandlers([onMouseDrag, this.onMouseDrag]),
74 | onMouseUp: callAllHandlers([onMouseUp, this.onMouseUp]),
75 | zoom: zoomLevel,
76 | center,
77 | },
78 | };
79 | });
80 | }
81 |
82 | onWheel: SyntheticWheelEvent => void = ({ deltaY }) => {
83 | const { onZoom, mergeProps } = this.props;
84 | mergeProps((state, props) => {
85 | let { zoom } = state.viewProps;
86 | if (deltaY < 0) {
87 | zoom = add(zoom, 0.1);
88 | if (onZoom) onZoom(zoom);
89 | return {
90 | viewProps: {
91 | ...props.viewProps,
92 | ...state.viewProps,
93 | zoom,
94 | },
95 | };
96 | }
97 | if (deltaY > 0 && zoom > 0.1) {
98 | zoom = add(zoom, -0.1);
99 | if (onZoom) onZoom(zoom);
100 | return {
101 | viewProps: {
102 | ...props.viewProps,
103 | ...state.viewProps,
104 | zoom,
105 | },
106 | };
107 | }
108 | return null;
109 | });
110 | }
111 |
112 | onKeyDown: KeyEvent => void = ({ key }) => {
113 | const { draggable } = this.state;
114 | if (key === 'space' && !draggable) {
115 | const { onPanEnabled, mergeProps } = this.props;
116 | mergeProps((state, props) => ({
117 | ...state,
118 | canvasProps: {
119 | ...props.canvasProps,
120 | ...state.canvasProps,
121 | 'drag-state': 'enabled',
122 | },
123 | }));
124 | this.setState({ draggable: true });
125 | if (onPanEnabled) onPanEnabled();
126 | }
127 | }
128 |
129 | onKeyUp: KeyEvent => void = ({ key }) => {
130 | if (key === 'space') {
131 | const { onPanDisabled, mergeProps } = this.props;
132 | mergeProps((state, props) => ({
133 | ...state,
134 | canvasProps: {
135 | ...props.canvasProps,
136 | ...state.canvasProps,
137 | 'drag-state': 'disabled',
138 | },
139 | }));
140 | this.setState({ draggable: false });
141 | if (onPanDisabled) onPanDisabled();
142 | }
143 | }
144 |
145 | onMouseDown: KeyEvent => void = ({ point }) => {
146 | const { draggable, dragStart } = this.state;
147 | if (draggable && !dragStart) {
148 | const { mergeProps } = this.props;
149 | mergeProps((state, props) => ({
150 | ...state,
151 | canvasProps: {
152 | ...props.canvasProps,
153 | ...state.canvasProps,
154 | 'drag-state': 'dragging',
155 | },
156 | }));
157 | this.setState({ dragStart: point });
158 | }
159 | }
160 |
161 | onMouseUp: () => void = () => {
162 | const { dragStart, draggable } = this.state;
163 | if (dragStart) {
164 | if (draggable) {
165 | const { mergeProps } = this.props;
166 | mergeProps((state, props) => ({
167 | ...state,
168 | canvasProps: {
169 | ...props.canvasProps,
170 | ...state.canvasProps,
171 | 'drag-state': 'enabled',
172 | },
173 | }));
174 | }
175 | this.setState({ dragStart: null });
176 | }
177 | }
178 |
179 | onMouseDrag: KeyEvent => void = ({ point }) => {
180 | const { mergeProps, paper } = this.props;
181 | const { draggable, dragStart } = this.state;
182 | mergeProps((state, props) => {
183 | if (dragStart) {
184 | return {
185 | viewProps: {
186 | ...props.viewProps,
187 | ...state.viewProps,
188 | center:
189 | paper.view.center
190 | .add(point.subtract(dragStart)
191 | .multiply(0.5)),
192 | },
193 | };
194 | }
195 | return null;
196 | });
197 | if (draggable) {
198 | this.setState({ dragStart: point });
199 | }
200 | }
201 |
202 | render(): React.Node {
203 | const { children } = this.props;
204 | return children;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/Paper.renderer.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import Reconciler from 'react-reconciler';
3 | import Paper from 'paper';
4 |
5 | import TYPES, { type Types } from './Paper.types';
6 | import { diffProps, updateProps } from './Paper.component';
7 |
8 | type Props = {
9 | pathData?: string,
10 | children?: any,
11 | };
12 |
13 | type CreateInstance = (type: string, props: Props, paper: typeof Paper.PaperScope) => any;
14 |
15 | export function getTypes(instanceFactory: Types): CreateInstance {
16 | return (type: string, { children, pathData, ...rest }: Props, paper: typeof Paper.PaperScope) => {
17 | const TYPE = instanceFactory[type];
18 | let instance;
19 | if (TYPE) {
20 | instance = TYPE(rest, paper, children);
21 | if (pathData) {
22 | instance.pathData = pathData;
23 | }
24 | }
25 | return instance;
26 | };
27 | }
28 |
29 | const createInstance: CreateInstance = getTypes(TYPES);
30 |
31 | type HostContext = {};
32 |
33 | type Instance = Object;
34 |
35 | type Fiber = {};
36 |
37 | type PaperScope = typeof Paper.PaperScope;
38 |
39 | /* eslint-disable no-console, no-unused-vars */
40 | const defaultHostConfig = {
41 | getRootHostContext(paper: PaperScope): PaperScope {
42 | return paper;
43 | },
44 | getChildHostContext(parentHostContext: HostContext, type: string, instance: Instance): any {
45 | return {};
46 | },
47 | getPublicInstance(instance: Instance): Instance {
48 | return instance;
49 | },
50 | createInstance,
51 | appendInitialChild(parent: Instance, child: Instance) {
52 | if (parent instanceof Paper.Group && child instanceof Paper.Item) {
53 | parent.addChild(child);
54 | } else if (parent instanceof Paper.TextItem && typeof child === 'string') {
55 | Object.assign(parent, { content: child });
56 | } else {
57 | // console.log('ignore append initial child');
58 | }
59 | },
60 | finalizeInitialChildren(instance: Instance, type: string, props: Props): boolean {
61 | return true;
62 | },
63 | commitMount(instance: Instance, type: string, newProps: Props, internalInstanceHandle: Fiber) {
64 | // console.log('ignore commit mount');
65 | },
66 | prepareUpdate(
67 | instance: Instance,
68 | type: string,
69 | oldProps: Props,
70 | newProps: Props,
71 | paper: PaperScope,
72 | hostContext: HostContext,
73 | ): any[] {
74 | return diffProps(oldProps, newProps);
75 | },
76 | shouldSetTextContent(type: string, props: Props): boolean {
77 | const { children } = props;
78 | return typeof children === 'string';
79 | },
80 | shouldDeprioritizeSubtree(type: string, props: Props): boolean {
81 | return false;
82 | },
83 | createTextInstance(
84 | text: string,
85 | paper: PaperScope,
86 | hostContext: HostContext,
87 | internalInstanceHandle: Fiber,
88 | ): string {
89 | return text;
90 | },
91 | scheduleDeferredCallback:
92 | (typeof window !== 'undefined'
93 | ? window.requestIdleCallback
94 | : function dummyRequestIdleCallback(callback, options) {
95 | setTimeout(callback, options.timeout);
96 | }: any | ((callback: () => any, options?: any) => void)),
97 | prepareForCommit(): any {
98 | return null;
99 | },
100 | clearContainer(container: PaperScope) {
101 | // console.log('ignore clear container');
102 | },
103 | resetAfterCommit() {
104 | // console.log('ignore reset for commit');
105 | },
106 | now: Date.now,
107 | supportsMutation: true,
108 | commitUpdate(
109 | instance: Instance,
110 | updatePayload: [],
111 | type: string,
112 | oldProps: Props,
113 | newProps: Props,
114 | internalInstanceHandle: Fiber,
115 | ) {
116 | updateProps(instance, updatePayload, type, oldProps, newProps);
117 | },
118 | commitTextUpdate(textInstance: Instance, oldText: string, newText: string) {
119 | // console.log('ignore commit text update');
120 | },
121 | resetTextContent(instance: Instance) {
122 | // console.log('ignore reset text content');
123 | },
124 | appendChild(parent: Instance, child: Instance) {
125 | if (parent instanceof Paper.Group && child instanceof Paper.Item) {
126 | parent.addChild(child);
127 | } else {
128 | // console.log('ignore append child', parent, child);
129 | }
130 | },
131 | appendChildToContainer(container: PaperScope, child: Instance) {
132 | if (child instanceof Paper.Item) {
133 | const { project } = container;
134 | const { $$default, $$metadata } = project.layers;
135 | if (child instanceof Paper.Layer) {
136 | child.insertBelow($$metadata);
137 | } else {
138 | child.addTo($$default);
139 | }
140 | } else if (child instanceof Paper.Tool) {
141 | child.activate();
142 | } else {
143 | // console.log('ignore append child to container', child);
144 | }
145 | },
146 | insertBefore(parent: Instance, child: Instance, beforeChild: Instance) {
147 | // console.log('ignore insert before child', parent, child, beforeChild);
148 | },
149 | insertInContainerBefore(container: PaperScope, child: Instance, beforeChild: Instance) {
150 | const { $$default, $$metadata } = container.project.layers;
151 | if (child instanceof Paper.Layer && beforeChild instanceof Paper.Layer) {
152 | child.insertBelow(beforeChild);
153 | } else if (child instanceof Paper.Layer) {
154 | child.insertBelow($$metadata);
155 | } else if (child instanceof Paper.Item && beforeChild instanceof Paper.Layer) {
156 | child.addTo($$default);
157 | } else if (child instanceof Paper.Item && beforeChild instanceof Paper.Item) {
158 | child.insertBelow(beforeChild);
159 | } else {
160 | // console.log('ignore insert in container before child', child, beforeChild);
161 | }
162 | },
163 | removeChild(parent: Instance, child: Instance) {
164 | child.remove();
165 | },
166 | removeChildFromContainer(container: PaperScope, child: Instance) {
167 | if (child instanceof Object) {
168 | child.remove();
169 | }
170 | },
171 | };
172 | /* eslint-enable no-console, no-unused-vars */
173 |
174 | export default class PaperRenderer {
175 | defaultHostConfig: typeof defaultHostConfig = defaultHostConfig;
176 |
177 | defaultTypes: Types = TYPES;
178 |
179 | reconciler: any;
180 |
181 | createInstance: CreateInstance;
182 |
183 | constructor() {
184 | const instanceFactory = this.getInstanceFactory();
185 | let hostConfig = this.getHostConfig();
186 |
187 | /* Overrides createInstance if host config is not changed but types are */
188 | if (this.defaultTypes !== instanceFactory
189 | && defaultHostConfig === hostConfig) {
190 | this.createInstance = getTypes(instanceFactory);
191 | hostConfig = {
192 | ...hostConfig,
193 | createInstance: this.createInstance,
194 | };
195 | } else {
196 | this.createInstance = createInstance;
197 | }
198 |
199 | this.reconciler = Reconciler(hostConfig);
200 | }
201 |
202 | getInstanceFactory(): Types {
203 | return this.defaultTypes;
204 | }
205 |
206 | getHostConfig(): typeof defaultHostConfig {
207 | return this.defaultHostConfig;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/packages/react-cache/cjs/react-cache.development.js:
--------------------------------------------------------------------------------
1 | /** @license React v16.6.1
2 | * react-cache.development.js
3 | *
4 | * Copyright (c) Facebook, Inc. and its affiliates.
5 | *
6 | * This source code is licensed under the MIT license found in the
7 | * LICENSE file in the root directory of this source tree.
8 | */
9 |
10 | 'use strict';
11 |
12 |
13 |
14 | if (process.env.NODE_ENV !== "production") {
15 | (function() {
16 | 'use strict';
17 |
18 | Object.defineProperty(exports, '__esModule', { value: true });
19 |
20 | var React = require('react');
21 | var scheduler = require('scheduler');
22 |
23 | /**
24 | * Similar to invariant but only logs a warning if the condition is not met.
25 | * This can be used to log issues in development environments in critical
26 | * paths. Removing the logging code for production environments will keep the
27 | * same logic and follow the same code paths.
28 | */
29 |
30 | var warningWithoutStack = function () {};
31 |
32 | {
33 | warningWithoutStack = function (condition, format) {
34 | for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
35 | args[_key - 2] = arguments[_key];
36 | }
37 |
38 | if (format === undefined) {
39 | throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
40 | }
41 | if (args.length > 8) {
42 | // Check before the condition to catch violations early.
43 | throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
44 | }
45 | if (condition) {
46 | return;
47 | }
48 | if (typeof console !== 'undefined') {
49 | var argsWithFormat = args.map(function (item) {
50 | return '' + item;
51 | });
52 | argsWithFormat.unshift('Warning: ' + format);
53 |
54 | // We intentionally don't use spread (or .apply) directly because it
55 | // breaks IE9: https://github.com/facebook/react/issues/13610
56 | Function.prototype.apply.call(console.error, console, argsWithFormat);
57 | }
58 | try {
59 | // --- Welcome to debugging React ---
60 | // This error was thrown as a convenience so that you can use this stack
61 | // to find the callsite that caused this warning to fire.
62 | var argIndex = 0;
63 | var message = 'Warning: ' + format.replace(/%s/g, function () {
64 | return args[argIndex++];
65 | });
66 | throw new Error(message);
67 | } catch (x) {}
68 | };
69 | }
70 |
71 | var warningWithoutStack$1 = warningWithoutStack;
72 |
73 | function createLRU(limit) {
74 | var LIMIT = limit;
75 |
76 | // Circular, doubly-linked list
77 | var first = null;
78 | var size = 0;
79 |
80 | var cleanUpIsScheduled = false;
81 |
82 | function scheduleCleanUp() {
83 | if (cleanUpIsScheduled === false && size > LIMIT) {
84 | // The cache size exceeds the limit. Schedule a callback to delete the
85 | // least recently used entries.
86 | cleanUpIsScheduled = true;
87 | scheduler.unstable_scheduleCallback(cleanUp);
88 | }
89 | }
90 |
91 | function cleanUp() {
92 | cleanUpIsScheduled = false;
93 | deleteLeastRecentlyUsedEntries(LIMIT);
94 | }
95 |
96 | function deleteLeastRecentlyUsedEntries(targetSize) {
97 | // Delete entries from the cache, starting from the end of the list.
98 | if (first !== null) {
99 | var resolvedFirst = first;
100 | var last = resolvedFirst.previous;
101 | while (size > targetSize && last !== null) {
102 | var _onDelete = last.onDelete;
103 | var _previous = last.previous;
104 | last.onDelete = null;
105 |
106 | // Remove from the list
107 | last.previous = last.next = null;
108 | if (last === first) {
109 | // Reached the head of the list.
110 | first = last = null;
111 | } else {
112 | first.previous = _previous;
113 | _previous.next = first;
114 | last = _previous;
115 | }
116 |
117 | size -= 1;
118 |
119 | // Call the destroy method after removing the entry from the list. If it
120 | // throws, the rest of cache will not be deleted, but it will be in a
121 | // valid state.
122 | _onDelete();
123 | }
124 | }
125 | }
126 |
127 | function add(value, onDelete) {
128 | var entry = {
129 | value: value,
130 | onDelete: onDelete,
131 | next: null,
132 | previous: null
133 | };
134 | if (first === null) {
135 | entry.previous = entry.next = entry;
136 | first = entry;
137 | } else {
138 | // Append to head
139 | var last = first.previous;
140 | last.next = entry;
141 | entry.previous = last;
142 |
143 | first.previous = entry;
144 | entry.next = first;
145 |
146 | first = entry;
147 | }
148 | size += 1;
149 | return entry;
150 | }
151 |
152 | function update(entry, newValue) {
153 | entry.value = newValue;
154 | }
155 |
156 | function access(entry) {
157 | var next = entry.next;
158 | if (next !== null) {
159 | // Entry already cached
160 | var resolvedFirst = first;
161 | if (first !== entry) {
162 | // Remove from current position
163 | var _previous2 = entry.previous;
164 | _previous2.next = next;
165 | next.previous = _previous2;
166 |
167 | // Append to head
168 | var last = resolvedFirst.previous;
169 | last.next = entry;
170 | entry.previous = last;
171 |
172 | resolvedFirst.previous = entry;
173 | entry.next = resolvedFirst;
174 |
175 | first = entry;
176 | }
177 | } else {
178 | // Cannot access a deleted entry
179 | // TODO: Error? Warning?
180 | }
181 | scheduleCleanUp();
182 | return entry.value;
183 | }
184 |
185 | function setLimit(newLimit) {
186 | LIMIT = newLimit;
187 | scheduleCleanUp();
188 | }
189 |
190 | return {
191 | add: add,
192 | update: update,
193 | access: access,
194 | setLimit: setLimit
195 | };
196 | }
197 |
198 | var Pending = 0;
199 | var Resolved = 1;
200 | var Rejected = 2;
201 |
202 | var ReactCurrentDispatcher = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;
203 |
204 | function readContext(Context, observedBits) {
205 | var dispatcher = ReactCurrentDispatcher.current;
206 | if (dispatcher === null) {
207 | throw new Error('react-cache: read and preload may only be called from within a ' + "component's render. They are not supported in event handlers or " + 'lifecycle methods.');
208 | }
209 | return dispatcher.readContext(Context, observedBits);
210 | }
211 |
212 | function identityHashFn(input) {
213 | {
214 | !(typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean' || input === undefined || input === null) ? warningWithoutStack$1(false, 'Invalid key type. Expected a string, number, symbol, or boolean, ' + 'but instead received: %s' + '\n\nTo use non-primitive values as keys, you must pass a hash ' + 'function as the second argument to createResource().', input) : void 0;
215 | }
216 | return input;
217 | }
218 |
219 | var CACHE_LIMIT = 500;
220 | var lru = createLRU(CACHE_LIMIT);
221 |
222 | var entries = new Map();
223 |
224 | var CacheContext = React.createContext(null);
225 |
226 | function accessResult(resource, fetch, input, key) {
227 | var entriesForResource = entries.get(resource);
228 | if (entriesForResource === undefined) {
229 | entriesForResource = new Map();
230 | entries.set(resource, entriesForResource);
231 | }
232 | var entry = entriesForResource.get(key);
233 | if (entry === undefined) {
234 | var thenable = fetch(input);
235 | thenable.then(function (value) {
236 | if (newResult.status === Pending) {
237 | var resolvedResult = newResult;
238 | resolvedResult.status = Resolved;
239 | resolvedResult.value = value;
240 | }
241 | }, function (error) {
242 | if (newResult.status === Pending) {
243 | var rejectedResult = newResult;
244 | rejectedResult.status = Rejected;
245 | rejectedResult.value = error;
246 | }
247 | });
248 | var newResult = {
249 | status: Pending,
250 | value: thenable
251 | };
252 | var newEntry = lru.add(newResult, deleteEntry.bind(null, resource, key));
253 | entriesForResource.set(key, newEntry);
254 | return newResult;
255 | } else {
256 | return lru.access(entry);
257 | }
258 | }
259 |
260 | function deleteEntry(resource, key) {
261 | var entriesForResource = entries.get(resource);
262 | if (entriesForResource !== undefined) {
263 | entriesForResource.delete(key);
264 | if (entriesForResource.size === 0) {
265 | entries.delete(resource);
266 | }
267 | }
268 | }
269 |
270 | function unstable_createResource(fetch, maybeHashInput) {
271 | var hashInput = maybeHashInput !== undefined ? maybeHashInput : identityHashFn;
272 |
273 | var resource = {
274 | read: function (input) {
275 | // react-cache currently doesn't rely on context, but it may in the
276 | // future, so we read anyway to prevent access outside of render.
277 | readContext(CacheContext);
278 | var key = hashInput(input);
279 | var result = accessResult(resource, fetch, input, key);
280 | switch (result.status) {
281 | case Pending:
282 | {
283 | var suspender = result.value;
284 | throw suspender;
285 | }
286 | case Resolved:
287 | {
288 | var _value = result.value;
289 | return _value;
290 | }
291 | case Rejected:
292 | {
293 | var error = result.value;
294 | throw error;
295 | }
296 | default:
297 | // Should be unreachable
298 | return undefined;
299 | }
300 | },
301 | preload: function (input) {
302 | // react-cache currently doesn't rely on context, but it may in the
303 | // future, so we read anyway to prevent access outside of render.
304 | readContext(CacheContext);
305 | var key = hashInput(input);
306 | accessResult(resource, fetch, input, key);
307 | }
308 | };
309 | return resource;
310 | }
311 |
312 | function unstable_setGlobalCacheLimit(limit) {
313 | lru.setLimit(limit);
314 | }
315 |
316 | exports.unstable_createResource = unstable_createResource;
317 | exports.unstable_setGlobalCacheLimit = unstable_setGlobalCacheLimit;
318 | })();
319 | }
320 |
--------------------------------------------------------------------------------
/packages/react-cache/umd/react-cache.development.js:
--------------------------------------------------------------------------------
1 | /** @license React v16.6.1
2 | * react-cache.development.js
3 | *
4 | * Copyright (c) Facebook, Inc. and its affiliates.
5 | *
6 | * This source code is licensed under the MIT license found in the
7 | * LICENSE file in the root directory of this source tree.
8 | */
9 |
10 | 'use strict';
11 |
12 | (function (global, factory) {
13 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('scheduler')) :
14 | typeof define === 'function' && define.amd ? define(['exports', 'react', 'scheduler'], factory) :
15 | (factory((global.ReactCache = {}),global.React,global.Scheduler));
16 | }(this, (function (exports,React,scheduler) { 'use strict';
17 |
18 | /**
19 | * Similar to invariant but only logs a warning if the condition is not met.
20 | * This can be used to log issues in development environments in critical
21 | * paths. Removing the logging code for production environments will keep the
22 | * same logic and follow the same code paths.
23 | */
24 |
25 | var warningWithoutStack = function () {};
26 |
27 | {
28 | warningWithoutStack = function (condition, format) {
29 | for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
30 | args[_key - 2] = arguments[_key];
31 | }
32 |
33 | if (format === undefined) {
34 | throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
35 | }
36 | if (args.length > 8) {
37 | // Check before the condition to catch violations early.
38 | throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
39 | }
40 | if (condition) {
41 | return;
42 | }
43 | if (typeof console !== 'undefined') {
44 | var argsWithFormat = args.map(function (item) {
45 | return '' + item;
46 | });
47 | argsWithFormat.unshift('Warning: ' + format);
48 |
49 | // We intentionally don't use spread (or .apply) directly because it
50 | // breaks IE9: https://github.com/facebook/react/issues/13610
51 | Function.prototype.apply.call(console.error, console, argsWithFormat);
52 | }
53 | try {
54 | // --- Welcome to debugging React ---
55 | // This error was thrown as a convenience so that you can use this stack
56 | // to find the callsite that caused this warning to fire.
57 | var argIndex = 0;
58 | var message = 'Warning: ' + format.replace(/%s/g, function () {
59 | return args[argIndex++];
60 | });
61 | throw new Error(message);
62 | } catch (x) {}
63 | };
64 | }
65 |
66 | var warningWithoutStack$1 = warningWithoutStack;
67 |
68 | function createLRU(limit) {
69 | var LIMIT = limit;
70 |
71 | // Circular, doubly-linked list
72 | var first = null;
73 | var size = 0;
74 |
75 | var cleanUpIsScheduled = false;
76 |
77 | function scheduleCleanUp() {
78 | if (cleanUpIsScheduled === false && size > LIMIT) {
79 | // The cache size exceeds the limit. Schedule a callback to delete the
80 | // least recently used entries.
81 | cleanUpIsScheduled = true;
82 | scheduler.unstable_scheduleCallback(cleanUp);
83 | }
84 | }
85 |
86 | function cleanUp() {
87 | cleanUpIsScheduled = false;
88 | deleteLeastRecentlyUsedEntries(LIMIT);
89 | }
90 |
91 | function deleteLeastRecentlyUsedEntries(targetSize) {
92 | // Delete entries from the cache, starting from the end of the list.
93 | if (first !== null) {
94 | var resolvedFirst = first;
95 | var last = resolvedFirst.previous;
96 | while (size > targetSize && last !== null) {
97 | var _onDelete = last.onDelete;
98 | var _previous = last.previous;
99 | last.onDelete = null;
100 |
101 | // Remove from the list
102 | last.previous = last.next = null;
103 | if (last === first) {
104 | // Reached the head of the list.
105 | first = last = null;
106 | } else {
107 | first.previous = _previous;
108 | _previous.next = first;
109 | last = _previous;
110 | }
111 |
112 | size -= 1;
113 |
114 | // Call the destroy method after removing the entry from the list. If it
115 | // throws, the rest of cache will not be deleted, but it will be in a
116 | // valid state.
117 | _onDelete();
118 | }
119 | }
120 | }
121 |
122 | function add(value, onDelete) {
123 | var entry = {
124 | value: value,
125 | onDelete: onDelete,
126 | next: null,
127 | previous: null
128 | };
129 | if (first === null) {
130 | entry.previous = entry.next = entry;
131 | first = entry;
132 | } else {
133 | // Append to head
134 | var last = first.previous;
135 | last.next = entry;
136 | entry.previous = last;
137 |
138 | first.previous = entry;
139 | entry.next = first;
140 |
141 | first = entry;
142 | }
143 | size += 1;
144 | return entry;
145 | }
146 |
147 | function update(entry, newValue) {
148 | entry.value = newValue;
149 | }
150 |
151 | function access(entry) {
152 | var next = entry.next;
153 | if (next !== null) {
154 | // Entry already cached
155 | var resolvedFirst = first;
156 | if (first !== entry) {
157 | // Remove from current position
158 | var _previous2 = entry.previous;
159 | _previous2.next = next;
160 | next.previous = _previous2;
161 |
162 | // Append to head
163 | var last = resolvedFirst.previous;
164 | last.next = entry;
165 | entry.previous = last;
166 |
167 | resolvedFirst.previous = entry;
168 | entry.next = resolvedFirst;
169 |
170 | first = entry;
171 | }
172 | } else {
173 | // Cannot access a deleted entry
174 | // TODO: Error? Warning?
175 | }
176 | scheduleCleanUp();
177 | return entry.value;
178 | }
179 |
180 | function setLimit(newLimit) {
181 | LIMIT = newLimit;
182 | scheduleCleanUp();
183 | }
184 |
185 | return {
186 | add: add,
187 | update: update,
188 | access: access,
189 | setLimit: setLimit
190 | };
191 | }
192 |
193 | var Pending = 0;
194 | var Resolved = 1;
195 | var Rejected = 2;
196 |
197 | var ReactCurrentDispatcher = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;
198 |
199 | function readContext(Context, observedBits) {
200 | var dispatcher = ReactCurrentDispatcher.current;
201 | if (dispatcher === null) {
202 | throw new Error('react-cache: read and preload may only be called from within a ' + "component's render. They are not supported in event handlers or " + 'lifecycle methods.');
203 | }
204 | return dispatcher.readContext(Context, observedBits);
205 | }
206 |
207 | function identityHashFn(input) {
208 | {
209 | !(typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean' || input === undefined || input === null) ? warningWithoutStack$1(false, 'Invalid key type. Expected a string, number, symbol, or boolean, ' + 'but instead received: %s' + '\n\nTo use non-primitive values as keys, you must pass a hash ' + 'function as the second argument to createResource().', input) : void 0;
210 | }
211 | return input;
212 | }
213 |
214 | var CACHE_LIMIT = 500;
215 | var lru = createLRU(CACHE_LIMIT);
216 |
217 | var entries = new Map();
218 |
219 | var CacheContext = React.createContext(null);
220 |
221 | function accessResult(resource, fetch, input, key) {
222 | var entriesForResource = entries.get(resource);
223 | if (entriesForResource === undefined) {
224 | entriesForResource = new Map();
225 | entries.set(resource, entriesForResource);
226 | }
227 | var entry = entriesForResource.get(key);
228 | if (entry === undefined) {
229 | var thenable = fetch(input);
230 | thenable.then(function (value) {
231 | if (newResult.status === Pending) {
232 | var resolvedResult = newResult;
233 | resolvedResult.status = Resolved;
234 | resolvedResult.value = value;
235 | }
236 | }, function (error) {
237 | if (newResult.status === Pending) {
238 | var rejectedResult = newResult;
239 | rejectedResult.status = Rejected;
240 | rejectedResult.value = error;
241 | }
242 | });
243 | var newResult = {
244 | status: Pending,
245 | value: thenable
246 | };
247 | var newEntry = lru.add(newResult, deleteEntry.bind(null, resource, key));
248 | entriesForResource.set(key, newEntry);
249 | return newResult;
250 | } else {
251 | return lru.access(entry);
252 | }
253 | }
254 |
255 | function deleteEntry(resource, key) {
256 | var entriesForResource = entries.get(resource);
257 | if (entriesForResource !== undefined) {
258 | entriesForResource.delete(key);
259 | if (entriesForResource.size === 0) {
260 | entries.delete(resource);
261 | }
262 | }
263 | }
264 |
265 | function unstable_createResource(fetch, maybeHashInput) {
266 | var hashInput = maybeHashInput !== undefined ? maybeHashInput : identityHashFn;
267 |
268 | var resource = {
269 | read: function (input) {
270 | // react-cache currently doesn't rely on context, but it may in the
271 | // future, so we read anyway to prevent access outside of render.
272 | readContext(CacheContext);
273 | var key = hashInput(input);
274 | var result = accessResult(resource, fetch, input, key);
275 | switch (result.status) {
276 | case Pending:
277 | {
278 | var suspender = result.value;
279 | throw suspender;
280 | }
281 | case Resolved:
282 | {
283 | var _value = result.value;
284 | return _value;
285 | }
286 | case Rejected:
287 | {
288 | var error = result.value;
289 | throw error;
290 | }
291 | default:
292 | // Should be unreachable
293 | return undefined;
294 | }
295 | },
296 | preload: function (input) {
297 | // react-cache currently doesn't rely on context, but it may in the
298 | // future, so we read anyway to prevent access outside of render.
299 | readContext(CacheContext);
300 | var key = hashInput(input);
301 | accessResult(resource, fetch, input, key);
302 | }
303 | };
304 | return resource;
305 | }
306 |
307 | function unstable_setGlobalCacheLimit(limit) {
308 | lru.setLimit(limit);
309 | }
310 |
311 | exports.unstable_createResource = unstable_createResource;
312 | exports.unstable_setGlobalCacheLimit = unstable_setGlobalCacheLimit;
313 |
314 | Object.defineProperty(exports, '__esModule', { value: true });
315 |
316 | })));
317 |
--------------------------------------------------------------------------------