npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-internal-editor

v1.0.2

Published

React.js + ant.design + fabric.js + react-pdf + react-design-editor + material-ui

Downloads

435

Readme

React Internal Editor 1.0.1

npm npm npm npm

React Internal Editor is an assignment editor tool for the internal building operations, which is using React.js + ant.design + fabric.js + react-pdf + react-design-editor + material-ui

Highlighting Features

  • Shortcut Layer Control
  • Region Comparsion (Dormer Window)
  • Export Layer Configurations (full layers)
  • Layer Assignments (Individual or Group)
  • Vector Drawings (Circle, Triangle, Path, Square)
  • Layer Panel (Grouping, Duplication)
  • Floor Panel

Screenshots

Floor Panel

Floor Panel

Shortcut Link Panel

Floor Panel

Region Comparsion

Floor Panel

Demo

Please refer to the demo link: http://dev.4d.com.hk/internaleditor/, or download the demo source react-internal-editor-demo

Important Concept Behinds

The project was using react-pdf and fabricjs to edit the pdf file, while the huge performance challenge for the browser loading and loading the pdf in mobile browser, thus we has changed to use image source control for replacing the pdf manipulation in client's browser. Also, you need to reference the followings by doing the stuffs at the server-side before getting the start for this plugin

  • Convert all of pages from the PDF document after upload Each of page in the PDF document should be separated to the piece of image and the image should be maintained under a correct relationship in the backend-side
const PDFImage = require("pdf-image").PDFImage;
const pdfImage = new PDFImage('./floors.pdf');
pdfImage.convertFile().then(function (imagePaths) {
    // [ /tmp/slide-0.png, /tmp/slide-1.png ]
    // save the relationships for those image (e.g. floor relationship)
});

See pdf-image for more info and all of the dependencies for the pdf-image.

  • Version Comparsion Cropping Use your logic for cropping the region from the full image for any pdf, the followings only for the reference
const sharp = require('sharp');
const fs    = require('fs');
// read from the local directory or remote server using (http request)
const cacheFilePath = './floors.pdf';
sharp(fs.readFileSync(cacheFilePath))
    .extract({
        width: Number(width) || 400,    // width
        height: Number(height) || 300,    // height
        left: Number(left) || 0,    // left
        top: Number(top) || 1,    // top
    })
    .toFile(cacheFilePath)
    .then(() => {
        res.download(cacheFilePath);
        req.on('end', () => {
            fs.unlinkSync(cacheFilePath);
        });
    });

See sharp for more info and all of the dependencies for the sharp.

Getting started

InternalEditor requires Node.js v8.0+ to run.

  1. Install the dependencies and devDependencies and start the server.
$ cd react-internal-editor
$ npm install
$ npm start
  1. Install the latest babel7 dependencies for react-internal-editor and preapre the static babel configuration file, and prepare the properly loaders in your project
$ npm install @babel/[email protected] @babel/[email protected] @babel/[email protected] @babel/[email protected] --save-dev

.babelrc

{
    "presets": [
        "@babel/preset-env", 
        "@babel/preset-react"
    ],
    "plugins": [
        ["@babel/transform-runtime"]
    ]
}

webpack.config.js

module: {
    rules: [
      .....
      .....
      .....
      {
          // important url loader for the svg fonts in `react-internal-editor`
          test: /\.(ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
          loader: 'url-loader',
          options: {
              publicPath: './',
              name: 'fonts/[hash].[ext]',
              limit: 10000,
          },
      },
      {
        test: /\.(css|less)$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',  // important less loader for the `react-internal-editor`
            options: {
              javascriptEnabled: true,
            }
          },
        ]
      }
    ]
}
  1. InternalEditor relies on some endpoints for partially operations.
import InternalEditor from 'react-internal-editor';

// On Shortcut View Clicking
onAreaViewClick(selected) {
    window.console.warn(`Selected Hyperlink data: ${selected}`);
    this.setState({
        // change the pdf image url, to fetch the target floor image
        // or do other stuffs
        sourceUrl: 'http://0.0.0.0:3010/api/viewAreaLink',
    });
}

// Save or export layer's config data
onExportConfig(type, data, closeCallback) {
    // callback the editor to close the save panel, required
    closeCallback();
    window.console.warn(type, data);
}

