
Product
Introducing Socket Firewall Enterprise: Flexible, Configurable Protection for Modern Package Ecosystems
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.
react-internal-editor
Advanced tools
React.js + ant.design + fabric.js + react-pdf + react-design-editor + material-ui
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



Please refer to the demo link: http://dev.4d.com.hk/internaleditor/, or download the demo source react-internal-editor-demo
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
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.
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.
InternalEditor requires Node.js v8.0+ to run.
$ cd react-internal-editor
$ npm install
$ npm start
react-internal-editor and preapre the static babel configuration file, and prepare the properly loaders in your project$ npm install @babel/runtime@7.5.5 @babel/preset-env@7.5.5 @babel/core@7.5.5 @babel/preset-react@7.0.0 --save-dev
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
["@babel/transform-runtime"]
]
}
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,
}
},
]
}
]
}
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
/>
| 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: "" } |
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": [],
};
$ cd react-internal-editor
$ npm install
$ npm start
Open http://0.0.0.0:3030 to view react-internal-editor
$ cd react-internal-editor
$ npm run publish
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.
react-internal-editor is available under the MIT License
FAQs
React.js + ant.design + fabric.js + react-pdf + react-design-editor + material-ui
We found that react-internal-editor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authorities’ publishing activity, highlighting trends and transparency across the CVE ecosystem.

Product
Detect malware, unsafe data flows, and license issues in GitHub Actions with Socket’s new workflow scanning support.