diff --git a/LICENSE b/LICENSE index 6c9a441..3981b27 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2020 Mahyar Madad Copyright (c) 2020 Xuebin Dong Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index 321be88..9177a1c 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,19 @@ -![OrgChart](http://dabeng.github.io/OrgChart/img/heading.svg) - -# [jQuery Version](https://github.com/dabeng/OrgChart) -# [ES6 Version](http://github.com/dabeng/OrgChart.js) -# [Web Components Version](http://github.com/dabeng/OrgChart-Webcomponents) -# [Vue.js Version](https://github.com/dabeng/vue-orgchart) -# [Angular Version -- the most space-saving solution](https://github.com/dabeng/ng-orgchart) - -## Features -- expand/collapse nodes -- Allows user to change orgchart structure by drag/drop nodes -- Allows user to edit orgchart -- Supports exporting chart as a picture or pdf document -- Supports pan and zoom -- Allows user to customize the internal structure for every node - -## Props - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
chartClassstringA css class to apply to the chart DOM node.
containerClassstringAdd an optional extra class name to .orgchart-container It could end up looking like class="orgchart-container xxx yyy".
collapsiblebooleantrueAllows expanding/collapsing the nodes.
datasourceobjectdatasource usded to build out structure of orgchart.
draggablebooleanfalseAllows dragging/dropping the nodes to the hierarchy of chart.
multipleSelectbooleanfalseIf true, user can select multiple nodes by mouse clicking.
NodeTemplateelementTypeA Component that provides the node content Markup. This is a useful prop when you want to use your own styles and markup to create a custom orgchart node.
onClickChartfunctionA callback fired when the orgchart is clicking.
onClickNodefunctionA callback fired when the node is clicking.
panbooleanfalseIf true, the chart can be panned.
panbooleanfalseIf true, the chart can be zoomed.
zoominLimitnumber7User can zoom the chart at different scales(0.5~7).
zoomoutLimitnumber0.5User can zoom the chart at different scales(0.5~7).
- -## Methods - - - - - - - - - - - - - - - - - -
NameDescription
expandAllNodesUser can use this method to expand all the nodes. Sample code: orgchartRef.current.expandAllNodes()
expandAllNodesUser can use this method to export orgchart to png org pdf file. Sample code: orgchartRef.current.exportTo(filename, fileextension)
- -## Install -``` -npm install @dabeng/react-orgchart -``` - -## [Demo](https://codesandbox.io/s/react-orgchart-demo-o3nf6) - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +

Hi guys

+ +this is an orgchart Libary that work with nextjs , we use React-orgchart libary and made some major changes to make it work with Nextjs + +

How to Use

+First you need to install the Liabry using npm : +npm i nextjs-orgchart + +Then u need to import the css file in your \_app.js : +import "nextjs-orgchart/dist/ChartContainer.css"; +import "nextjs-orgchart/dist/ChartNode.css"; + +After that u can import the chart in your component file : +import OrganizationChart from "nextjs-orgchart" + +and use it like the React-orgchart Documents ## Available Scripts @@ -171,33 +51,3 @@ If you aren’t satisfied with the build tool and configuration choices, you can Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting - -### Analyzing the Bundle Size - -This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size - -### Making a Progressive Web App - -This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app - -### Advanced Configuration - -This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration - -### Deployment - -This section has moved here: https://facebook.github.io/create-react-app/docs/deployment - -### `npm run build` fails to minify - -This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify diff --git a/dist/ChartContainer.css b/dist/ChartContainer.css index bd43bd4..5c19de7 100644 --- a/dist/ChartContainer.css +++ b/dist/ChartContainer.css @@ -1,8 +1,10 @@ +/* global styles for orgchart */ .orgchart-container { box-sizing: border-box; position: relative; margin: 10px; height: 700px; + background-color: #fff; border: 2px dashed #aaa; border-radius: 5px; overflow: auto; @@ -12,13 +14,39 @@ .orgchart { box-sizing: border-box; display: inline-block; - background-image: linear-gradient( - 90deg, - rgba(200, 0, 0, 0.15) 10%, - rgba(0, 0, 0, 0) 10% - ), - linear-gradient(rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%); + background-image: linear-gradient(90deg, rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%), linear-gradient(rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%); background-size: 10px 10px; - border: 1px dashed rgba(0, 0, 0, 0); + border: 1px dashed rgba(0,0,0,0); padding: 20px 20px 0 20px; } + +.orgchart-container .hidden { + display: none!important; +} + +.orgchart-container > .oc-mask { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + text-align: center; + background-color: rgba(0,0,0,0.3); +} + +.orgchart-container > .oc-mask .spinner { + position: absolute; + top: calc(50% - 50px); + left: calc(50% - 50px); +} + +.orgchart-container > .oc-mask .spinner::before { + width: 100px; + height: 100px; + border-width: 10px; + border-radius: 50px; + border-top-color: rgba(68, 157, 68, 0.8); + border-bottom-color: rgba(68, 157, 68, 0.8); + border-left-color: rgba(68, 157, 68, 0.8); +} diff --git a/dist/ChartContainer.js b/dist/ChartContainer.js index 3251e91..e0450d5 100644 --- a/dist/ChartContainer.js +++ b/dist/ChartContainer.js @@ -5,35 +5,37 @@ Object.defineProperty(exports, "__esModule", { }); exports.default = void 0; -var _react = _interopRequireDefault(require("react")); +var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); -var _ChartNode = _interopRequireDefault(require("./ChartNode")); +var _service = require("./service"); + +var _jsonDigger = _interopRequireDefault(require("json-digger")); -require("./ChartContainer.css"); +var _ChartNode = _interopRequireDefault(require("./ChartNode")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var propTypes = { datasource: _propTypes.default.object.isRequired, @@ -43,7 +45,12 @@ var propTypes = { zoominLimit: _propTypes.default.number, containerClass: _propTypes.default.string, chartClass: _propTypes.default.string, - nodeTemplate: _propTypes.default.elementType + NodeTemplate: _propTypes.default.elementType, + draggable: _propTypes.default.bool, + collapsible: _propTypes.default.bool, + multipleSelect: _propTypes.default.bool, + onClickNode: _propTypes.default.func, + onClickChart: _propTypes.default.func }; var defaultProps = { pan: false, @@ -51,199 +58,335 @@ var defaultProps = { zoomoutLimit: 0.5, zoominLimit: 7, containerClass: "", - chartClass: "" + chartClass: "", + draggable: false, + collapsible: true, + multipleSelect: false }; +var ChartContainer = (0, _react.forwardRef)(function (_ref, ref) { + var datasource = _ref.datasource, + pan = _ref.pan, + zoom = _ref.zoom, + zoomoutLimit = _ref.zoomoutLimit, + zoominLimit = _ref.zoominLimit, + containerClass = _ref.containerClass, + chartClass = _ref.chartClass, + NodeTemplate = _ref.NodeTemplate, + draggable = _ref.draggable, + collapsible = _ref.collapsible, + multipleSelect = _ref.multipleSelect, + onClickNode = _ref.onClickNode, + onClickChart = _ref.onClickChart; + var container = (0, _react.useRef)(); + var chart = (0, _react.useRef)(); + var downloadButton = (0, _react.useRef)(); + + var _useState = (0, _react.useState)(0), + _useState2 = _slicedToArray(_useState, 2), + startX = _useState2[0], + setStartX = _useState2[1]; + + var _useState3 = (0, _react.useState)(0), + _useState4 = _slicedToArray(_useState3, 2), + startY = _useState4[0], + setStartY = _useState4[1]; + + var _useState5 = (0, _react.useState)(""), + _useState6 = _slicedToArray(_useState5, 2), + transform = _useState6[0], + setTransform = _useState6[1]; + + var _useState7 = (0, _react.useState)(false), + _useState8 = _slicedToArray(_useState7, 2), + panning = _useState8[0], + setPanning = _useState8[1]; + + var _useState9 = (0, _react.useState)("default"), + _useState10 = _slicedToArray(_useState9, 2), + cursor = _useState10[0], + setCursor = _useState10[1]; + + var _useState11 = (0, _react.useState)(false), + _useState12 = _slicedToArray(_useState11, 2), + exporting = _useState12[0], + setExporting = _useState12[1]; + + var _useState13 = (0, _react.useState)(""), + _useState14 = _slicedToArray(_useState13, 2), + dataURL = _useState14[0], + setDataURL = _useState14[1]; + + var _useState15 = (0, _react.useState)(""), + _useState16 = _slicedToArray(_useState15, 2), + download = _useState16[0], + setDownload = _useState16[1]; + + var attachRel = function attachRel(data, flags) { + data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0); + + if (data.children) { + data.children.forEach(function (item) { + attachRel(item, "1" + (data.children.length > 1 ? 1 : 0)); + }); + } -var ChartContainer = -/*#__PURE__*/ -function (_React$Component) { - _inherits(ChartContainer, _React$Component); - - function ChartContainer() { - var _this; - - _classCallCheck(this, ChartContainer); + return data; + }; - _this = _possibleConstructorReturn(this, _getPrototypeOf(ChartContainer).call(this)); + var _useState17 = (0, _react.useState)(datasource), + _useState18 = _slicedToArray(_useState17, 2), + ds = _useState18[0], + setDS = _useState18[1]; - _defineProperty(_assertThisInitialized(_this), "startX", 0); + (0, _react.useEffect)(function () { + setDS(datasource); + }, [datasource]); + var dsDigger = new _jsonDigger.default(datasource, "id", "children"); - _defineProperty(_assertThisInitialized(_this), "startY", 0); + var clickChartHandler = function clickChartHandler(event) { + if (!event.target.closest(".oc-node")) { + if (onClickChart) { + onClickChart(); + } - _this.state = { - transform: "", - panning: false, - cursor: "default" - }; - return _this; - } - - _createClass(ChartContainer, [{ - key: "render", - value: function render() { - var _this$props = this.props, - datasource = _this$props.datasource, - containerClass = _this$props.containerClass, - chartClass = _this$props.chartClass; - return _react.default.createElement("div", { - className: "orgchart-container " + containerClass, - onWheel: this.props.zoom ? this.zoomHandler.bind(this) : undefined, - onMouseUp: this.props.pan && this.state.panning ? this.panEndHandler.bind(this) : undefined - }, _react.default.createElement("div", { - className: "orgchart " + chartClass, - style: { - transform: this.state.transform, - cursor: this.state.cursor - }, - onMouseDown: this.props.pan ? this.panStartHandler.bind(this) : undefined, - onMouseMove: this.props.pan && this.state.panning ? this.panHandler.bind(this) : undefined - }, _react.default.createElement("ul", null, _react.default.createElement(_ChartNode.default, { - datasource: datasource, - nodeTemplate: this.props.nodeTemplate - })))); + _service.selectNodeService.clearSelectedNodeInfo(); } - }, { - key: "panEndHandler", - value: function panEndHandler() { - this.setState({ - panning: false, - cursor: "default" - }); + }; + + var panEndHandler = function panEndHandler() { + setPanning(false); + setCursor("default"); + }; + + var panHandler = function panHandler(e) { + var newX = 0; + var newY = 0; + + if (!e.targetTouches) { + // pand on desktop + newX = e.pageX - startX; + newY = e.pageY - startY; + } else if (e.targetTouches.length === 1) { + // pan on mobile device + newX = e.targetTouches[0].pageX - startX; + newY = e.targetTouches[0].pageY - startY; + } else if (e.targetTouches.length > 1) { + return; } - }, { - key: "panHandler", - value: function panHandler(e) { - var newX = 0; - var newY = 0; - - if (!e.targetTouches) { - // pand on desktop - newX = e.pageX - this.startX; - newY = e.pageY - this.startY; - } else if (e.targetTouches.length === 1) { - // pan on mobile device - newX = e.targetTouches[0].pageX - this.startX; - newY = e.targetTouches[0].pageY - this.startY; - } else if (e.targetTouches.length > 1) { - return; - } - if (this.state.transform === "") { - if (this.state.transform.indexOf("3d") === -1) { - this.setState({ - transform: "matrix(1,0,0,1," + newX + "," + newY + ")" - }); - } else { - this.setState({ - transform: "matrix3d(1,0,0,0,0,1,0,0,0,0,1,0," + newX + ", " + newY + ",0,1)" - }); - } + if (transform === "") { + if (transform.indexOf("3d") === -1) { + setTransform("matrix(1,0,0,1," + newX + "," + newY + ")"); } else { - var matrix = this.state.transform.split(","); - - if (this.state.transform.indexOf("3d") === -1) { - matrix[4] = newX; - matrix[5] = newY + ")"; - } else { - matrix[12] = newX; - matrix[13] = newY; - } - - this.setState({ - transform: matrix.join(",") - }); + setTransform("matrix3d(1,0,0,0,0,1,0,0,0,0,1,0," + newX + ", " + newY + ",0,1)"); } - } - }, { - key: "panStartHandler", - value: function panStartHandler(e) { - if (e.target.closest(".oc-node")) { - this.setState({ - panning: false - }); - return; + } else { + var matrix = transform.split(","); + + if (transform.indexOf("3d") === -1) { + matrix[4] = newX; + matrix[5] = newY + ")"; } else { - this.setState({ - panning: true, - cursor: "move" - }); + matrix[12] = newX; + matrix[13] = newY; } - var lastX = 0; - var lastY = 0; + setTransform(matrix.join(",")); + } + }; + + var panStartHandler = function panStartHandler(e) { + if (e.target.closest(".oc-node")) { + setPanning(false); + return; + } else { + setPanning(true); + setCursor("move"); + } - if (this.state.transform !== "") { - var matrix = this.state.transform.split(","); + var lastX = 0; + var lastY = 0; - if (this.state.transform.indexOf("3d") === -1) { - lastX = parseInt(matrix[4]); - lastY = parseInt(matrix[5]); - } else { - lastX = parseInt(matrix[12]); - lastY = parseInt(matrix[13]); - } - } + if (transform !== "") { + var matrix = transform.split(","); - if (!e.targetTouches) { - // pand on desktop - this.startX = e.pageX - lastX; - this.startY = e.pageY - lastY; - } else if (e.targetTouches.length === 1) { - // pan on mobile device - this.startX = e.targetTouches[0].pageX - lastX; - this.startY = e.targetTouches[0].pageY - lastY; - } else if (e.targetTouches.length > 1) { - return; + if (transform.indexOf("3d") === -1) { + lastX = parseInt(matrix[4]); + lastY = parseInt(matrix[5]); + } else { + lastX = parseInt(matrix[12]); + lastY = parseInt(matrix[13]); } } - }, { - key: "setChartScale", - value: function setChartScale(newScale) { - var matrix = []; - var targetScale = 1; - - if (this.state.transform === "") { - this.setState({ - transform: "matrix(" + newScale + ", 0, 0, " + newScale + ", 0, 0)" - }); + + if (!e.targetTouches) { + // pand on desktop + setStartX(e.pageX - lastX); + setStartY(e.pageY - lastY); + } else if (e.targetTouches.length === 1) { + // pan on mobile device + setStartX(e.targetTouches[0].pageX - lastX); + setStartY(e.targetTouches[0].pageY - lastY); + } else if (e.targetTouches.length > 1) { + return; + } + }; + + var updateChartScale = function updateChartScale(newScale) { + var matrix = []; + var targetScale = 1; + + if (transform === "") { + setTransform("matrix(" + newScale + ", 0, 0, " + newScale + ", 0, 0)"); + } else { + matrix = transform.split(","); + + if (transform.indexOf("3d") === -1) { + targetScale = Math.abs(window.parseFloat(matrix[3]) * newScale); + + if (targetScale > zoomoutLimit && targetScale < zoominLimit) { + matrix[0] = "matrix(" + targetScale; + matrix[3] = targetScale; + setTransform(matrix.join(",")); + } } else { - matrix = this.state.transform.split(","); - - if (this.state.transform.indexOf("3d") === -1) { - targetScale = Math.abs(window.parseFloat(matrix[3]) * newScale); - - if (targetScale > this.props.zoomoutLimit && targetScale < this.props.zoominLimit) { - matrix[0] = "matrix(" + targetScale; - matrix[3] = targetScale; - this.setState({ - transform: matrix.join(",") - }); - } - } else { - targetScale = Math.abs(window.parseFloat(matrix[5]) * newScale); - - if (targetScale > this.props.zoomoutLimit && targetScale < this.props.zoominLimit) { - matrix[0] = "matrix3d(" + targetScale; - matrix[5] = targetScale; - this.setState({ - transform: matrix.join(",") - }); - } + targetScale = Math.abs(window.parseFloat(matrix[5]) * newScale); + + if (targetScale > zoomoutLimit && targetScale < zoominLimit) { + matrix[0] = "matrix3d(" + targetScale; + matrix[5] = targetScale; + setTransform(matrix.join(",")); } } } - }, { - key: "zoomHandler", - value: function zoomHandler(e) { - var newScale = 1 + (e.deltaY > 0 ? -0.2 : 0.2); - this.setChartScale(newScale); + }; + + var zoomHandler = function zoomHandler(e) { + var newScale = 1 + (e.deltaY > 0 ? -0.2 : 0.2); + updateChartScale(newScale); + }; + + var exportPDF = function exportPDF(canvas, exportFilename) { + Promise.resolve().then(function () { + return _interopRequireWildcard(require("jspdf")); + }).then(function (jsPDF) { + var canvasWidth = Math.floor(canvas.width); + var canvasHeight = Math.floor(canvas.height); + var doc = canvasWidth > canvasHeight ? new jsPDF.default({ + orientation: "landscape", + unit: "px", + format: [canvasWidth, canvasHeight] + }) : new jsPDF.default({ + orientation: "portrait", + unit: "px", + format: [canvasHeight, canvasWidth] + }); + doc.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", 0, 0); + doc.save(exportFilename + ".pdf"); + }); + }; + + var exportPNG = function exportPNG(canvas, exportFilename) { + var isWebkit = "WebkitAppearance" in document.documentElement.style; + var isFf = !!window.sidebar; + var isEdge = navigator.appName === "Microsoft Internet Explorer" || navigator.appName === "Netscape" && navigator.appVersion.indexOf("Edge") > -1; + + if (!isWebkit && !isFf || isEdge) { + window.navigator.msSaveBlob(canvas.msToBlob(), exportFilename + ".png"); + } else { + setDataURL(canvas.toDataURL()); + setDownload(exportFilename + ".png"); + downloadButton.current.click(); } - }]); - - return ChartContainer; -}(_react.default.Component); - + }; + + var changeHierarchy = function changeHierarchy(draggedItemData, dropTargetId) { + return dsDigger.removeNode(draggedItemData.id).then(function () { + return dsDigger.addChildren(dropTargetId, draggedItemData); + }).then(function () { + setDS(_objectSpread({}, dsDigger.ds)); + }); + }; + + (0, _react.useImperativeHandle)(ref, function () { + return { + exportTo: function exportTo(exportFilename, exportFileextension) { + exportFilename = exportFilename || "OrgChart"; + exportFileextension = exportFileextension || "png"; + setExporting(true); + var originalScrollLeft = container.current.scrollLeft; + container.current.scrollLeft = 0; + var originalScrollTop = container.current.scrollTop; + container.current.scrollTop = 0; + Promise.resolve().then(function () { + return _interopRequireWildcard(require("html2canvas")); + }).then(function (html2canvas) { + html2canvas.default(chart.current, { + width: chart.current.clientWidth, + height: chart.current.clientHeight, + onclone: function onclone(clonedDoc) { + clonedDoc.querySelector(".orgchart").style.background = "none"; + clonedDoc.querySelector(".orgchart").style.transform = ""; + } + }).then(function (canvas) { + if (exportFileextension.toLowerCase() === "pdf") { + exportPDF(canvas, exportFilename); + } else { + exportPNG(canvas, exportFilename); + } + + setExporting(false); + container.current.scrollLeft = originalScrollLeft; + container.current.scrollTop = originalScrollTop; + }, function () { + setExporting(false); + container.current.scrollLeft = originalScrollLeft; + container.current.scrollTop = originalScrollTop; + }); + }); + }, + expandAllNodes: function expandAllNodes() { + chart.current.querySelectorAll(".oc-node.hidden, .oc-hierarchy.hidden, .isSiblingsCollapsed, .isAncestorsCollapsed").forEach(function (el) { + el.classList.remove("hidden", "isSiblingsCollapsed", "isAncestorsCollapsed"); + }); + } + }; + }); + return _react.default.createElement("div", { + ref: container, + className: "orgchart-container " + containerClass, + onWheel: zoom ? zoomHandler : undefined, + onMouseUp: pan && panning ? panEndHandler : undefined + }, _react.default.createElement("div", { + ref: chart, + className: "orgchart " + chartClass, + style: { + transform: transform, + cursor: cursor + }, + onClick: clickChartHandler, + onMouseDown: pan ? panStartHandler : undefined, + onMouseMove: pan && panning ? panHandler : undefined + }, _react.default.createElement("ul", null, _react.default.createElement(_ChartNode.default, { + datasource: attachRel(ds, "00"), + NodeTemplate: NodeTemplate, + draggable: draggable, + collapsible: collapsible, + multipleSelect: multipleSelect, + changeHierarchy: changeHierarchy, + onClickNode: onClickNode + }))), _react.default.createElement("a", { + className: "oc-download-btn hidden", + ref: downloadButton, + href: dataURL, + download: download + }, "\xA0"), _react.default.createElement("div", { + className: "oc-mask ".concat(exporting ? "" : "hidden") + }, _react.default.createElement("i", { + className: "oci oci-spinner spinner" + }))); +}); ChartContainer.propTypes = propTypes; ChartContainer.defaultProps = defaultProps; var _default = ChartContainer; -exports.default = _default; \ No newline at end of file +exports.default = _default; diff --git a/dist/ChartNode.css b/dist/ChartNode.css index 572374b..470fac8 100644 --- a/dist/ChartNode.css +++ b/dist/ChartNode.css @@ -1,13 +1,17 @@ + .orgchart ul { padding-left: 0; text-align: center; display: flex; margin: 0; + transition: transform 0.3s, opacity 0.3s; } + .orgchart ul li { display: inline-block; position: relative; } + /* excluding root node */ .orgchart > ul > li > ul li::before { content: ""; @@ -19,13 +23,19 @@ box-sizing: border-box; } +.orgchart > ul > li > ul li.isSiblingsCollapsed::before { + content: none; +} + .orgchart > ul > li > ul li:first-child::before { left: calc(50% - 1px); width: calc(50% + 1px); } + .orgchart > ul > li > ul li:last-child::before { width: calc(50% + 1px); } + .orgchart > ul > li > ul li:only-child::before { width: 2px; } @@ -34,14 +44,23 @@ display: inline-block; position: relative; padding: 3px; - border: 2px solid transparent; + border: 2px dashed transparent; margin-bottom: 20px; } +.orgchart ul li .oc-node.allowedDrop { + border-color: rgba(68, 157, 68, 0.9); +} +.orgchart ul li .oc-node.currentDropTarget { + background-color: rgba(68, 157, 68, 0.9); +} +.orgchart ul li .oc-node.selected { + background-color: rgba(238, 217, 54, 0.5); +} .orgchart ul li .oc-node:hover { background-color: rgba(238, 217, 54, 0.5); } /* excluding root node */ -.orgchart > ul > li > ul li .oc-node::before { +.orgchart > ul > li > ul li > .oc-node::before { content: ""; position: absolute; top: -11px; @@ -50,6 +69,16 @@ height: 9px; background-color: rgba(217, 83, 79, 0.8); } + +.orgchart > ul > li > ul li.isSiblingsCollapsed > .oc-node::before { + top: -13px; + height: 11px; +} + +.orgchart > ul > li > ul li.isAncestorsCollapsed > .oc-node::before { + content: none; +} + /* excluding leaf node */ .orgchart ul li .oc-node:not(:only-child)::after { content: ""; @@ -60,6 +89,11 @@ height: 9px; background-color: rgba(217, 83, 79, 0.8); } + +.orgchart ul li .oc-node.isChildrenCollapsed:not(:only-child)::after { + content: none; +} + .orgchart ul li .oc-node .oc-heading { box-sizing: border-box; padding: 2px; @@ -76,6 +110,22 @@ color: #fff; border-radius: 4px 4px 0 0; } + +.orgchart ul li .oc-node .oc-heading .oc-symbol { + float: left; + margin-top: 12px; + margin-left: 2px; +} + +.orgchart ul li .oc-node .oc-heading .oc-symbol::before { + background-color: #fff; + border-color: rgba(217, 83, 79, 0.8); +} + +.orgchart ul li .oc-node .oc-heading .oc-symbol::after { + background-color: #fff; +} + .orgchart ul li .oc-node .oc-content { box-sizing: border-box; padding: 2px; @@ -90,3 +140,313 @@ text-overflow: ellipsis; white-space: nowrap; } + +/* styles for edges */ +.orgchart .oc-node .oc-edge { + position: absolute; + cursor: default; + transition: .2s; +} + +.orgchart .oc-node .oc-edge::before { + border-color: rgba(68, 157, 68, 0.5); +} + +.orgchart.noncollapsable .oc-node .oc-edge { + display: none; +} + +.orgchart .oc-node .oc-edge:hover { + cursor: pointer; +} + +.orgchart .oc-edge:hover::before { + border-color: #449d44; +} + +.orgchart .oc-node .verticalEdge { + width: calc(100% - 6px); + width: -webkit-calc(100% - 6px); + width: -moz-calc(100% - 6px); + height: 10px; + left: 3px; +} + +.orgchart .oc-node .verticalEdge::before { + position: absolute; + left: calc(50% - 0.3125rem); +} + +.orgchart .oc-node .topEdge { + top: -2px; +} + +.orgchart .oc-node .topEdge.oci-chevron-up::before { + top: 2px; +} + +.orgchart .oc-node .topEdge.oci-chevron-down::before { + bottom: 3px; +} + +.orgchart .oc-node .bottomEdge { + bottom: -2px; +} + +.orgchart .oc-node .bottomEdge.oci-chevron-up::before { + bottom: -3px; +} + +.orgchart .oc-node .bottomEdge.oci-chevron-down::before { + bottom: 1px; +} + +.orgchart .oc-node .horizontalEdge { + width: 10px; + height: calc(100% - 6px); + height: -webkit-calc(100% - 6px); + height: -moz-calc(100% - 6px); + top: 3px; +} + +.orgchart .oc-node .rightEdge { + right: -2px; +} + +.orgchart .oc-node .leftEdge { + left: -2px; +} + +.orgchart .oc-node .horizontalEdge::before { + position: absolute; + top: calc(50% - 0.3125rem); +} + +.orgchart .oc-node .rightEdge.oci-chevron-left::before { + right: -3px; +} + +.orgchart .oc-node .rightEdge.oci-chevron-right::before { + right: 1px; +} + +.orgchart .oc-node .leftEdge.oci-chevron-right::before { + left: -3px; +} + +.orgchart .oc-node .leftEdge.oci-chevron-left::before { + left: 1px; +} + +/* slide animations */ +.orgchart .slide-up { + opacity: 0; + transform: translateY(-40px); +} + +.orgchart .slide-down { + opacity: 0; + transform: translateY(40px); +} + +.orgchart .slide-left { + opacity: 0; + transform: translateX(-130px); +} + +.orgchart .slide-right { + opacity: 0; + transform: translateX(130px); +} + +/* oci means organization chart icons */ +.oci { + display: inline-block; + position: relative; + font-style: normal; + font-family: Arial; +} + +.oci-chevron-up::before { + content: ""; + display: inline-block; + box-sizing: border-box; + vertical-align: text-bottom; + width: 0.625rem; + height: 0.625rem; + border-width: 0 0.2rem 0.2rem 0; + border-style: solid; + border-color: #000; + background: transparent; + transform: rotate(-135deg); +} + +.oci-chevron-right::before { + content: ""; + display: inline-block; + box-sizing: border-box; + width: 0.625rem; + height: 0.625rem; + border-width: 0 0.2rem 0.2rem 0; + border-style: solid; + border-color: #000; + background: transparent; + transform: rotate(-45deg); +} + +.oci-chevron-down::before { + content: ""; + display: inline-block; + box-sizing: border-box; + width: 0.625rem; + height: 0.625rem; + border-width: 0 0.2rem 0.2rem 0; + border-style: solid; + border-color: #000; + background: transparent; + transform: rotate(45deg); +} + +.oci-chevron-left::before { + content: ""; + display: inline-block; + box-sizing: border-box; + width: 0.625rem; + height: 0.625rem; + border-width: 0 0.2rem 0.2rem 0; + border-style: solid; + border-color: #000; + background: transparent; + transform: rotate(135deg); +} + +.oci-leader::before { + position: absolute; + content: ""; + display: inline-block; + width: 0.4rem; + height: 0.4rem; + border-radius: 0.2rem; + background: #000; + top: -0.75rem; + left: 0.125rem; +} + +.oci-leader::after { + position: absolute; + content: ""; + display: inline-block; + width: 0.875rem; + height: 0.375rem; + border-radius: 0.25rem 0.25rem 0 0; + background: #000; + top: -0.3rem; + left: -0.125rem; +} + +.oci-plus-square::before { + content: "+"; + display: inline-block; + vertical-align: text-bottom; + text-align: center; + width: 1rem; + height: 1rem; + background-color: #000; + color: #fff; +} + +.oci-plus-circle::before { + content: "+"; + display: inline-block; + vertical-align: text-bottom; + text-align: center; + width: 1rem; + height: 1rem; + border-radius: 0.5rem; + background-color: #000; + color: #fff; +} + +.oci-minus-square::before { + content: "−"; + display: inline-block; + vertical-align: text-bottom; + text-align: center; + width: 1rem; + height: 1rem; + background-color: #000; + color: #fff; +} + +.oci-minus-circle::before { + content: "−"; + display: inline-block; + vertical-align: text-bottom; + text-align: center; + width: 1rem; + height: 1rem; + border-radius: 0.5rem; + background-color: #000; + color: #fff; +} + +.oci-arrow-circle-up::before { + content: "▲"; + display: inline-block; + text-align: center; + vertical-align: text-bottom; + width: 1rem; + height: 1rem; + border-radius: 0.5rem; + background-color: #000; + color: #fff; + font-size: 0.875rem; +} + +.oci-arrow-circle-down::before { + content: "▼"; + text-align: center; + display: inline-block; + vertical-align: text-bottom; + width: 1rem; + height: 1rem; + border-radius: 0.5rem; + background-color: #000; + color: #fff; + font-size: 0.875rem; +} + +.oci-info-circle::before { + content: "i"; + display: inline-block; + vertical-align: text-bottom; + width: 1rem; + height: 1rem; + border-radius: 0.5rem; + background-color: #000; + color: #fff; + text-align: center; + font-weight: bold; +} + +.oci-spinner::before { + content: ""; + vertical-align: text-bottom; + display: inline-block; + box-sizing: border-box; + width: 1rem; + height: 1rem; + border: 0.1rem solid #000; + border-right-color: transparent; + border-radius: 0.625rem; + animation: oci-infinite-spinning .75s linear infinite; +} + +@keyframes oci-infinite-spinning { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/dist/ChartNode.js b/dist/ChartNode.js index d9c7ec3..0405789 100644 --- a/dist/ChartNode.js +++ b/dist/ChartNode.js @@ -1,81 +1,338 @@ "use strict"; +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; -var _react = _interopRequireDefault(require("react")); +var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); -require("./ChartNode.css"); +var _service = require("./service"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } +function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var propTypes = { - datasource: _propTypes.default.object.isRequired, - nodeTemplate: _propTypes.default.elementType + datasource: _propTypes.default.object, + NodeTemplate: _propTypes.default.elementType, + draggable: _propTypes.default.bool, + collapsible: _propTypes.default.bool, + multipleSelect: _propTypes.default.bool, + changeHierarchy: _propTypes.default.func, + onClickNode: _propTypes.default.func }; +var defaultProps = { + draggable: false, + collapsible: true, + multipleSelect: false +}; + +var ChartNode = function ChartNode(_ref) { + var datasource = _ref.datasource, + NodeTemplate = _ref.NodeTemplate, + draggable = _ref.draggable, + collapsible = _ref.collapsible, + multipleSelect = _ref.multipleSelect, + changeHierarchy = _ref.changeHierarchy, + onClickNode = _ref.onClickNode; + var node = (0, _react.useRef)(); + + var _useState = (0, _react.useState)(false), + _useState2 = _slicedToArray(_useState, 2), + isChildrenCollapsed = _useState2[0], + setIsChildrenCollapsed = _useState2[1]; + + var _useState3 = (0, _react.useState)(), + _useState4 = _slicedToArray(_useState3, 2), + topEdgeExpanded = _useState4[0], + setTopEdgeExpanded = _useState4[1]; + + var _useState5 = (0, _react.useState)(), + _useState6 = _slicedToArray(_useState5, 2), + rightEdgeExpanded = _useState6[0], + setRightEdgeExpanded = _useState6[1]; + + var _useState7 = (0, _react.useState)(), + _useState8 = _slicedToArray(_useState7, 2), + bottomEdgeExpanded = _useState8[0], + setBottomEdgeExpanded = _useState8[1]; + + var _useState9 = (0, _react.useState)(), + _useState10 = _slicedToArray(_useState9, 2), + leftEdgeExpanded = _useState10[0], + setLeftEdgeExpanded = _useState10[1]; -var ChartNode = -/*#__PURE__*/ -function (_React$Component) { - _inherits(ChartNode, _React$Component); - - function ChartNode() { - _classCallCheck(this, ChartNode); - - return _possibleConstructorReturn(this, _getPrototypeOf(ChartNode).apply(this, arguments)); - } - - _createClass(ChartNode, [{ - key: "render", - value: function render() { - var _this = this; - - var datasource = this.props.datasource; - return _react.default.createElement("li", null, this.props.nodeTemplate ? _react.default.createElement("div", { - className: "oc-node" - }, _react.default.createElement(this.props.nodeTemplate, { - nodeData: datasource - })) : _react.default.createElement("div", { - className: "oc-node" - }, _react.default.createElement("div", { - className: "oc-heading" - }, datasource.name), _react.default.createElement("div", { - className: "oc-content" - }, datasource.title)), datasource.children && _react.default.createElement("ul", null, datasource.children.map(function (node) { - return _react.default.createElement(ChartNode, { - datasource: node, - nodeTemplate: _this.props.nodeTemplate, - key: node.id - }); - }))); + var _useState11 = (0, _react.useState)(false), + _useState12 = _slicedToArray(_useState11, 2), + allowedDrop = _useState12[0], + setAllowedDrop = _useState12[1]; + + var _useState13 = (0, _react.useState)(false), + _useState14 = _slicedToArray(_useState13, 2), + selected = _useState14[0], + setSelected = _useState14[1]; + + var nodeClass = ["oc-node", isChildrenCollapsed ? "isChildrenCollapsed" : "", allowedDrop ? "allowedDrop" : "", selected ? "selected" : ""].filter(function (item) { + return item; + }).join(" "); + (0, _react.useEffect)(function () { + var subs1 = _service.dragNodeService.getDragInfo().subscribe(function (draggedInfo) { + if (draggedInfo) { + setAllowedDrop(!document.querySelector("#" + draggedInfo.draggedNodeId).closest("li").querySelector("#" + node.current.id) ? true : false); + } else { + setAllowedDrop(false); + } + }); + + var subs2 = _service.selectNodeService.getSelectedNodeInfo().subscribe(function (selectedNodeInfo) { + if (selectedNodeInfo) { + if (multipleSelect) { + if (selectedNodeInfo.selectedNodeId === datasource.id) { + setSelected(true); + } + } else { + setSelected(selectedNodeInfo.selectedNodeId === datasource.id); + } + } else { + setSelected(false); + } + }); + + return function () { + subs1.unsubscribe(); + subs2.unsubscribe(); + }; + }, [multipleSelect, datasource]); + + var addArrows = function addArrows(e) { + var node = e.target.closest("li"); + var parent = node.parentNode.closest("li"); + var isAncestorsCollapsed = node && parent ? parent.firstChild.classList.contains("hidden") : undefined; + var isSiblingsCollapsed = Array.from(node.parentNode.children).some(function (item) { + return item.classList.contains("hidden"); + }); + setTopEdgeExpanded(!isAncestorsCollapsed); + setRightEdgeExpanded(!isSiblingsCollapsed); + setLeftEdgeExpanded(!isSiblingsCollapsed); + setBottomEdgeExpanded(!isChildrenCollapsed); + }; + + var removeArrows = function removeArrows() { + setTopEdgeExpanded(undefined); + setRightEdgeExpanded(undefined); + setBottomEdgeExpanded(undefined); + setLeftEdgeExpanded(undefined); + }; + + var toggleAncestors = function toggleAncestors(actionNode) { + var node = actionNode.parentNode.closest("li"); + if (!node) return; + var isAncestorsCollapsed = node.firstChild.classList.contains("hidden"); + + if (isAncestorsCollapsed) { + // 向上展开,只展开一级 + actionNode.classList.remove("isAncestorsCollapsed"); + node.firstChild.classList.remove("hidden"); + } else { + var _actionNode$classList; + + // 向下折叠,则折叠所有祖先节点以及祖先节点的兄弟节点 + var isSiblingsCollapsed = Array.from(actionNode.parentNode.children).some(function (item) { + return item.classList.contains("hidden"); + }); + + if (!isSiblingsCollapsed) { + toggleSiblings(actionNode); + } + + (_actionNode$classList = actionNode.classList).add.apply(_actionNode$classList, _toConsumableArray(("isAncestorsCollapsed" + (isSiblingsCollapsed ? "" : " isSiblingsCollapsed")).split(" "))); + + node.firstChild.classList.add("hidden"); // 如果还有展开的祖先节点,那继续折叠关闭之 + + if (node.parentNode.closest("li") && !node.parentNode.closest("li").firstChild.classList.contains("hidden")) { + toggleAncestors(node); + } } - }]); + }; + + var topEdgeClickHandler = function topEdgeClickHandler(e) { + e.stopPropagation(); + setTopEdgeExpanded(!topEdgeExpanded); + toggleAncestors(e.target.closest("li")); + }; - return ChartNode; -}(_react.default.Component); + var bottomEdgeClickHandler = function bottomEdgeClickHandler(e) { + e.stopPropagation(); + setIsChildrenCollapsed(!isChildrenCollapsed); + setBottomEdgeExpanded(!bottomEdgeExpanded); + }; + + var toggleSiblings = function toggleSiblings(actionNode) { + var node = actionNode.previousSibling; + var isSiblingsCollapsed = Array.from(actionNode.parentNode.children).some(function (item) { + return item.classList.contains("hidden"); + }); + actionNode.classList.toggle("isSiblingsCollapsed", !isSiblingsCollapsed); // 先处理同级的兄弟节点 + + while (node) { + if (isSiblingsCollapsed) { + node.classList.remove("hidden"); + } else { + node.classList.add("hidden"); + } + + node = node.previousSibling; + } + + node = actionNode.nextSibling; + + while (node) { + if (isSiblingsCollapsed) { + node.classList.remove("hidden"); + } else { + node.classList.add("hidden"); + } + + node = node.nextSibling; + } // 在展开兄弟节点的同时,还要展开父节点 + + + var isAncestorsCollapsed = actionNode.parentNode.closest("li").firstChild.classList.contains("hidden"); + + if (isAncestorsCollapsed) { + toggleAncestors(actionNode); + } + }; + + var hEdgeClickHandler = function hEdgeClickHandler(e) { + e.stopPropagation(); + setLeftEdgeExpanded(!leftEdgeExpanded); + setRightEdgeExpanded(!rightEdgeExpanded); + toggleSiblings(e.target.closest("li")); + }; + + var filterAllowedDropNodes = function filterAllowedDropNodes(id) { + _service.dragNodeService.sendDragInfo(id); + }; + + var clickNodeHandler = function clickNodeHandler(event) { + if (onClickNode) { + onClickNode(datasource); + } + + _service.selectNodeService.sendSelectedNodeInfo(datasource.id); + }; + + var dragstartHandler = function dragstartHandler(event) { + var copyDS = _objectSpread({}, datasource); + + delete copyDS.relationship; + event.dataTransfer.setData("text/plain", JSON.stringify(copyDS)); // highlight all potential drop targets + + filterAllowedDropNodes(node.current.id); + }; + + var dragoverHandler = function dragoverHandler(event) { + // prevent default to allow drop + event.preventDefault(); + }; + + var dragendHandler = function dragendHandler() { + // reset background of all potential drop targets + _service.dragNodeService.clearDragInfo(); + }; + + var dropHandler = function dropHandler(event) { + if (!event.currentTarget.classList.contains("allowedDrop")) { + return; + } + + _service.dragNodeService.clearDragInfo(); + + changeHierarchy(JSON.parse(event.dataTransfer.getData("text/plain")), event.currentTarget.id); + }; + + return _react.default.createElement("li", { + className: "oc-hierarchy" + }, _react.default.createElement("div", { + ref: node, + id: datasource.id, + className: nodeClass, + draggable: draggable ? "true" : undefined, + onClick: clickNodeHandler, + onDragStart: dragstartHandler, + onDragOver: dragoverHandler, + onDragEnd: dragendHandler, + onDrop: dropHandler, + onMouseEnter: addArrows, + onMouseLeave: removeArrows + }, NodeTemplate ? _react.default.createElement(NodeTemplate, { + nodeData: datasource + }) : _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("div", { + className: "oc-heading" + }, datasource.relationship && datasource.relationship.charAt(2) === "1" && _react.default.createElement("i", { + className: "oci oci-leader oc-symbol" + }), datasource.name), _react.default.createElement("div", { + className: "oc-content" + }, datasource.title)), collapsible && datasource.relationship && datasource.relationship.charAt(0) === "1" && _react.default.createElement("i", { + className: "oc-edge verticalEdge topEdge oci ".concat(topEdgeExpanded === undefined ? "" : topEdgeExpanded ? "oci-chevron-down" : "oci-chevron-up"), + onClick: topEdgeClickHandler + }), collapsible && datasource.relationship && datasource.relationship.charAt(1) === "1" && _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("i", { + className: "oc-edge horizontalEdge rightEdge oci ".concat(rightEdgeExpanded === undefined ? "" : rightEdgeExpanded ? "oci-chevron-left" : "oci-chevron-right"), + onClick: hEdgeClickHandler + }), _react.default.createElement("i", { + className: "oc-edge horizontalEdge leftEdge oci ".concat(leftEdgeExpanded === undefined ? "" : leftEdgeExpanded ? "oci-chevron-right" : "oci-chevron-left"), + onClick: hEdgeClickHandler + })), collapsible && datasource.relationship && datasource.relationship.charAt(2) === "1" && _react.default.createElement("i", { + className: "oc-edge verticalEdge bottomEdge oci ".concat(bottomEdgeExpanded === undefined ? "" : bottomEdgeExpanded ? "oci-chevron-up" : "oci-chevron-down"), + onClick: bottomEdgeClickHandler + })), datasource.children && datasource.children.length > 0 && _react.default.createElement("ul", { + className: isChildrenCollapsed ? "hidden" : "" + }, datasource.children.map(function (node) { + return _react.default.createElement(ChartNode, { + datasource: node, + NodeTemplate: NodeTemplate, + id: node.id, + key: node.id, + draggable: draggable, + collapsible: collapsible, + multipleSelect: multipleSelect, + changeHierarchy: changeHierarchy, + onClickNode: onClickNode + }); + }))); +}; ChartNode.propTypes = propTypes; +ChartNode.defaultProps = defaultProps; var _default = ChartNode; exports.default = _default; \ No newline at end of file diff --git a/dist/service.js b/dist/service.js new file mode 100644 index 0000000..1f3c98d --- /dev/null +++ b/dist/service.js @@ -0,0 +1,39 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.selectNodeService = exports.dragNodeService = void 0; + +var _rxjs = require("rxjs"); + +var subject1 = new _rxjs.Subject(); +var subject2 = new _rxjs.Subject(); +var dragNodeService = { + sendDragInfo: function sendDragInfo(id) { + return subject1.next({ + draggedNodeId: id + }); + }, + clearDragInfo: function clearDragInfo() { + return subject1.next(); + }, + getDragInfo: function getDragInfo() { + return subject1.asObservable(); + } +}; +exports.dragNodeService = dragNodeService; +var selectNodeService = { + sendSelectedNodeInfo: function sendSelectedNodeInfo(id) { + return subject2.next({ + selectedNodeId: id + }); + }, + clearSelectedNodeInfo: function clearSelectedNodeInfo() { + return subject2.next(); + }, + getSelectedNodeInfo: function getSelectedNodeInfo() { + return subject2.asObservable(); + } +}; +exports.selectNodeService = selectNodeService; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 19e3334..5652ae8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "@dabeng/react-orgchart", - "version": "0.0.8", + "name": "@dabeng/nextjs-orgchart", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d9a5e47..0a78117 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@dabeng/react-orgchart", - "version": "1.0.0", + "name": "nextjs-orgchart-fix", + "version": "1.0.8", "main": "./dist/ChartContainer.js", "files": [ "dist", @@ -8,7 +8,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/dabeng/react-orgchart.git" + "url": "https://github.com/mahyarmadad/nextjs-orgchart" }, "keywords": [ "react", @@ -20,12 +20,12 @@ "tree-like", "tree-view" ], - "author": "dabeng ", + "author": "dabeng && mahyarmadad", "license": "MIT", "bugs": { - "url": "https://github.com/dabeng/react-orgchart/issues" + "url": "https://github.com/mahyarmadad/nextjs-orgchart/issues" }, - "homepage": "https://github.com/dabeng/react-orgchart#readme", + "homepage": "https://github.com/mahyarmadad/nextjs-orgchart#readme", "babel": { "presets": [ "@babel/preset-env", @@ -59,7 +59,7 @@ "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", - "publish:npm": "rm -rf dist && mkdir dist && babel src/components -d dist --copy-files" + "publish:npm": "mkdir dist && babel src/components -d dist --copy-files" }, "eslintConfig": { "extends": "react-app" diff --git a/src/components/ChartContainer.js b/src/components/ChartContainer.js index 44d8947..a0ec7d3 100644 --- a/src/components/ChartContainer.js +++ b/src/components/ChartContainer.js @@ -3,15 +3,12 @@ import React, { useEffect, useRef, forwardRef, - useImperativeHandle + useImperativeHandle, } from "react"; import PropTypes from "prop-types"; import { selectNodeService } from "./service"; import JSONDigger from "json-digger"; -import html2canvas from "html2canvas"; -import jsPDF from "jspdf"; import ChartNode from "./ChartNode"; -import "./ChartContainer.css"; const propTypes = { datasource: PropTypes.object.isRequired, @@ -26,7 +23,7 @@ const propTypes = { collapsible: PropTypes.bool, multipleSelect: PropTypes.bool, onClickNode: PropTypes.func, - onClickChart: PropTypes.func + onClickChart: PropTypes.func, }; const defaultProps = { @@ -38,7 +35,7 @@ const defaultProps = { chartClass: "", draggable: false, collapsible: true, - multipleSelect: false + multipleSelect: false, }; const ChartContainer = forwardRef( @@ -56,7 +53,7 @@ const ChartContainer = forwardRef( collapsible, multipleSelect, onClickNode, - onClickChart + onClickChart, }, ref ) => { @@ -77,7 +74,7 @@ const ChartContainer = forwardRef( data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0); if (data.children) { - data.children.forEach(function(item) { + data.children.forEach(function (item) { attachRel(item, "1" + (data.children.length > 1 ? 1 : 0)); }); } @@ -91,7 +88,7 @@ const ChartContainer = forwardRef( const dsDigger = new JSONDigger(datasource, "id", "children"); - const clickChartHandler = event => { + const clickChartHandler = (event) => { if (!event.target.closest(".oc-node")) { if (onClickChart) { onClickChart(); @@ -105,7 +102,7 @@ const ChartContainer = forwardRef( setCursor("default"); }; - const panHandler = e => { + const panHandler = (e) => { let newX = 0; let newY = 0; if (!e.targetTouches) { @@ -140,7 +137,7 @@ const ChartContainer = forwardRef( } }; - const panStartHandler = e => { + const panStartHandler = (e) => { if (e.target.closest(".oc-node")) { setPanning(false); return; @@ -173,7 +170,7 @@ const ChartContainer = forwardRef( } }; - const updateChartScale = newScale => { + const updateChartScale = (newScale) => { let matrix = []; let targetScale = 1; if (transform === "") { @@ -198,28 +195,30 @@ const ChartContainer = forwardRef( } }; - const zoomHandler = e => { + const zoomHandler = (e) => { let newScale = 1 + (e.deltaY > 0 ? -0.2 : 0.2); updateChartScale(newScale); }; const exportPDF = (canvas, exportFilename) => { - const canvasWidth = Math.floor(canvas.width); - const canvasHeight = Math.floor(canvas.height); - const doc = - canvasWidth > canvasHeight - ? new jsPDF({ - orientation: "landscape", - unit: "px", - format: [canvasWidth, canvasHeight] - }) - : new jsPDF({ - orientation: "portrait", - unit: "px", - format: [canvasHeight, canvasWidth] - }); - doc.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", 0, 0); - doc.save(exportFilename + ".pdf"); + import("jspdf").then((jsPDF) => { + const canvasWidth = Math.floor(canvas.width); + const canvasHeight = Math.floor(canvas.height); + const doc = + canvasWidth > canvasHeight + ? new jsPDF({ + orientation: "landscape", + unit: "px", + format: [canvasWidth, canvasHeight], + }) + : new jsPDF({ + orientation: "portrait", + unit: "px", + format: [canvasHeight, canvasWidth], + }); + doc.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", 0, 0); + doc.save(exportFilename + ".pdf"); + }); }; const exportPNG = (canvas, exportFilename) => { @@ -239,10 +238,13 @@ const ChartContainer = forwardRef( } }; - const changeHierarchy = async (draggedItemData, dropTargetId) => { - await dsDigger.removeNode(draggedItemData.id); - await dsDigger.addChildren(dropTargetId, draggedItemData); - setDS({ ...dsDigger.ds }); + const changeHierarchy = (draggedItemData, dropTargetId) => { + return dsDigger + .removeNode(draggedItemData.id) + .then(() => dsDigger.addChildren(dropTargetId, draggedItemData)) + .then(() => { + setDS({ ...dsDigger.ds }); + }); }; useImperativeHandle(ref, () => ({ @@ -254,44 +256,46 @@ const ChartContainer = forwardRef( container.current.scrollLeft = 0; const originalScrollTop = container.current.scrollTop; container.current.scrollTop = 0; - html2canvas(chart.current, { - width: chart.current.clientWidth, - height: chart.current.clientHeight, - onclone: function(clonedDoc) { - clonedDoc.querySelector(".orgchart").style.background = "none"; - clonedDoc.querySelector(".orgchart").style.transform = ""; - } - }).then( - canvas => { - if (exportFileextension.toLowerCase() === "pdf") { - exportPDF(canvas, exportFilename); - } else { - exportPNG(canvas, exportFilename); + import("html2canvas").then((html2canvas) => { + html2canvas(chart.current, { + width: chart.current.clientWidth, + height: chart.current.clientHeight, + onclone: function (clonedDoc) { + clonedDoc.querySelector(".orgchart").style.background = "none"; + clonedDoc.querySelector(".orgchart").style.transform = ""; + }, + }).then( + (canvas) => { + if (exportFileextension.toLowerCase() === "pdf") { + exportPDF(canvas, exportFilename); + } else { + exportPNG(canvas, exportFilename); + } + setExporting(false); + container.current.scrollLeft = originalScrollLeft; + container.current.scrollTop = originalScrollTop; + }, + () => { + setExporting(false); + container.current.scrollLeft = originalScrollLeft; + container.current.scrollTop = originalScrollTop; } - setExporting(false); - container.current.scrollLeft = originalScrollLeft; - container.current.scrollTop = originalScrollTop; - }, - () => { - setExporting(false); - container.current.scrollLeft = originalScrollLeft; - container.current.scrollTop = originalScrollTop; - } - ); + ); + }); }, expandAllNodes: () => { chart.current .querySelectorAll( ".oc-node.hidden, .oc-hierarchy.hidden, .isSiblingsCollapsed, .isAncestorsCollapsed" ) - .forEach(el => { + .forEach((el) => { el.classList.remove( "hidden", "isSiblingsCollapsed", "isAncestorsCollapsed" ); }); - } + }, })); return ( diff --git a/src/components/ChartNode.js b/src/components/ChartNode.js index a2f1a47..62948c4 100644 --- a/src/components/ChartNode.js +++ b/src/components/ChartNode.js @@ -1,7 +1,6 @@ import React, { useState, useRef, useEffect } from "react"; import PropTypes from "prop-types"; import { dragNodeService, selectNodeService } from "./service"; -import "./ChartNode.css"; const propTypes = { datasource: PropTypes.object, @@ -10,13 +9,13 @@ const propTypes = { collapsible: PropTypes.bool, multipleSelect: PropTypes.bool, changeHierarchy: PropTypes.func, - onClickNode: PropTypes.func + onClickNode: PropTypes.func, }; const defaultProps = { draggable: false, collapsible: true, - multipleSelect: false + multipleSelect: false, }; const ChartNode = ({ @@ -26,7 +25,7 @@ const ChartNode = ({ collapsible, multipleSelect, changeHierarchy, - onClickNode + onClickNode, }) => { const node = useRef(); @@ -42,13 +41,13 @@ const ChartNode = ({ "oc-node", isChildrenCollapsed ? "isChildrenCollapsed" : "", allowedDrop ? "allowedDrop" : "", - selected ? "selected" : "" + selected ? "selected" : "", ] - .filter(item => item) + .filter((item) => item) .join(" "); useEffect(() => { - const subs1 = dragNodeService.getDragInfo().subscribe(draggedInfo => { + const subs1 = dragNodeService.getDragInfo().subscribe((draggedInfo) => { if (draggedInfo) { setAllowedDrop( !document @@ -65,7 +64,7 @@ const ChartNode = ({ const subs2 = selectNodeService .getSelectedNodeInfo() - .subscribe(selectedNodeInfo => { + .subscribe((selectedNodeInfo) => { if (selectedNodeInfo) { if (multipleSelect) { if (selectedNodeInfo.selectedNodeId === datasource.id) { @@ -85,7 +84,7 @@ const ChartNode = ({ }; }, [multipleSelect, datasource]); - const addArrows = e => { + const addArrows = (e) => { const node = e.target.closest("li"); const parent = node.parentNode.closest("li"); const isAncestorsCollapsed = @@ -94,7 +93,7 @@ const ChartNode = ({ : undefined; const isSiblingsCollapsed = Array.from( node.parentNode.children - ).some(item => item.classList.contains("hidden")); + ).some((item) => item.classList.contains("hidden")); setTopEdgeExpanded(!isAncestorsCollapsed); setRightEdgeExpanded(!isSiblingsCollapsed); @@ -109,7 +108,7 @@ const ChartNode = ({ setLeftEdgeExpanded(undefined); }; - const toggleAncestors = actionNode => { + const toggleAncestors = (actionNode) => { let node = actionNode.parentNode.closest("li"); if (!node) return; const isAncestorsCollapsed = node.firstChild.classList.contains("hidden"); @@ -121,7 +120,7 @@ const ChartNode = ({ // 向下折叠,则折叠所有祖先节点以及祖先节点的兄弟节点 const isSiblingsCollapsed = Array.from( actionNode.parentNode.children - ).some(item => item.classList.contains("hidden")); + ).some((item) => item.classList.contains("hidden")); if (!isSiblingsCollapsed) { toggleSiblings(actionNode); } @@ -142,23 +141,23 @@ const ChartNode = ({ } }; - const topEdgeClickHandler = e => { + const topEdgeClickHandler = (e) => { e.stopPropagation(); setTopEdgeExpanded(!topEdgeExpanded); toggleAncestors(e.target.closest("li")); }; - const bottomEdgeClickHandler = e => { + const bottomEdgeClickHandler = (e) => { e.stopPropagation(); setIsChildrenCollapsed(!isChildrenCollapsed); setBottomEdgeExpanded(!bottomEdgeExpanded); }; - const toggleSiblings = actionNode => { + const toggleSiblings = (actionNode) => { let node = actionNode.previousSibling; const isSiblingsCollapsed = Array.from( actionNode.parentNode.children - ).some(item => item.classList.contains("hidden")); + ).some((item) => item.classList.contains("hidden")); actionNode.classList.toggle("isSiblingsCollapsed", !isSiblingsCollapsed); // 先处理同级的兄弟节点 while (node) { @@ -187,18 +186,18 @@ const ChartNode = ({ } }; - const hEdgeClickHandler = e => { + const hEdgeClickHandler = (e) => { e.stopPropagation(); setLeftEdgeExpanded(!leftEdgeExpanded); setRightEdgeExpanded(!rightEdgeExpanded); toggleSiblings(e.target.closest("li")); }; - const filterAllowedDropNodes = id => { + const filterAllowedDropNodes = (id) => { dragNodeService.sendDragInfo(id); }; - const clickNodeHandler = event => { + const clickNodeHandler = (event) => { if (onClickNode) { onClickNode(datasource); } @@ -206,7 +205,7 @@ const ChartNode = ({ selectNodeService.sendSelectedNodeInfo(datasource.id); }; - const dragstartHandler = event => { + const dragstartHandler = (event) => { const copyDS = { ...datasource }; delete copyDS.relationship; event.dataTransfer.setData("text/plain", JSON.stringify(copyDS)); @@ -214,7 +213,7 @@ const ChartNode = ({ filterAllowedDropNodes(node.current.id); }; - const dragoverHandler = event => { + const dragoverHandler = (event) => { // prevent default to allow drop event.preventDefault(); }; @@ -224,7 +223,7 @@ const ChartNode = ({ dragNodeService.clearDragInfo(); }; - const dropHandler = event => { + const dropHandler = (event) => { if (!event.currentTarget.classList.contains("allowedDrop")) { return; } @@ -321,7 +320,7 @@ const ChartNode = ({ {datasource.children && datasource.children.length > 0 && (