<InternalEditor
    source={{   // required
        _id: '9e1324686886df234d42', // main pdf id
        name: 'Hong Kong-Zhuhai-Macau Bridge',
    }}
    dataSources={this.state.sources}   // optional
    preview={false}     // optional
    language='en-US'
    sourceEndpoint={{ // source pdf image endpoint, required
        url: this.state.sourceUrl,
        // url: 'https://www.deermember.com/api/testpdf',
        onError: err => {
            alert(err);
        },
    }}
    onSave=this.onExportConfig,
    areaListEndpoint={{
        url: 'http://0.0.0.0:3010/api/testAreaLink',
        onError: err => {
            alert(err);
        },
    }}
    onClickHyperLink={this.onAreaViewClick} 
    groupAssignmentEndpoint={{ // group search list endpoint, required
        url: 'http://0.0.0.0:3010/api/testGroup',
        onError: err => {
            alert(err);
        },
        singleSelection: false,    // multi select
    }}
    userAssignmentEndpoint={{ // user search list endpoint, required
        url: 'http://0.0.0.0:3010/api/testUser',
        onError: err => {
            alert(err);
        },
    }}
    comparsionListEndpoint={{ // comparsion version list endpoint, required
        url: 'http://0.0.0.0:3010/api/testComparsion',
        onError: err => {
            alert(err);
        },
    }}
    comparsionDetailEndpoint={{ // get the blob pdf image for the comparsion item endpoint, required
        url: 'http://0.0.0.0:3010/api/testLoadComparsion',
        onError: err => {
            alert(err);
        },
    }}
    floorList={[    // suppose not update frequently, options
        {
            _id: '9e1324686886df234d42',
            name: 'Hong Kong-Zhuhai-Macau Bridge (First Floor)',    // labe
        },
        {
            _id: '9e142fc86886df234d6d',
            name: 'Hong Kong-Zhuhai-Macau Bridge (Second Floor)',
        },
        {
            _id: '9e1324843443df2523d74',
            name: 'Hong Kong-Zhuhai-Macau Bridge (Third Floor)',
        },
        {
            _id: '84132484344dk2434d74',
            name: 'Hong Kong-Zhuhai-Macau Bridge (Fourth Floor)',
        },
        {
            _id: '6e1324843443df54d74',
            name: 'Hong Kong-Zhuhai-Macau Bridge (Fifth Floor)',
        },
    ]}
    onFloorSelect={selected => window.console.warn(`Selected Floor Plan data: ${selected}`)}   // optional
/>

Component Options

Property | Type | Default | Description :-----------------------|:--------------|:--------------|:-------------------------------- source | object | undefined | Required. A source infomation including target id and name for the configuation export use, you can also store it at your own component state. e.g. { id: "", name: "" } dataSources | object | undefined | Required. A layer configurations toward the source pdf. e.g. { workarea: {}, objects: [], "animations": [], "styles": [], "dataSources": [], } preview | boolean | false | Optional. the editor turns on preview mode, where the panels will be disabled in readonly mode sourceEndpoint | object | undefined | Required. providing the source endpoint api service for fetching the target PDF Image to the editor, onError callback also provides for any error occurs. e.g. { url: "", onError: () => {}, } onSave | function | undefined | Required. layer export callback and the type of save will be also returned either save or 'saveAs' with the source.id areaListEndpoint | object | undefined | Required. providing the shortcut area endpoint api service for fetching the target area item to be selected, while there is the different area item belonging to the current chosen floor. also, onError callback provides for any error occurs. e.g. { url: "", onError: () => {}, } onClickHyperLink | function | undefined | Required. area shortcut callback for the view action, should handle the data configuration changing after the callback for the other target area of the floor groupAssignmentEndpoint | object | undefined | Required. providing the group endpoint api service for fetching the group list to the assignment panel in the editor, onError callback also provides for any error occurs. e.g. { url: "", onError: () => {}, } userAssignmentEndpoint | object | undefined | Required. providing the user endpoint api service for fetching the individual user list to the assignment panel in the editor, onError callback also provides for any error occurs. e.g. { url: "", onError: () => {}, } comparsionListEndpoint | object | undefined | Required. providing the version endpoint api service for fetching the version list to the comparsion panel in the editor towards the chosen floor, onError callback also provides for any error occurs. e.g. { url: "", onError: () => {}, } comparsionDetailEndpoint | object | undefined | Required. providing the version details endpoint api service for cropping the target region towards the chosen version in comparsionListEndpoint, onError callback also provides for any error occurs. e.g. { url: "", onError: () => {}, } floorList | array | [] | Optional. the floor list for the current building project, each of building project should has many floors e.g. [ { _id: "", name: "" } ] onFloorSelect | function | [] | Optional. the floor clicking callback, the other layer configuration data and floor image should be also fetched from sourceEndpoint.url to dataSources with source: { _id: "", name: "" }

Layer Configuration Example

const dataSources = {
    "workarea": {
        "width": 900,
        "height": 900
    },
    "objects": [
        // basic workarea object, required
        {
            "type": "image",
            "version": "2.3.6",
            "originX": "left",
            "originY": "top",
            "left": 297.5,
            "top": 421,
            "width": 0,
            "height": 0,
            "fill": "rgb(0,0,0)",
            "stroke": null,
            "strokeWidth": 0,
            "strokeDashArray": null,
            "strokeLineCap": "butt",
            "strokeLineJoin": "miter",
            "strokeMiterLimit": 4,
            "scaleX": 1,
            "scaleY": 1,
            "angle": 0,
            "flipX": false,
            "flipY": false,
            "opacity": 1,
            "shadow": null,
            "visible": true,
            "clipTo": null,
            "backgroundColor": "rgba(255, 255, 255, 0)",
            "fillRule": "nonzero",
            "paintFirst": "fill",
            "globalCompositeOperation": "source-over",
            "transformMatrix": null,
            "skewX": 0,
            "skewY": 0,
            "crossOrigin": "",
            "cropX": 0,
            "cropY": 0,
            "id": "workarea",
            "name": "",
            "link": {},
            "tooltip": {
                "enabled": false
            },
            "layout": "fixed",
            "workareaWidth": 595,
            "workareaHeight": 842,
            "src": "",
            "filters": []
        },
        {
            "type": "rect",
            "version": "2.3.6",
            "originX": "left",
            "originY": "top",
            "left": 225,
            "top": 230.5,
            "width": 40,
            "height": 40,
            "fill": "rgba(0, 0, 0, 1)",
            "stroke": "rgba(255, 255, 255, 0)",
            "strokeWidth": 1,
            "strokeDashArray": null,
            "strokeLineCap": "butt",
            "strokeLineJoin": "miter",
            "strokeMiterLimit": 4,
            "scaleX": 1,
            "scaleY": 1,
            "angle": 0,
            "flipX": false,
            "flipY": false,
            "opacity": 1,
            "shadow": null,
            "visible": true,
            "clipTo": null,
            "backgroundColor": "",
            "fillRule": "nonzero",
            "paintFirst": "fill",
            "globalCompositeOperation": "source-over",
            "transformMatrix": null,
            "skewX": 0,
            "skewY": 0,
            "rx": 0,
            "ry": 0,
            "id": "886caf5f-3b8b-474c-9330-264b23f2efaa",
            "name": "New shape",
            "link": {
                "enabled": false,
                "type": "resource",
                "state": "new",
                "dashboard": {}
            },
            "tooltip": {
                "enabled": true,
                "type": "resource",
                "template": "<div>{{message.name}}</div>"
            },
            "animation": {
                "type": "none",
                "loop": true,
                "autoplay": true,
                "delay": 100,
                "duration": 1000
            },
            "userProperty": {},
            "trigger": {
                "enabled": false,
                "type": "alarm",
                "script": "return message.value > 0;",
                "effect": "style"
            }
        },
    ],
    "animations": [],
    "styles": [],
    "dataSources": [],
};

Development

$ cd react-internal-editor
$ npm install
$ npm start

Open http://0.0.0.0:3030 to view react-internal-editor

Depolyment

$ cd react-internal-editor
$ npm run publish

Todos

  1. Features
  • Job Assignments Control
  • Area Separation
  • Job Accomplishment
  1. Environment
  • Migrate to React 16.9.0 with context hooks
  • Upgrade Material-ui will the latest core set
  • Remove useless code for 'react-designer-editor'

Dependencies

react-internal-editor has some external dependencies, which are the usual react, react-dom, ant.design, material-ui, i18next and fabric, as well as design-editor.

Changelogs

Verion 1.0.2 (29/08/2019)

  • Add Demo Project
  • Fix the issue fot the use of react-rangeslider at the Region Comparsion Panel.

Verion 1.0.1 (28/08/2019)

  • Downgrade React.js to v16.4.0 from v16.9.0
  • Add publishing script for the distribution release using babel-cil
  • Remove Slider from @material-ui/code, use react-rangeslider instead.
  • Remove pdf-lib and react-pdf dependencies
  • Add babel.config.js instead of .babelrc static configuration file

License

react-internal-editor is available under the MIT License