├── license.pdf ├── public ├── favicon.ico ├── photos │ ├── a.png │ ├── b.png │ ├── c.png │ ├── d.png │ ├── e.png │ ├── f.png │ ├── g.png │ ├── h.png │ ├── i.png │ ├── j.png │ ├── k.png │ ├── l.png │ ├── m.png │ ├── n.png │ ├── o.png │ ├── p.png │ ├── q.png │ ├── r.png │ ├── s.png │ ├── t.png │ ├── u.png │ ├── v.png │ ├── w.png │ ├── x.png │ ├── y.png │ └── z.png └── manifest.json ├── docs ├── images │ ├── cbp88.png │ ├── cbp88bundled.png │ ├── cbp88matrixed.png │ ├── PageSizeDiagram1.png │ ├── PageSizeDiagram2.png │ ├── PageSizeDiagram3.png │ ├── cbp88everyparent.png │ └── sparse_data_problem.pdf ├── LoopsInFamily.md ├── ZoomWithCSSScaleTransform.md ├── CrossBranchAlignment.md ├── MatrixLayoutInFamilyChart.md ├── SelectionPathModeInFamilyChart.md ├── Labels.md ├── LabelsCascadesInFamilyChart.md ├── MultipleRootItemsInChart.md ├── LevelAnnotation.md ├── ShapeAndBackgroundAnnotations.md ├── ItemAndGroupTitleColors.md ├── SelectionPathMode.md ├── ZoomWithItemTemplate.md ├── HighlightTemplate.md ├── SelectedItems.md ├── FamilyChartItemsOrdering.md ├── InactiveItems.md ├── InactiveFamilyItems.md ├── HighlightPathAnnotation.md ├── CustomLayoutWithInvisibleItems.md ├── PartnerItemTypes.md ├── readme.md ├── AddingNewItemsToChartAtRuntime.md ├── DragNDrop.md ├── AdviserAndAssistantItemTypes.md ├── Buttons.md ├── ChildrenLayout.md ├── ConnectorAnnotation.md ├── CursorTemplate.md ├── SelectingHighlightItem.md ├── FamilyConnectorsVisualization.md └── FirstOrganizationalChart.md ├── src ├── Diagrams │ ├── Templates │ │ ├── AbstractTemplate.js │ │ ├── UserTemplate.js │ │ ├── CustomRenderTemplate.js │ │ ├── AnnotationLabelTemplate.js │ │ ├── ButtonsTemplate.js │ │ ├── LabelAnnotationTemplate.js │ │ ├── index.js │ │ ├── CheckBoxTemplate.jsx │ │ ├── LevelBackgroundTemplate.jsx │ │ ├── HighlightTemplate.jsx │ │ ├── EndPointTemplate.jsx │ │ ├── CursorTemplate.jsx │ │ ├── DotHighlightTemplate.jsx │ │ ├── GroupTitleTemplate.jsx │ │ ├── RotatedText.jsx │ │ └── LevelTitleTemplate.jsx │ ├── Schemas │ │ ├── PaletteItemConfigShape.js │ │ ├── LabelAnnotationConfigShape.js │ │ ├── HighlightPathAnnotationConfigShape.js │ │ ├── BackgroundAnnotationConfigShape.js │ │ ├── LevelAnnotationConfigShape.js │ │ ├── CalloutAnnotationComponentConfigShape.js │ │ ├── ShapeAnnotationConfigShape.js │ │ ├── RotatedTextComponentConfigShape.js │ │ ├── ConnectorAnnotationConfigShape.js │ │ ├── ConnectorAnnotationComponentConfigShape.js │ │ ├── TemplateConfigShape.js │ │ ├── FamItemConfigShape.js │ │ ├── OrgItemConfigShape.js │ │ └── ShapeAnnotationComponentConfigShape.js │ ├── FamDiagram.js │ ├── OrgDiagram.js │ └── index.js ├── index.jsx ├── App.test.js ├── index.css ├── Samples │ ├── FirstOrganizationalChart.jsx │ ├── ItemAndGroupTitleColors.jsx │ ├── AdviserAndAssistantItemTypes.jsx │ ├── SubAdviserAndSubAssistantItemTypes.jsx │ ├── SkippedLevels.jsx │ ├── LimitedPartnerItemType.jsx │ ├── HighlightPathAnnotation.jsx │ ├── ButtonsPanel.jsx │ ├── FirstFamilyChart.jsx │ ├── CursorTemplate.jsx │ ├── HighlightTemplate.jsx │ ├── AdviserPartnerItemType.jsx │ ├── GeneralPartnerItemType.jsx │ ├── MatrixGroupsInFamilyChart.jsx │ ├── SelectingCursorItem.jsx │ ├── SelectingHighlightItem.jsx │ ├── SelectionPathMode.jsx │ ├── SelectionPathModeInFamilyChart.jsx │ ├── LevelAnnotation.jsx │ ├── MatrixLayoutOfMultipleRootItemsInChart.jsx │ ├── MultipleRootItemsInChart.jsx │ ├── MatrixLayoutInFamilyChart.jsx │ ├── ChildrenPlacementType.jsx │ ├── FamilyChartPrimaryParent.jsx │ ├── LabelsCascadesInFamilyChart.jsx │ ├── LoopsInFamilyChart.jsx │ ├── FamilyChartItemsOrdering.jsx │ ├── GroupTitleTemplate.jsx │ ├── SelectedItems.jsx │ ├── FamilyHideGrandParentsConnections.jsx │ ├── PlaceAdvisersAboveChildren.jsx │ ├── PlaceAssistantsAboveChildren.jsx │ ├── ItemTemplateLabel.jsx │ ├── ShapeAnnotation.jsx │ └── MultipleFamiliesOrdering.jsx └── App.jsx ├── .npmignore ├── vite.config.js ├── .gitignore ├── eslint.config.js ├── index.html └── package.json /license.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/license.pdf -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/photos/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/a.png -------------------------------------------------------------------------------- /public/photos/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/b.png -------------------------------------------------------------------------------- /public/photos/c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/c.png -------------------------------------------------------------------------------- /public/photos/d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/d.png -------------------------------------------------------------------------------- /public/photos/e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/e.png -------------------------------------------------------------------------------- /public/photos/f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/f.png -------------------------------------------------------------------------------- /public/photos/g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/g.png -------------------------------------------------------------------------------- /public/photos/h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/h.png -------------------------------------------------------------------------------- /public/photos/i.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/i.png -------------------------------------------------------------------------------- /public/photos/j.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/j.png -------------------------------------------------------------------------------- /public/photos/k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/k.png -------------------------------------------------------------------------------- /public/photos/l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/l.png -------------------------------------------------------------------------------- /public/photos/m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/m.png -------------------------------------------------------------------------------- /public/photos/n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/n.png -------------------------------------------------------------------------------- /public/photos/o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/o.png -------------------------------------------------------------------------------- /public/photos/p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/p.png -------------------------------------------------------------------------------- /public/photos/q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/q.png -------------------------------------------------------------------------------- /public/photos/r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/r.png -------------------------------------------------------------------------------- /public/photos/s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/s.png -------------------------------------------------------------------------------- /public/photos/t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/t.png -------------------------------------------------------------------------------- /public/photos/u.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/u.png -------------------------------------------------------------------------------- /public/photos/v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/v.png -------------------------------------------------------------------------------- /public/photos/w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/w.png -------------------------------------------------------------------------------- /public/photos/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/x.png -------------------------------------------------------------------------------- /public/photos/y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/y.png -------------------------------------------------------------------------------- /public/photos/z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/public/photos/z.png -------------------------------------------------------------------------------- /docs/images/cbp88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/cbp88.png -------------------------------------------------------------------------------- /docs/images/cbp88bundled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/cbp88bundled.png -------------------------------------------------------------------------------- /docs/images/cbp88matrixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/cbp88matrixed.png -------------------------------------------------------------------------------- /docs/images/PageSizeDiagram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/PageSizeDiagram1.png -------------------------------------------------------------------------------- /docs/images/PageSizeDiagram2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/PageSizeDiagram2.png -------------------------------------------------------------------------------- /docs/images/PageSizeDiagram3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/PageSizeDiagram3.png -------------------------------------------------------------------------------- /docs/images/cbp88everyparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/cbp88everyparent.png -------------------------------------------------------------------------------- /docs/images/sparse_data_problem.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasicPrimitives/react/HEAD/docs/images/sparse_data_problem.pdf -------------------------------------------------------------------------------- /src/Diagrams/Templates/AbstractTemplate.js: -------------------------------------------------------------------------------- 1 | class AbstractTemplate { 2 | template() { } 3 | getHashCode() { } 4 | render(data) { } 5 | }; 6 | 7 | export default AbstractTemplate; -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /build 2 | /node_modules 3 | /public 4 | /docs 5 | /src 6 | /output 7 | .gitignore 8 | eslint.config.js 9 | package-lock.json 10 | yarn.lock 11 | index.html 12 | .vscode 13 | *.log 14 | vite.config.js -------------------------------------------------------------------------------- /docs/LoopsInFamily.md: -------------------------------------------------------------------------------- 1 | # Optimization of loops in layered graph visualization 2 | 3 | If relations between nodes form loops, the control tries to find a layout minimizing the number of links going in the opposite direction. 4 | 5 | [React](../src/Samples/LoopsInFamilyChart.jsx) 6 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | base: process.env.NODE_ENV === 'production' ? '/react/' : '/', 7 | build: { 8 | outDir: 'output' 9 | }, 10 | }); -------------------------------------------------------------------------------- /src/Diagrams/Templates/UserTemplate.js: -------------------------------------------------------------------------------- 1 | import AbstractTemplate from './AbstractTemplate'; 2 | 3 | class UserTemplate extends AbstractTemplate { 4 | constructor(options, content, onRender) { 5 | super(); 6 | this.render = onRender; 7 | } 8 | }; 9 | 10 | export default UserTemplate; -------------------------------------------------------------------------------- /src/Diagrams/Templates/CustomRenderTemplate.js: -------------------------------------------------------------------------------- 1 | import AbstractTemplate from './AbstractTemplate'; 2 | 3 | class CustomRenderTemplate extends AbstractTemplate { 4 | constructor(options, onRender) { 5 | super(); 6 | this.render = onRender; 7 | } 8 | }; 9 | 10 | export default CustomRenderTemplate; -------------------------------------------------------------------------------- /src/Diagrams/Templates/AnnotationLabelTemplate.js: -------------------------------------------------------------------------------- 1 | import AbstractTemplate from './AbstractTemplate'; 2 | 3 | class AnnotationLabelTemplate extends AbstractTemplate { 4 | render(data) { 5 | var { label } = data.context; 6 | return label; 7 | } 8 | }; 9 | 10 | export default AnnotationLabelTemplate; -------------------------------------------------------------------------------- /src/Diagrams/Templates/ButtonsTemplate.js: -------------------------------------------------------------------------------- 1 | import AbstractTemplate from './AbstractTemplate'; 2 | 3 | class ButtonsTemplate extends AbstractTemplate { 4 | render(data) { 5 | const { onButtonsRender } = data; 6 | return onButtonsRender(data); 7 | }; 8 | }; 9 | 10 | export default ButtonsTemplate; 11 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/LabelAnnotationTemplate.js: -------------------------------------------------------------------------------- 1 | import AbstractTemplate from './AbstractTemplate'; 2 | 3 | class LabelAnnotationTemplate extends AbstractTemplate { 4 | render(data) { 5 | var { title } = data.context; 6 | return title; 7 | } 8 | }; 9 | 10 | export default LabelAnnotationTemplate; -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StrictMode } from 'react' 3 | import { createRoot } from 'react-dom/client'; 4 | import './index.css'; 5 | import App from './App.jsx'; 6 | 7 | createRoot(document.getElementById('root')).render( 8 | 9 | 10 | , 11 | ) 12 | 13 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const container = document.createElement('div'); 7 | const root = createRoot(container); 8 | root.render(); 9 | root.unmount(); 10 | }); 11 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/PaletteItemConfigShape.js: -------------------------------------------------------------------------------- 1 | import { LineType } from 'basicprimitives'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const PaletteItemConfigShape = PropTypes.shape({ 5 | lineColor: PropTypes.string, 6 | lineWidth: PropTypes.number, 7 | lineType: PropTypes.oneOf(Object.values(LineType)) 8 | }); 9 | 10 | export default PaletteItemConfigShape; 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | output 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/Diagrams/FamDiagram.js: -------------------------------------------------------------------------------- 1 | import { FamTaskManagerFactory, FamEventArgs } from 'basicprimitives'; 2 | import BaseDiagram from './BaseDiagram'; 3 | import FamConfigShape from './Schemas/FamConfigShape'; 4 | 5 | class FamDiagram extends BaseDiagram { 6 | static propTypes = { 7 | config: FamConfigShape.isRequired // eslint-disable-line react/no-unused-prop-types 8 | }; 9 | 10 | constructor(props) { 11 | super(props, FamTaskManagerFactory, FamEventArgs); 12 | } 13 | } 14 | 15 | export default FamDiagram; 16 | -------------------------------------------------------------------------------- /src/Diagrams/OrgDiagram.js: -------------------------------------------------------------------------------- 1 | import { OrgTaskManagerFactory, OrgEventArgs } from 'basicprimitives'; 2 | import BaseDiagram from './BaseDiagram'; 3 | import OrgConfigShape from './Schemas/OrgConfigShape'; 4 | 5 | class OrgDiagram extends BaseDiagram { 6 | static propTypes = { 7 | config: OrgConfigShape.isRequired // eslint-disable-line react/no-unused-prop-types 8 | }; 9 | 10 | constructor(props) { 11 | super(props, OrgTaskManagerFactory, OrgEventArgs); 12 | } 13 | } 14 | 15 | export default OrgDiagram; 16 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/LabelAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { AnnotationType } from 'basicprimitives'; 3 | 4 | const LabelAnnotationConfigShape = PropTypes.shape({ 5 | annotationType: PropTypes.oneOf([AnnotationType.Label]), 6 | fromItem: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, 7 | toItems: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])).isRequired, 8 | title: PropTypes.any, 9 | itemTitleColor: PropTypes.string, 10 | templateName: PropTypes.string 11 | }); 12 | 13 | export default LabelAnnotationConfigShape; 14 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/HighlightPathAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { AnnotationType, ZOrderType, LineType } from 'basicprimitives'; 3 | 4 | const HighlightPathAnnotationConfigShape = PropTypes.shape({ 5 | annotationType: PropTypes.oneOf([AnnotationType.HighlightPath]), 6 | zOrderType: PropTypes.oneOf(Object.values(ZOrderType)), 7 | items: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), 8 | lineWidth: PropTypes.number, 9 | color: PropTypes.string, 10 | lineType: PropTypes.oneOf(Object.values(LineType)), 11 | opacity: PropTypes.number, 12 | showArrows: PropTypes.bool, 13 | selectItems: PropTypes.bool 14 | }); 15 | 16 | export default HighlightPathAnnotationConfigShape; 17 | -------------------------------------------------------------------------------- /docs/ZoomWithCSSScaleTransform.md: -------------------------------------------------------------------------------- 1 | # Zoom using CSS scale transform 2 | 3 | The control has the `scale` option setting CSS scale transform of the control's content. It scales everything except scroll bars and correctly handles mouse event coordinates. 4 | 5 | ```JavaScript 6 | import { OrgDiagram } from basicprimitivesreact; 7 | 11 | ``` 12 | 13 | Excessive CSS scale produces unreadable text and corrupts graphics lines in some desktop browsers. It looks good on mobile devices. We suggest testing your diagram on various devices to ensure a consistent user experience. 14 | 15 | Use custom item templates for various zoom levels, so your application would be responsive regardless of available screen space and scale. 16 | 17 | [React](../src/Samples/ZoomWithCSSScaleTransform.jsx) 18 | -------------------------------------------------------------------------------- /docs/CrossBranchAlignment.md: -------------------------------------------------------------------------------- 1 | # Cross-Branch children alignment 2 | In the Organizational Chart layout, the number of rows occupied by the immediate children depends on the number of assistants, advisers, and levels of children of the node. So nodes at the same logical level in the organizational chart hierarchy require visual alignment during visualization. 3 | As expected, the control aligns regular horizontally placed children across branches of the hierarchy. But we support the alignment of Assistants, SubAssistants, Advisers, and SubAdvisers hierarchies and child nodes in vertical and matrix formations across departments. 4 | 5 | The following options control cross-branch nodes alignment: 6 | * `alignBranches` - property enables alignment 7 | * `placeAdvisersAboveChildren` - if this property is disabled, then advisers' children are placed at the same level and aligned to the children of the parent's node. 8 | 9 | [React](../src/Samples/CrossBranchAlignment.jsx) 10 | 11 | -------------------------------------------------------------------------------- /docs/MatrixLayoutInFamilyChart.md: -------------------------------------------------------------------------------- 1 | # Matrix layout in family chart 2 | The option `enableMatrixLayout` enables automatic matrix layout for nodes sharing the same set of parents and children in the diagram. The matrix forms a square shape. If the matrix outgrows the screen width, it makes diagram navigation complicated. The `maximumColumnsInMatrix` option limits the maximum number of columns and forces the matrix to grow vertically instead of horizontally. The option `minimumMatrixSize` sets the minimum number of nodes needed to be shaped into matrix formation. 3 | 4 | [React](../src/Samples/MatrixLayoutInFamilyChart.jsx) 5 | 6 | # Grouping nodes into multiple matrixes 7 | The family diagram provides options to control grouping nodes into matrixes per node. The option `addToMatrix` lets you explicitly prohibit adding nodes into any group of matrixed nodes. And the `matrixId` option allows you to group nodes into multiple matrixes. 8 | 9 | [React](../src/Samples/MatrixGroupsInFamilyChart.jsx) -------------------------------------------------------------------------------- /src/Diagrams/Schemas/BackgroundAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import {AnnotationType, ZOrderType, LineType} from 'basicprimitives'; 3 | 4 | const BackgroundAnnotationConfigShape = PropTypes.shape({ 5 | annotationType: PropTypes.oneOf([AnnotationType.Background]), 6 | items: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), 7 | includeChildren: PropTypes.bool, 8 | zOrderType: PropTypes.oneOf(Object.values(ZOrderType)), 9 | offset: PropTypes.shape({ 10 | left: PropTypes.number.isRequired, 11 | top: PropTypes.number.isRequired, 12 | right: PropTypes.number.isRequired, 13 | bottom: PropTypes.number.isRequired 14 | }), 15 | lineWidth: PropTypes.number, 16 | opacity: PropTypes.number, 17 | borderColor: PropTypes.string, 18 | fillColor: PropTypes.string, 19 | lineType: PropTypes.oneOf(Object.values(LineType)), 20 | selectItems: PropTypes.bool 21 | }); 22 | 23 | export default BackgroundAnnotationConfigShape; 24 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/index.js: -------------------------------------------------------------------------------- 1 | export { default as AnnotationLabelTemplate } from './AnnotationLabelTemplate'; 2 | export { default as ButtonsTemplate } from './ButtonsTemplate'; 3 | export { default as CheckBoxTemplate } from './CheckBoxTemplate'; 4 | export { default as CursorTemplate } from './CursorTemplate'; 5 | export { default as DotHighlightTemplate } from './DotHighlightTemplate'; 6 | export { default as GroupTitleTemplate } from './GroupTitleTemplate'; 7 | export { default as HighlightTemplate } from './HighlightTemplate'; 8 | export { default as ItemTemplate } from './ItemTemplate'; 9 | export { default as UserTemplate } from './UserTemplate'; 10 | export { default as CustomRenderTemplate } from './CustomRenderTemplate'; 11 | export { default as LabelAnnotationTemplate } from './LabelAnnotationTemplate'; 12 | export { default as LevelTitleTemplate } from './LevelTitleTemplate'; 13 | export { default as LevelBackgroundTemplate } from './LevelBackgroundTemplate'; 14 | export { default as EndPointTemplate } from './EndPointTemplate'; -------------------------------------------------------------------------------- /src/Diagrams/Schemas/LevelAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { AnnotationType, LineType } from 'basicprimitives'; 3 | 4 | const LevelAnnotationConfigShape = PropTypes.shape({ 5 | annotationType: PropTypes.oneOf([AnnotationType.Level]), 6 | levels: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), 7 | title: PropTypes.any, 8 | titleFontColor: PropTypes.string, 9 | titleColor: PropTypes.string, 10 | offset: PropTypes.shape({ 11 | left: PropTypes.number.isRequired, 12 | top: PropTypes.number.isRequired, 13 | right: PropTypes.number.isRequired, 14 | bottom: PropTypes.number.isRequired 15 | }), 16 | lineWidth: PropTypes.shape({ 17 | left: PropTypes.number.isRequired, 18 | top: PropTypes.number.isRequired, 19 | right: PropTypes.number.isRequired, 20 | bottom: PropTypes.number.isRequired 21 | }), 22 | opacity: PropTypes.number, 23 | borderColor: PropTypes.string, 24 | fillColor: PropTypes.string, 25 | lineType: PropTypes.oneOf(Object.values(LineType)) 26 | }); 27 | 28 | export default LevelAnnotationConfigShape; 29 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/CheckBoxTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | 4 | class CheckBoxTemplate extends AbstractTemplate { 5 | constructor(selectCheckBoxLabel) { 6 | super(); 7 | this.render = this.render.bind(this); 8 | 9 | this.selectCheckBoxLabel = selectCheckBoxLabel; 10 | } 11 | 12 | render(data) { 13 | return
14 | null} data-id={data.id} /> 21 | 31 | {this.selectCheckBoxLabel} 32 | 33 |
; 34 | } 35 | }; 36 | 37 | export default CheckBoxTemplate; -------------------------------------------------------------------------------- /src/Samples/FirstOrganizationalChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { Enabled, PageFitMode } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.FitToPage, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | hasSelectorCheckbox: Enabled.True, 12 | items: [ 13 | { 14 | id: 0, 15 | parent: null, 16 | title: 'James Smith', 17 | description: 'VP, Public Sector', 18 | image: './photos/a.png' 19 | }, 20 | { 21 | id: 1, 22 | parent: 0, 23 | title: 'Ted Lucas', 24 | description: 'VP, Human Resources', 25 | image: './photos/b.png' 26 | }, 27 | { 28 | id: 2, 29 | parent: 0, 30 | title: 'Fritz Stuger', 31 | description: 'Business Solutions, US', 32 | image: './photos/c.png' 33 | } 34 | ] 35 | }; 36 | 37 | return
38 | 39 |
40 | } 41 | } 42 | 43 | export default Sample; 44 | -------------------------------------------------------------------------------- /docs/SelectionPathModeInFamilyChart.md: -------------------------------------------------------------------------------- 1 | # Selection path nodes in Family Diagram 2 | 3 | ## Selected items 4 | 5 | Suppose the total square size of items exceeds the available screen size. There is no possibility to show all of them, and the chart should use various strategies to display essential information. By default family diagram shows `cursorItem` and all its neighbors in full size using templates. The same rule applies to selected items and annotated items. For all other non-selected items, the chart conditionally minimizes them into dots or lines. 6 | 7 | ## Selection path items 8 | 9 | The selection path is nodes between selected items and top row nodes in the layered diagram. The developer can show selection path items in full size with property `selectionPathMode` set to `SelectionPathMode.FullStack` or let the layout manager hide them and show them as dots or lines with `SelectionPathMode.None`. 10 | 11 | 12 | ```JavaScript 13 | import { FamDiagram } from basicprimitivesreact; 14 | import { SelectionPathMode } from basicprimitives; 15 | 16 | 20 | ``` 21 | 22 | [React](../src/Samples/SelectionPathModeInFamilyChart.jsx) 23 | -------------------------------------------------------------------------------- /docs/Labels.md: -------------------------------------------------------------------------------- 1 | # Labels 2 | 3 | ## Minimized items labels 4 | Chart supports rendering labels for minimized dotted items. The following properties on `config` used to control labels rendering: 5 | * `showLabels` 6 | * `labelSize` 7 | * `labelOffset` 8 | * `labelOrientation` 9 | * `labelPlacement` 10 | 11 | User can customize labels per individual item with `ItemConfig` properties: 12 | * `label` 13 | * `showLabel` 14 | 15 | ```JavaScript 16 | import { OrgDiagram } from basicprimitivesreact; 17 | import { Enabled } from basicprimitives; 18 | 31 | ``` 32 | 33 | [React](../src/Samples/Labels.jsx) 34 | 35 | ## Adding labels to the item template 36 | If we need to draw labels for visible items, we can add them to templates. The following example demonstrates how to add an extra property "percent" to the item and render it a label over the item connection line. 37 | 38 | [React](../src/Samples/ItemTemplateLabel.jsx) 39 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/CalloutAnnotationComponentConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { LineType, PlacementType } from 'basicprimitives'; 3 | 4 | const RectShape = PropTypes.shape({ 5 | x: PropTypes.number.isRequired, 6 | y: PropTypes.number.isRequired, 7 | width: PropTypes.number.isRequired, 8 | height: PropTypes.number.isRequired 9 | }); 10 | 11 | const PointShape = PropTypes.shape({ 12 | x: PropTypes.number.isRequired, 13 | y: PropTypes.number.isRequired 14 | }); 15 | 16 | const CalloutAnnotationControlConfigShape = PropTypes.shape({ 17 | pointerPlacement: PropTypes.oneOf(Object.values(PlacementType)), 18 | position: RectShape, 19 | snapPoint: PointShape, 20 | cornerRadius: PropTypes.oneOfType([ 21 | PropTypes.string, // e.g. "10%" 22 | PropTypes.number // in case pixel radius is supported 23 | ]), 24 | offset: PropTypes.number, 25 | opacity: PropTypes.number, 26 | lineWidth: PropTypes.number, 27 | lineType: PropTypes.oneOf(Object.values(LineType)), 28 | pointerWidth: PropTypes.oneOfType([ 29 | PropTypes.string, // e.g. "10%" 30 | PropTypes.number // e.g. 10 31 | ]), 32 | borderColor: PropTypes.string, 33 | fillColor: PropTypes.string 34 | }); 35 | 36 | export default CalloutAnnotationControlConfigShape; 37 | -------------------------------------------------------------------------------- /src/Diagrams/index.js: -------------------------------------------------------------------------------- 1 | export { default as OrgDiagram } from './OrgDiagram'; 2 | export { default as FamDiagram } from './FamDiagram'; 3 | 4 | export { default as RotatedText } from './Templates/RotatedText'; 5 | 6 | export { default as BackgroundAnnotationConfigShape } from './Schemas/BackgroundAnnotationConfigShape'; 7 | export { default as ConnectorAnnotationConfigShape } from './Schemas/ConnectorAnnotationConfigShape'; 8 | export { default as FamConfigShape } from './Schemas/FamConfigShape'; 9 | export { default as FamItemConfigShape } from './Schemas/FamItemConfigShape'; 10 | export { default as HighlightPathAnnotationConfigShape } from './Schemas/HighlightPathAnnotationConfigShape'; 11 | export { default as LabelAnnotationConfigShape } from './Schemas/LabelAnnotationConfigShape'; 12 | export { default as OrgConfigShape } from './Schemas/OrgConfigShape'; 13 | export { default as OrgItemConfigShape } from './Schemas/OrgItemConfigShape'; 14 | export { default as PaletteItemConfigShape } from './Schemas/PaletteItemConfigShape'; 15 | export { default as ShapeAnnotationConfigShape } from './Schemas/ShapeAnnotationConfigShape'; 16 | export { default as LevelAnnotationConfigShape } from './Schemas/LevelAnnotationConfigShape'; 17 | export { default as TemplateConfigShape } from './Schemas/TemplateConfigShape'; 18 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/ShapeAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { AnnotationType, ZOrderType, ShapeType, LineType, PlacementType } from 'basicprimitives'; 3 | 4 | const ShapeAnnotationConfigShape = PropTypes.shape({ 5 | annotationType: PropTypes.oneOf([AnnotationType.Shape]), 6 | zOrderType: PropTypes.oneOf(Object.values(ZOrderType)), 7 | items: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), 8 | shapeType: PropTypes.oneOf(Object.values(ShapeType)), 9 | offset: PropTypes.shape({ 10 | left: PropTypes.number.isRequired, 11 | top: PropTypes.number.isRequired, 12 | right: PropTypes.number.isRequired, 13 | bottom: PropTypes.number.isRequired 14 | }), 15 | lineWidth: PropTypes.number, 16 | cornerRadius: PropTypes.string, 17 | opacity: PropTypes.number, 18 | borderColor: PropTypes.string, 19 | fillColor: PropTypes.string, 20 | lineType: PropTypes.oneOf(Object.values(LineType)), 21 | selectItems: PropTypes.bool, 22 | label: PropTypes.any, 23 | labelSize: PropTypes.shape({ 24 | width: PropTypes.number.isRequired, 25 | height: PropTypes.number.isRequired 26 | }), 27 | labelPlacement: PropTypes.oneOf(Object.values(PlacementType)), 28 | labelOffset: PropTypes.number 29 | }); 30 | 31 | export default ShapeAnnotationConfigShape; 32 | -------------------------------------------------------------------------------- /docs/LabelsCascadesInFamilyChart.md: -------------------------------------------------------------------------------- 1 | # Labels cascades over connection lines in family chart 2 | Label annotations are family diagram specific annotations. Label annotations are placed inside the diagram layout between nodes so they don't overlap nodes, can occupy relatively large space. Still, they require the control to rerender nodes when we add label annotations to the diagram. 3 | 4 | Label annotations can bind multiple nodes together, so visually label annotation can define aggregate value for several nodes bundled together. In the following example, we create a cascade of labels from the common parent down to its children. If two labels create mutually exclusive bundles, the first label wins. 5 | 6 | See LabelAnnotationConfig for details. By default, label annotations display regular text labels, but we can use node templates to define their content. So from this perspective, they are regular diagram nodes, having complex placement logic in the diagram. See `templateName` option of `LabelAnnotationConfig` and `defaultLabelAnnotationTemplate` of `FamConfig` class. 7 | 8 | [React](../src/Samples/LabelsCascadesInFamilyChart.jsx) 9 | 10 | # Labels over connection lines in matrix-shaped family chart 11 | The following sample demonstrates how matrix layout places labels around nodes. 12 | 13 | [React](../src/Samples/LabelsNMatrixInFamilyChart.jsx) -------------------------------------------------------------------------------- /src/Diagrams/Templates/LevelBackgroundTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | import { LineType } from 'basicprimitives'; 4 | 5 | function getBorderStyle(lineType) { 6 | var result = null; 7 | switch (lineType) { 8 | case LineType.Dotted: 9 | result = "dotted"; 10 | break; 11 | case LineType.Dashed: 12 | result = "dashed"; 13 | break; 14 | default: 15 | result = "solid"; 16 | break; 17 | } 18 | return result; 19 | } 20 | 21 | 22 | class LevelBackgroundTemplate extends AbstractTemplate { 23 | constructor(options) { 24 | super(); 25 | 26 | this.options = options; 27 | 28 | this.render = this.render.bind(this); 29 | 30 | this.style = { 31 | position: "absolute", 32 | width: "100%", 33 | height: "100%" 34 | } 35 | } 36 | 37 | render(data) { 38 | var annotationConfig = data.context; 39 | var style = { 40 | opacity: annotationConfig.opacity, 41 | borderColor: annotationConfig.borderColor, 42 | backgroundColor: annotationConfig.fillColor, 43 | borderWidth: annotationConfig.lineWidth.toString(), 44 | borderStyle: getBorderStyle(annotationConfig.lineType), 45 | ...this.style 46 | } 47 | return
; 48 | } 49 | }; 50 | 51 | export default LevelBackgroundTemplate; -------------------------------------------------------------------------------- /docs/MultipleRootItemsInChart.md: -------------------------------------------------------------------------------- 1 | # Multiple root items in the chart 2 | 3 | Multiple root items are convenient to display tree structure having missing parent relations for some nodes. The component would render such hierarchy using multiple tree fragments placed side by side in the diagram. 4 | 5 | Following example shows two disconnected hierarchies: 6 | 7 | [React](../src/Samples/MultipleRootItemsInChart.jsx) 8 | 9 | ## Matrix layout of multiple root items 10 | It is an extreme example when nodes don't have parent relations at all. So the control displays them in the form of a long horizontal list of nodes. It isn't easy to scroll and arrange them. The suggested solution is to shape root items into the matrixed formation, so our fragments take less space and fit into the available screen space. 11 | The matrixed formation works best when we use drag and drop to merge nodes back to the tree structure, so we need to place our nodes close to each other. 12 | In the following example, we create an invisible root item and use it as a parent node for all our parentless nodes in the structure. Then we set its `childrenPlacementType` option to `ChildrenPlacementType.Matrix`, so our nine nodes form compact 3 * 3 matrix formation. 13 | 14 | 15 | See Children Layout example to place fragments vertically or horizontally. 16 | 17 | [React](../src/Samples/MatrixLayoutOfMultipleRootItemsInChart.jsx) -------------------------------------------------------------------------------- /docs/LevelAnnotation.md: -------------------------------------------------------------------------------- 1 | # Level Annotations 2 | 3 | Basic Primitives Diagrams render layered hierarchies and dependency diagrams. Level annotation decorates the level of nodes with title and background stripe drawn across the diagram's whole level. The title is placed outside the diagram's view area and occupies screen space. Optionally you can place level annotation titles inside the diagram's view area in the background. 4 | 5 | Level Annotations reference logical levels of the diagram. The first node placed into the logical level defines the level index. So if you have manager nodes vertically aligned under their CEO, you will have multiple managerial level annotations alternating with direct reports level annotations. See vertical organizational chart demo application. 6 | The node's children types and layout options change the number of visual levels in the hierarchy; use the `alignBranches` option to align children having the same logical level at one visual level. 7 | 8 | [React](../src/Samples/LevelAnnotation.jsx) 9 | 10 | # Templates 11 | 12 | Use the following options to modify level annotation default content and mouse events: 13 | 14 | * `onLevelTitleRender` - callback function to render level title 15 | * `onLevelBackgroundRender` - callback function to render level background 16 | 17 | Click on the level annotation title to see a popup message. 18 | 19 | [React](../src/Samples/LevelAnnotationTemplate.jsx) -------------------------------------------------------------------------------- /src/Diagrams/Templates/HighlightTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | 4 | class HighlightTemplate extends AbstractTemplate { 5 | constructor(options, config) { 6 | super(); 7 | 8 | this.options = options; 9 | this.config = config; 10 | 11 | this.render = this.render.bind(this); 12 | 13 | this.style = { 14 | position: "absolute", 15 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 16 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 17 | WebkitUserSelect: "none", 18 | WebkitTouchCallout: "none", 19 | KhtmlUserSelect: "none", 20 | MozUserSelect: "none", 21 | msUserSelect: "none", 22 | userSelect: "none", 23 | boxSizing: "content-box", 24 | 25 | MozBorderRadius: "4px", 26 | WebkitBorderRadius: "4px", 27 | KhtmlBorderRadius: "4px", 28 | BorderRadius: "4px", 29 | 30 | border: "1px solid #fbcb09", 31 | background: "#fdf5ce", 32 | color: "#c77405", 33 | 34 | width: "100%", 35 | height: "100%", 36 | left: "-1px", 37 | top: "-1px" 38 | } 39 | } 40 | 41 | render(data) { 42 | const { highlightBorderWidth } = this.config; 43 | return
; 47 | } 48 | }; 49 | 50 | export default HighlightTemplate; -------------------------------------------------------------------------------- /docs/ShapeAndBackgroundAnnotations.md: -------------------------------------------------------------------------------- 1 | # Shape & Background Annotations 2 | ## Shape 3 | Diagrams support on-screen annotations for visualization of non - hierarchical relations between nodes. Components use SVG elements to render geometrical figures depending on user options. If you need some specific custom shapes, you have to make changes to the source code or use images and custom items templates. Shape annotation supports the following figures: 4 | * Rounded Rectangle 5 | * Oval 6 | * Triangle 7 | * Cross Out 8 | * Circle 9 | * Rhombus 10 | 11 | If you define multiple nodes, then annotation draws one large shape on top of them. If you need numerous individual figures for a list of nodes, you have to create shape annotation for every node. If you don't define background or borderline color, then they are not drawn. Annotation can render a text label on the side of the geometrical figure. 12 | 13 | ## Background Annotation 14 | This visual is a common background area having borderline and fill color. When we render the same background annotation for multiple items, control automatically blends them into a single continuous shape. Increase background offset around nodes if they don't merge. Merging annotations improves visualization and minimizes visual clutter in the diagram. Use `includeChildren` option to expand the background around the item down to all of its descendants. 15 | 16 | [React](../src/Samples/ShapeAnnotation.jsx) 17 | -------------------------------------------------------------------------------- /docs/ItemAndGroupTitleColors.md: -------------------------------------------------------------------------------- 1 | # Item & group title colors 2 | 3 | Item's title background color may be used for indicating the group of nodes in the diagram and may vary in a wide range of colors. At the same time, the title font color should always be readable on every background. So taking these considerations into account, the control has two options setting font color for node titles: `itemTitleFirstFontColor` and `itemTitleSecondFontColor`. The control selects the most readable font color using the `highestContrast` function. It returns the highest contrast color from two on the given background color. 4 | 5 | See reference for group title size, placement, and orientation. 6 | 7 | ```JavaScript 8 | import { OrgDiagram } from basicprimitivesreact; 9 | import { Enabled, Colors, TextOrientationType } from basicprimitives; 10 | 28 | ``` 29 | 30 | [React](../src/Samples/ItemAndGroupTitleColors.jsx) 31 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/EndPointTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | 4 | class EndPointTemplate extends AbstractTemplate { 5 | constructor(options) { 6 | super(); 7 | this.render = this.render.bind(this); 8 | 9 | this.options = options; 10 | 11 | this.style = { 12 | position: "absolute", 13 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 14 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 15 | WebkitUserSelect: "none", 16 | WebkitTouchCallout: "none", 17 | KhtmlUserSelect: "none", 18 | MozUserSelect: "none", 19 | msUserSelect: "none", 20 | userSelect: "none", 21 | boxSizing: "content-box", 22 | 23 | borderWidth: "0px", 24 | width: "100%", 25 | height: "100%", 26 | left: "0px", 27 | top: "0px" 28 | } 29 | } 30 | 31 | render(data) { 32 | const { endPointCornerRadius, endPointFillColor, endPointOpacity } = this.options; 33 | 34 | const style = { 35 | ...this.style, 36 | MozBorderRadius: endPointCornerRadius + "px", 37 | WebkitBorderRadius: endPointCornerRadius + "px", 38 | KhtmlBorderRadius: endPointCornerRadius + "px", 39 | borderRadius: endPointCornerRadius + "px", 40 | background: endPointFillColor, 41 | opacity: endPointOpacity 42 | } 43 | return
; 44 | } 45 | }; 46 | 47 | export default EndPointTemplate; -------------------------------------------------------------------------------- /src/Diagrams/Schemas/RotatedTextComponentConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { 3 | VerticalAlignmentType, 4 | TextOrientationType, 5 | HorizontalAlignmentType 6 | } from 'basicprimitives'; // Adjust this import path as needed 7 | 8 | const RotatedTextControlConfigShape = PropTypes.shape({ 9 | /** 10 | * Label orientation. 11 | */ 12 | orientation: PropTypes.oneOf(Object.values(TextOrientationType)), 13 | 14 | /** 15 | * The text content to display. 16 | */ 17 | text: PropTypes.string, 18 | 19 | /** 20 | * Label vertical alignment inside bounding rectangle. 21 | */ 22 | verticalAlignment: PropTypes.oneOf(Object.values(VerticalAlignmentType)), 23 | 24 | /** 25 | * Label horizontal alignment inside bounding rectangle. 26 | */ 27 | horizontalAlignment: PropTypes.oneOf(Object.values(HorizontalAlignmentType)), 28 | 29 | /** 30 | * Font size (e.g., "16px") 31 | */ 32 | fontSize: PropTypes.string, 33 | 34 | /** 35 | * Font family (e.g., "Arial") 36 | */ 37 | fontFamily: PropTypes.string, 38 | 39 | /** 40 | * Font color (e.g., Colors.Black) 41 | */ 42 | color: PropTypes.string, 43 | 44 | /** 45 | * Font weight (e.g., "normal", "bold") 46 | */ 47 | fontWeight: PropTypes.oneOf(['normal', 'bold']), 48 | 49 | /** 50 | * Font style (e.g., "normal", "italic") 51 | */ 52 | fontStyle: PropTypes.oneOf(['normal', 'italic']) 53 | }); 54 | 55 | export default RotatedTextControlConfigShape; 56 | -------------------------------------------------------------------------------- /docs/SelectionPathMode.md: -------------------------------------------------------------------------------- 1 | # Selection path nodes in Organizational Chart 2 | Suppose the total square size of items exceeds the available screen size. There is no possibility to show all of them, and the chart should use various strategies to display the essential information. By default organizational chart shows the current cursor item and all its children and parents up to the root of the hierarchy. The same rule applies to the list of selected items. All other less essential nodes, chart is allowed to display in the form of markers and lines. 3 | 4 | The selection path is a collection of parents between any node and the root of the hierarchy. The control can show the selection path for the current cursor node and selected nodes as well. If you need to show selection path nodes, set the option `selectionPathMode` to `SelectionPathMode.FullStack` or hide them to markers with `SelectionPathMode.None` option. 5 | 6 | ```JavaScript 7 | import { OrgDiagram } from basicprimitivesreact; 8 | import { SelectionPathMode } from basicprimitives; 9 | 13 | ``` 14 | 15 | When we need to compare two or three selected items in the organizational chart, it is essential to show their selection paths. It gives you context for nodes comparison: immediate managers, departments, and relative levels in the organization. In the case of many selected items, it is better to hide selection paths to save valuable space and fit the chart into the screen. 16 | 17 | [React](../src/Samples/SelectionPathMode.jsx) 18 | -------------------------------------------------------------------------------- /docs/ZoomWithItemTemplate.md: -------------------------------------------------------------------------------- 1 | # Zoom using Item template 2 | When we scale our diagram, the primary objective is to enlarge or downsize nodes to fit the chart into available screen space and overview its layout. 3 | 4 | We design our components primarily for business applications, where usability requirements constrain text size. If the text is too small and unreadable, it is a UI bug. When we design our nodes templates for various zoom levels, we can change their size, but we have to keep minimal font size unchanged, so nodes become small and contain less text. The same happens when we increase nodes in size, we don't increase fonts endlessly. It is not needed. Instead, we pack more content into the template, so our nodes become more informative. 5 | 6 | Usage of custom node templates for various scales guarantees that users see node content, regardless of the zoom level. You design, tune and test templates for every zoom level individually. 7 | 8 | You protect yourself against UX support tickets caused by misuse of the zoom. Users may accidentally scroll the mouse over the diagram and zoom it out into a meaningless graphics element. The user would not comprehend that it is the diagram on the screen anymore. 9 | 10 | Scaling diagrams with custom templates is very similar to developing responsive web applications for desktop and mobile. The screen space is so different that desktop application is not readable on mobile phone screen without adjustments. 11 | 12 | See item template use case for more details about templates usage. 13 | 14 | [React](../src/Samples/ZoomWithItemTemplate.jsx) 15 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/ConnectorAnnotationConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { ConnectorLabelPlacementType, ConnectorPlacementType, 3 | ConnectorShapeType, ZOrderType, AnnotationType, LineType, Enabled } from 'basicprimitives'; 4 | 5 | const ConnectorAnnotationConfigShape = PropTypes.shape({ 6 | annotationType: PropTypes.oneOf([AnnotationType.Connector]), 7 | zOrderType: PropTypes.oneOf(Object.values(ZOrderType)), 8 | fromItem: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 9 | toItem: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 10 | connectorShapeType: PropTypes.oneOf(Object.values(ConnectorShapeType)), 11 | connectorPlacementType: PropTypes.oneOf(Object.values(ConnectorPlacementType)), 12 | labelPlacementType: PropTypes.oneOf(Object.values(ConnectorLabelPlacementType)), 13 | offset: PropTypes.oneOfType([ 14 | PropTypes.number, 15 | PropTypes.shape({ 16 | left: PropTypes.number.isRequired, 17 | top: PropTypes.number.isRequired, 18 | right: PropTypes.number.isRequired, 19 | bottom: PropTypes.number.isRequired 20 | }) 21 | ]), 22 | lineWidth: PropTypes.number, 23 | color: PropTypes.string, 24 | lineType: PropTypes.oneOf(Object.values(LineType)), 25 | selectItems: PropTypes.bool, 26 | label: PropTypes.any, 27 | labelSize: PropTypes.shape({ 28 | width: PropTypes.number.isRequired, 29 | height: PropTypes.number.isRequired 30 | }), 31 | showFromEndpoint: PropTypes.oneOf(Object.values(Enabled)), 32 | showToEndpoint: PropTypes.oneOf(Object.values(Enabled)), 33 | context: PropTypes.any 34 | }); 35 | 36 | export default ConnectorAnnotationConfigShape; 37 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/CursorTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | 4 | class CursorTemplate extends AbstractTemplate { 5 | constructor(options, itemTemplateConfig) { 6 | super(); 7 | 8 | this.options = options; 9 | this.itemTemplateConfig = itemTemplateConfig; 10 | 11 | this.render = this.render.bind(this); 12 | 13 | this.style = { 14 | position: "absolute", 15 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 16 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 17 | WebkitUserSelect: "none", 18 | WebkitTouchCallout: "none", 19 | KhtmlUserSelect: "none", 20 | MozUserSelect: "none", 21 | msUserSelect: "none", 22 | userSelect: "none", 23 | boxSizing: "content-box", 24 | 25 | MozBorderRadius: "4px", 26 | WebkitBorderRadius: "4px", 27 | KhtmlBorderRadius: "4px", 28 | BorderRadius: "4px", 29 | 30 | border: "2px solid #fbd850", 31 | background: "#ffffff", 32 | color: "#eb8f00", 33 | 34 | width: "100%", 35 | height: "100%", 36 | left: "-2px", 37 | top: "-2px" 38 | } 39 | } 40 | 41 | render(data) { 42 | const { cursorPadding, itemSize, cursorBorderWidth } = this.itemTemplateConfig; 43 | const style = { 44 | ...this.style, 45 | width: (itemSize.width + cursorPadding.left + cursorPadding.right) + "px", 46 | height: (itemSize.height + cursorPadding.top + cursorPadding.bottom) + "px", 47 | borderWidth: cursorBorderWidth + "px" 48 | } 49 | return
; 50 | } 51 | }; 52 | 53 | export default CursorTemplate; -------------------------------------------------------------------------------- /src/Diagrams/Schemas/ConnectorAnnotationComponentConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { 3 | OrientationType, 4 | ConnectorPlacementType, 5 | ConnectorShapeType, 6 | ConnectorLabelPlacementType, 7 | LineType 8 | } from 'basicprimitives'; 9 | 10 | const RectShape = PropTypes.shape({ 11 | x: PropTypes.number.isRequired, 12 | y: PropTypes.number.isRequired, 13 | width: PropTypes.number.isRequired, 14 | height: PropTypes.number.isRequired 15 | }); 16 | 17 | const ThicknessShape = PropTypes.shape({ 18 | left: PropTypes.number.isRequired, 19 | top: PropTypes.number.isRequired, 20 | right: PropTypes.number.isRequired, 21 | bottom: PropTypes.number.isRequired 22 | }); 23 | 24 | const SizeShape = PropTypes.shape({ 25 | width: PropTypes.number.isRequired, 26 | height: PropTypes.number.isRequired 27 | }); 28 | 29 | const ConnectorAnnotationControlConfigShape = PropTypes.shape({ 30 | orientationType: PropTypes.oneOf(Object.values(OrientationType)), 31 | connectorPlacementType: PropTypes.oneOf(Object.values(ConnectorPlacementType)), 32 | connectorShapeType: PropTypes.oneOf(Object.values(ConnectorShapeType)), 33 | fromRectangle: RectShape, 34 | toRectangle: RectShape, 35 | offset: ThicknessShape, 36 | lineWidth: PropTypes.number, 37 | color: PropTypes.string, 38 | lineType: PropTypes.oneOf(Object.values(LineType)), 39 | label:PropTypes.oneOfType([ 40 | PropTypes.string, 41 | PropTypes.object 42 | ]), 43 | labelSize: SizeShape, 44 | labelOffset: PropTypes.number, 45 | labelPlacementType: PropTypes.oneOf(Object.values(ConnectorLabelPlacementType)) 46 | }); 47 | 48 | export default ConnectorAnnotationControlConfigShape; -------------------------------------------------------------------------------- /src/Diagrams/Schemas/TemplateConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { ShapeType, LineType } from 'basicprimitives'; 3 | 4 | const TemplateConfigShape = PropTypes.shape({ 5 | name: PropTypes.string, 6 | isActive: PropTypes.bool, 7 | itemSize: PropTypes.shape({ 8 | width: PropTypes.number.isRequired, 9 | height: PropTypes.number.isRequired 10 | }), 11 | itemBorderWidth: PropTypes.number, 12 | onItemRender: PropTypes.func, 13 | minimizedItemShapeType: PropTypes.oneOf(Object.values(ShapeType)), 14 | minimizedItemSize: PropTypes.shape({ 15 | width: PropTypes.number.isRequired, 16 | height: PropTypes.number.isRequired 17 | }), 18 | minimizedItemCornerRadius: PropTypes.number, 19 | minimizedItemLineWidth: PropTypes.number, 20 | minimizedItemBorderColor: PropTypes.string, 21 | minimizedItemLineType: PropTypes.oneOf(Object.values(LineType)), 22 | minimizedItemFillColor: PropTypes.string, 23 | minimizedItemOpacity: PropTypes.number, 24 | highlightPadding: PropTypes.shape({ 25 | left: PropTypes.number.isRequired, 26 | top: PropTypes.number.isRequired, 27 | right: PropTypes.number.isRequired, 28 | bottom: PropTypes.number.isRequired 29 | }), 30 | highlightBorderWidth: PropTypes.number, 31 | onHighlightRender: PropTypes.func, 32 | cursorPadding: PropTypes.shape({ 33 | left: PropTypes.number.isRequired, 34 | top: PropTypes.number.isRequired, 35 | right: PropTypes.number.isRequired, 36 | bottom: PropTypes.number.isRequired 37 | }), 38 | cursorBorderWidth: PropTypes.number, 39 | onCursorRender: PropTypes.func, 40 | onButtonsRender: PropTypes.func 41 | }); 42 | 43 | export default TemplateConfigShape; 44 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React App 23 | 24 | 25 | 26 |
27 | 28 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import SamplesList from './SamplesList'; 3 | import './global.css'; 4 | 5 | 6 | class App extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.onChange = this.onChange.bind(this); 11 | 12 | let key = 1; 13 | this.hash = SamplesList.reduce((agg, group) => { 14 | group.key = key; 15 | key += 1; 16 | group.items.reduce((agg, item) => { 17 | item.key = key; 18 | key += 1; 19 | agg[item.key] = item; 20 | return agg; 21 | }, agg) 22 | return agg; 23 | }, {}); 24 | 25 | this.state = { 26 | activeItem: (SamplesList[0].items[0]) 27 | }; 28 | } 29 | 30 | onChange({ target }) { 31 | const { activeItem } = this.state; 32 | const newItem = this.hash[target.value]; 33 | if (activeItem.key !== newItem.key) { 34 | this.setState({ 35 | activeItem: newItem 36 | }); 37 | } 38 | } 39 | 40 | render() { 41 | const { activeItem } = this.state; 42 | return ( 43 |
44 |

Basic Primitives Diagrams for React

45 |

46 | 56 |

57 |
58 | {activeItem.component} 59 |
60 |
61 | ); 62 | } 63 | } 64 | 65 | export default App; 66 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/FamItemConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { ShapeType, Enabled, TextOrientationType, PlacementType, AdviserPlacementType } from 'basicprimitives'; 3 | 4 | const FamItemConfigShape = PropTypes.shape({ 5 | id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, 6 | parents: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), 7 | title: PropTypes.string, 8 | description: PropTypes.string, 9 | image: PropTypes.string, 10 | context: PropTypes.any, 11 | itemTitleColor: PropTypes.string, 12 | minimizedItemShapeType: PropTypes.oneOf(Object.values(ShapeType)), 13 | groupTitle: PropTypes.string, 14 | groupTitleColor: PropTypes.string, 15 | isActive: PropTypes.bool, 16 | hasSelectorCheckbox: PropTypes.oneOf(Object.values(Enabled)), 17 | hasButtons: PropTypes.oneOf(Object.values(Enabled)), 18 | templateName: PropTypes.string, 19 | showCallout: PropTypes.oneOf(Object.values(Enabled)), 20 | calloutTemplateName: PropTypes.string, 21 | label: PropTypes.string, 22 | showLabel: PropTypes.oneOf(Object.values(Enabled)), 23 | labelSize: PropTypes.shape({ 24 | width: PropTypes.number.isRequired, 25 | height: PropTypes.number.isRequired 26 | }), 27 | labelOrientation: PropTypes.oneOf(Object.values(TextOrientationType)), 28 | labelPlacement: PropTypes.oneOf(Object.values(PlacementType)), 29 | primaryParent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 30 | relativeItem: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 31 | position: PropTypes.number, 32 | placementType: PropTypes.oneOf(Object.values(AdviserPlacementType)), 33 | matrixId: PropTypes.string, 34 | addToMatrix: PropTypes.bool 35 | }); 36 | 37 | export default FamItemConfigShape; 38 | -------------------------------------------------------------------------------- /src/Samples/ItemAndGroupTitleColors.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, Colors, TextOrientationType, OrgItemConfig } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.FitToPage, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | hasSelectorCheckbox: Enabled.True, 12 | itemTitleFirstFontColor: Colors.Yellow, 13 | itemTitleSecondFontColor: Colors.Blue, 14 | groupTitleOrientation: TextOrientationType.RotateRight, 15 | 16 | items: [ 17 | new OrgItemConfig({ 18 | id: 0, 19 | parent: null, 20 | title: "James Smith", 21 | description: "VP, Public Sector", 22 | groupTitle: "Group 1", 23 | image: "./photos/a.png", 24 | itemTitleColor: Colors.Black 25 | }), 26 | new OrgItemConfig({ 27 | id: 1, 28 | parent: 0, 29 | title: "Ted Lucas", 30 | description: "VP, Human Resources", 31 | image: "./photos/b.png", 32 | itemTitleColor: Colors.Green, 33 | groupTitle: "Group 2", 34 | groupTitleColor: Colors.Gray 35 | }), 36 | new OrgItemConfig({ 37 | id: 2, 38 | parent: 0, 39 | title: "Fritz Stuger", 40 | description: "Business Solutions, US", 41 | image: "./photos/c.png", 42 | itemTitleColor: Colors.Yellow, 43 | groupTitle: "Group 2" 44 | }) 45 | ] 46 | }; 47 | 48 | return
49 | 50 |
51 | } 52 | } 53 | 54 | export default Sample; 55 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/OrgItemConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { ShapeType, Enabled, ItemType, AdviserPlacementType, ChildrenPlacementType, TextOrientationType } from 'basicprimitives'; 3 | 4 | const OrgItemConfigShape = PropTypes.shape({ 5 | id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, 6 | parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 7 | title: PropTypes.string, 8 | description: PropTypes.string, 9 | image: PropTypes.string, 10 | context: PropTypes.any, 11 | itemTitleColor: PropTypes.string, 12 | minimizedItemShapeType: PropTypes.oneOf(Object.values(ShapeType)), 13 | groupTitle: PropTypes.string, 14 | groupTitleColor: PropTypes.string, 15 | isVisible: PropTypes.bool, 16 | isActive: PropTypes.bool, 17 | hasSelectorCheckbox: PropTypes.oneOf(Object.values(Enabled)), 18 | hasButtons: PropTypes.oneOf(Object.values(Enabled)), 19 | itemType: PropTypes.oneOf(Object.values(ItemType)), 20 | adviserPlacementType: PropTypes.oneOf(Object.values(AdviserPlacementType)), 21 | childrenPlacementType: PropTypes.oneOf(Object.values(ChildrenPlacementType)), 22 | levelOffset: PropTypes.number, 23 | placeAdvisersAboveChildren: PropTypes.oneOf(Object.values(Enabled)), 24 | placeAssistantsAboveChildren: PropTypes.oneOf(Object.values(Enabled)), 25 | templateName: PropTypes.string, 26 | showCallout: PropTypes.oneOf(Object.values(Enabled)), 27 | calloutTemplateName: PropTypes.string, 28 | label: PropTypes.string, 29 | showLabel: PropTypes.oneOf(Object.values(Enabled)), 30 | labelSize: PropTypes.shape({ 31 | width: PropTypes.number.isRequired, 32 | height: PropTypes.number.isRequired 33 | }), 34 | labelOrientation: PropTypes.oneOf(Object.values(TextOrientationType)) 35 | }); 36 | 37 | export default OrgItemConfigShape; 38 | -------------------------------------------------------------------------------- /src/Samples/AdviserAndAssistantItemTypes.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, GroupByType, Enabled, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | arrowsDirection: GroupByType.Children, 12 | hasSelectorCheckbox: Enabled.False, 13 | items: [ 14 | { 15 | id: 0, 16 | parent: null, 17 | title: "James Smith", 18 | description: "Parent Item", 19 | image: "./photos/a.png" 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | itemType: ItemType.Adviser, 25 | adviserPlacementType: AdviserPlacementType.Right, 26 | title: "Robert Canon", 27 | description: "Adviser item", 28 | groupTitle: "Adviser", 29 | image: "./photos/b.png" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | itemType: ItemType.Assistant, 35 | adviserPlacementType: AdviserPlacementType.Right, 36 | title: "Ted Lucas", 37 | description: "Assitant Item", 38 | groupTitle: "Assistant", 39 | image: "./photos/c.png" 40 | }, 41 | { 42 | id: 3, 43 | parent: 0, 44 | title: "Fritz Stuger", 45 | description: "Regular Item", 46 | groupTitle: "Regular", 47 | image: "./photos/d.png" 48 | } 49 | ] 50 | }; 51 | 52 | return
53 | 54 |
55 | } 56 | } 57 | 58 | export default Sample; 59 | -------------------------------------------------------------------------------- /src/Samples/SubAdviserAndSubAssistantItemTypes.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, GroupByType, Enabled, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | arrowsDirection: GroupByType.Children, 12 | hasSelectorCheckbox: Enabled.False, 13 | items: [ 14 | { 15 | id: 0, 16 | parent: null, 17 | title: "James Smith", 18 | description: "Parent Item", 19 | image: "./photos/a.png" 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | itemType: ItemType.SubAdviser, 25 | adviserPlacementType: AdviserPlacementType.Right, 26 | title: "Robert Canon", 27 | description: "Sub Adviser item", 28 | groupTitle: "SubAdviser", 29 | image: "./photos/b.png" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | itemType: ItemType.SubAssistant, 35 | adviserPlacementType: AdviserPlacementType.Left, 36 | title: "Ted Lucas", 37 | description: "Sub Assitant Item", 38 | groupTitle: "SubAssistant", 39 | image: "./photos/c.png" 40 | }, 41 | { 42 | id: 3, 43 | parent: 0, 44 | title: "Fritz Stuger", 45 | description: "Regular Item", 46 | groupTitle: "Regular", 47 | image: "./photos/d.png" 48 | } 49 | ] 50 | }; 51 | 52 | return
53 | 54 |
55 | } 56 | } 57 | 58 | export default Sample; 59 | -------------------------------------------------------------------------------- /docs/HighlightTemplate.md: -------------------------------------------------------------------------------- 1 | # Highlight template 2 | The diagram component provides built-in support of mouse over visual feedback over nodes. Minimized nodes have no HTML content, so the only way to create highlight is to have a custom HTML element dedicated to the mouse over visual feedback. 3 | The default highlight template is `div`, having a solid `1px` borderline around. 4 | 5 | ## Properties: 6 | The component customizes the visual representation of items with `templates`. Every template has customization properties for item content, cursor, and highlight visualizations. By default, if properties are nulls, then the component uses built-in default functionality. The following properties customize highlight template: 7 | * `highlightPadding` - reserves space around item, for example: `{left: 3, top: 3, right: 50, bottom: 3}` will provide extra 50 pixel on right side of item for highlight content. 8 | * `highlightBorderWidth` is a legacy property. We use it to align highlight position around item properly. 9 | * `onHighlightRender` - callback method to render highlight content for given item of diagram 10 | 11 | See the Item template for more details. 12 | 13 | ```JavaScript 14 | import { OrgDiagram } from basicprimitivesreact; 15 | { 22 | return
; 23 | } 24 | }] 25 | } 26 | /> 27 | ``` 28 | 29 | ## Custom highlight template border 30 | The following example demonstrates how to create a custom highlight border having item dependent color and tag element. 31 | 32 | [React](../src/Samples/HighlightTemplate.jsx) -------------------------------------------------------------------------------- /src/Diagrams/Templates/DotHighlightTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | 4 | class DotHighlightTemplate extends AbstractTemplate { 5 | constructor(options, itemTemplateConfig) { 6 | super(); 7 | this.render = this.render.bind(this); 8 | 9 | this.config = itemTemplateConfig; 10 | 11 | this.style = { 12 | position: "absolute", 13 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 14 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 15 | WebkitUserSelect: "none", 16 | WebkitTouchCallout: "none", 17 | KhtmlUserSelect: "none", 18 | MozUserSelect: "none", 19 | msUserSelect: "none", 20 | userSelect: "none", 21 | boxSizing: "content-box", 22 | 23 | border: "1px solid #fbcb09", 24 | background: "#fdf5ce", 25 | color: "#c77405", 26 | width: "100%", 27 | height: "100%", 28 | left: "0px", 29 | top: "0px" 30 | } 31 | } 32 | 33 | render(data) { 34 | const { minimizedItemCornerRadius, highlightPadding, highlightBorderWidth, minimizedItemSize } = this.config; 35 | let radius = 0; 36 | if (minimizedItemCornerRadius === null) { 37 | radius = Math.max(minimizedItemSize.width / 2, minimizedItemSize.height / 2) + highlightPadding.left; 38 | } else { 39 | radius = minimizedItemCornerRadius + highlightPadding.left; 40 | } 41 | const style = { 42 | ...this.style, 43 | borderWidth: highlightBorderWidth + "px", 44 | left: - highlightBorderWidth + "px", 45 | top: - highlightBorderWidth + "px", 46 | MozBorderRadius: radius + "px", 47 | WebkitBorderRadius: radius + "px", 48 | KhtmlBorderRadius: radius + "px", 49 | borderRadius: radius + "px" 50 | } 51 | return
; 52 | } 53 | }; 54 | 55 | export default DotHighlightTemplate; -------------------------------------------------------------------------------- /docs/SelectedItems.md: -------------------------------------------------------------------------------- 1 | # Selected items & Checkboxes 2 | Selected items property is the standard UI control option for multi-select of items in collection control. The selected items property is a collection of item ids: 3 | 4 | ```JavaScript 5 | import { OrgDiagram } from basicprimitivesreact; 6 | 10 | ``` 11 | 12 | The control displays selected items with checked checkboxes and in the full-size form. So this can be convenient for navigation. Users can browse diagram and checkmark nodes, so they stay pinned and visible. 13 | 14 | Use selected items property when you need to display cross-functional working groups in the organization hierarchy or visualize extensive mail recipients list in the organization by groups and seniority. Usually, when the number of people exceeds ten, it is hard to say who is in the subscription except for active participants. 15 | 16 | Collection of `OrgConfig.selectedItems` contains item id's. The control notifies about changes in this collection with `OrgConfig.onSelectionChanged` event. 17 | 18 | The following example demonstrates how to programmatically select items in the organizational chart and be notified about selection changes. 19 | 20 | [React](../src/Samples/SelectedItems.jsx) 21 | 22 | # Showing selected items on the frame 23 | 24 | The control displays selected items invisible in the current viewport on the control's frame as markers. The control uses the same marker properties defined by item templates for markers in the diagram. Marker's placement on the frame indicates the direction towards the selected item outside the control view area. The frame takes as much space as the largest marker in the diagram. Use `fameInnerPadding` and `frameOuterPadding` configuration properties to add extra padding around frame markers. 25 | 26 | [React](../src/Samples/ShowFrame.jsx) -------------------------------------------------------------------------------- /src/Samples/SkippedLevels.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | hasSelectorCheckbox: Enabled.True, 11 | items: [ 12 | { 13 | id: 0, 14 | parent: null, 15 | title: "James Smith", 16 | description: "VP, Public Sector", 17 | image: "./photos/a.png" 18 | }, 19 | { 20 | id: 1, 21 | parent: 0, 22 | title: "Ted Lucas", 23 | description: "VP, Human Resources", 24 | image: "./photos/b.png" 25 | }, 26 | { 27 | id: 4, 28 | parent: 1, 29 | title: "Ted Lucas 2", 30 | description: "VP, Human Resources", 31 | image: "./photos/b.png" 32 | }, 33 | { 34 | id: 5, 35 | parent: 1, 36 | title: "Ted Lucas 3", 37 | description: "VP, Human Resources", 38 | image: "./photos/b.png" 39 | }, 40 | { id: 2, parent: 0, isVisible: false }, 41 | { 42 | id: 3, 43 | parent: 2, 44 | title: "Fritz Stuger", 45 | description: "Business Solutions, US", 46 | image: "./photos/c.png" 47 | }, 48 | { id: 6, parent: null, isVisible: false }, 49 | { id: 7, parent: 6, isVisible: false }, 50 | { 51 | id: 8, 52 | parent: 7, 53 | title: "Fritz Stuger 2", 54 | description: "Business Solutions, US", 55 | image: "./photos/c.png" 56 | } 57 | ] 58 | }; 59 | 60 | return
61 | 62 |
63 | } 64 | } 65 | 66 | export default Sample; 67 | -------------------------------------------------------------------------------- /docs/FamilyChartItemsOrdering.md: -------------------------------------------------------------------------------- 1 | # Family Items Ordering 2 | The family diagram supports multiple parents and children per node, so there is no way to define groups of items and their order in the group. The family diagram provides API options helping to guide the layout engine to order nodes. That means if items expected to be in one layout group, then the developer can use the following properties to guide the layout engine about user preferred relative order of items: 3 | 4 | * `relativeItem` - item position and placement type defined relative to the node referenced by this property 5 | * `placementType` - this property defines placement on the left or right side of the relative item. The property has the following values: 6 | * `AdviserPlacementType.Left` 7 | * `AdviserPlacementType.Right` 8 | * `position` - if several items reference the same relative item and relative offset, then this property defines the order of them. 9 | 10 | If the item has no relative node defined, then the layout engine will try to find optimal placement based on its relations. 11 | 12 | Please, pay attention that control ignores looped references between nodes, so it is the developer's responsibility to avoid them. 13 | 14 | ## Family Items Ordering Sample 15 | 16 | [React](../src/Samples/FamilyChartItemsOrdering.jsx) 17 | 18 | ## Multiple Families Ordering Sample 19 | 20 | [React](../src/Samples/MultipleFamiliesOrdering.jsx) 21 | 22 | ## Primary Parent 23 | 24 | If a node has multiple parents and belongs to distant branches of the diagram, then the `primaryParent` property can give higher priority to one of them. So the control would place the node to the hierarchy of that primary parent. 25 | 26 | * `primaryParent` - defines the primary parent of the node. The control would place the node into the hierarchy of the primary node. If there is no parent found, then the layout engine will ignore it. 27 | 28 | [React](../src/Samples/FamilyChartPrimaryParent.jsx) -------------------------------------------------------------------------------- /docs/InactiveItems.md: -------------------------------------------------------------------------------- 1 | # Inactive family items 2 | Inactive items are regular items excluded from the navigation. End-user cannot click on them and move the cursor to them. In the auto fit mode, selecting the neighboring node next to the inactive item makes all nodes of the inactive item visible. The inactive items play a role in layout annotations having no user interaction and templated with HTML the same way as other diagram nodes. For example, we can add titles into family diagram layout to separate groups of nodes. We can add inactive terminator items indicating that the diagram would load extra nodes into the structure upon reaching them. 3 | 4 | Control provides two ways to make nodes inactive. The first one is to set the `isActive` property of the node to false. The other one is more generic. It is where we make all nodes having some particular template inactive. An adequately structured application should have some item templates, so making stationery items with template options is the most appropriate. 5 | 6 | See following configuration classes: 7 | 8 | * `FamItemConfig` 9 | * `TemplateConfig` 10 | 11 | They have the following option: 12 | 13 | * `isActive` - Setting it to false makes items inactive in diagram layout, so they become irresponsive to mouse and keyboard navigation events. 14 | 15 | If you need to disable controls interactivity, it provides global scope options to disable mouse highlights and cursor navigation. 16 | 17 | ```JavaScript 18 | import { OrgDiagram } from basicprimitivesreact; 19 | 36 | ``` 37 | 38 | [React](../src/Samples/InactiveItems.jsx) 39 | -------------------------------------------------------------------------------- /src/Samples/LimitedPartnerItemType.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, GroupByType, Enabled, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | arrowsDirection: GroupByType.Parents, 12 | hasSelectorCheckbox: Enabled.True, 13 | items: [ 14 | { 15 | id: 0, 16 | parent: null, 17 | title: "James Smith", 18 | description: "VP, Public Sector", 19 | image: "./photos/a.png" 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | title: "Robert Canon", 25 | description: "General Partner", 26 | image: "./photos/z.png", 27 | itemType: ItemType.LimitedPartner, 28 | adviserPlacementType: AdviserPlacementType.Right, 29 | groupTitle: "Partner" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | title: "Ted Lucas", 35 | description: "VP, Human Resources", 36 | image: "./photos/b.png" 37 | }, 38 | { 39 | id: 3, 40 | parent: 2, 41 | title: "Fritz Stuger", 42 | description: "General Partner item", 43 | image: "./photos/z.png", 44 | itemType: ItemType.LimitedPartner, 45 | adviserPlacementType: AdviserPlacementType.Right, 46 | groupTitle: "Partner" 47 | }, 48 | { 49 | id: 4, 50 | parent: 2, 51 | title: "Ted Lucas 2", 52 | description: "VP, Human Resources", 53 | image: "./photos/b.png" 54 | } 55 | ] 56 | }; 57 | 58 | return
59 | 60 |
61 | } 62 | } 63 | 64 | export default Sample; 65 | -------------------------------------------------------------------------------- /src/Samples/HighlightPathAnnotation.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { AnnotationType, Colors, Enabled, LineType, GroupByType, PageFitMode } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | items: [ 9 | { id: 0, parent: null, title: "James Smith", description: "VP, Public Sector", image: "./photos/a.png" }, 10 | { id: 1, parent: 0, title: "Ted Lucas", description: "VP, Human Resources", image: "./photos/b.png" }, 11 | { id: 2, parent: 0, title: "Fritz Stuger", description: "Business Solutions, US", image: "./photos/c.png" }, 12 | { id: 3, parent: 0, title: "Mike Wazowski", description: "Business Analyst, Canada", image: "./photos/o.png" }, 13 | { id: 4, parent: 3, title: "Edward Smith", description: "Field Engineer", image: "./photos/s.png" }, 14 | { id: 5, parent: 1, title: "Doug Parker", description: "Field Engineer", image: "./photos/p.png" } 15 | ], 16 | annotations: [ 17 | { 18 | annotationType: AnnotationType.HighlightPath, 19 | items: [5, 0], 20 | color: Colors.Navy, 21 | lineWidth: 12, 22 | opacity: 0.3, 23 | showArrows: false 24 | }, 25 | { 26 | annotationType: AnnotationType.HighlightPath, 27 | items: [4, 0], 28 | color: Colors.Red, 29 | lineWidth: 2, 30 | opacity: 1, 31 | showArrows: true 32 | } 33 | ], 34 | cursorItem: 0, 35 | hasSelectorCheckbox: Enabled.True, 36 | highlightLinesColor: Colors.Red, 37 | highlightLinesWidth: 1.5, 38 | highlightLinesType: LineType.Dashed, 39 | arrowsDirection: GroupByType.Parents, 40 | pageFitMode: PageFitMode.None 41 | }; 42 | 43 | return
44 | 45 |
46 | } 47 | } 48 | 49 | export default Sample; 50 | -------------------------------------------------------------------------------- /docs/InactiveFamilyItems.md: -------------------------------------------------------------------------------- 1 | # Inactive family items 2 | Inactive items are regular items excluded from the navigation. End-user cannot click on them and move the cursor to them. In the auto fit mode, selecting the neighboring node next to the inactive item makes all nodes of the inactive item visible. The inactive items play a role in layout annotations having no user interaction and templated with HTML the same way as other diagram nodes. For example, we can add titles into family diagram layout to separate groups of nodes. We can add inactive terminator items indicating that the diagram would load extra nodes into the structure upon reaching them. 3 | 4 | Control provides two ways to make nodes inactive. The first one is to set the `isActive` property of the node to false. The other one is more generic. It is where we make all nodes having some particular template inactive. An adequately structured application should have some item templates, so making stationery items with template options is the most appropriate. 5 | 6 | See following configuration classes: 7 | 8 | * `FamItemConfig` 9 | * `TemplateConfig` 10 | 11 | They have the following option: 12 | 13 | * `isActive` - Setting it to false makes items inactive in diagram layout, so they become irresponsive to mouse and keyboard navigation events. 14 | 15 | If you need to disable controls interactivity, it provides global scope options to disable mouse highlights and cursor navigation. 16 | 17 | ```JavaScript 18 | import { FamDiagram } from basicprimitivesreact; 19 | 36 | ``` 37 | 38 | [React](../src/Samples/InactiveFamilyItems.jsx) 39 | -------------------------------------------------------------------------------- /docs/HighlightPathAnnotation.md: -------------------------------------------------------------------------------- 1 | # Highlight Path Annotation 2 | 3 | Highlight Path annotation highlights connections between a group of items in the hierarchy with distinct line properties. The highlight path annotation supports only one type of highlighting, use the following configuration object properties to style it: `highlightLinesColor`, `highlightLinesWidth`, `highlightLinesType`. 4 | 5 | See family diagram and organizational chart partners demos for more use usage scenarios. 6 | 7 | ```JavaScript 8 | import { OrgDiagram } from basicprimitivesreact; 9 | import { LineType, Colors } from basicprimitives; 10 | 16 | ``` 17 | [React](../src/Samples/HighlightPathAnnotation.jsx) 18 | 19 | ## PERT - Program evaluation and review technique chart & Critical Path Visualization 20 | PERT chart critical path visualization with Highlight Path Annotation. The chart does not provide any means to find a critical path. It is the developer's responsibility to trace items and visualize critical path with Highlight Path Annotation. The application should sequence the Critical path nodes and set them in the annotation. 21 | 22 | [React](../src/Samples/PERTChart.jsx) 23 | 24 | ## Routing Highlight Path Annotation for hidden grandparents connections 25 | 26 | The connection lines are hard to trace on the screen visually. They make sense for traditional paper form diagrams visualization when we have no interactivity. But they bring no value for interactive applications, so the main point is to remove redundant connection lines giving no visual value and replace them with dynamic highlight path annotations and custom node templates for compensation. 27 | 28 | ```JavaScript 29 | import { FamDiagram } from basicprimitivesreact; 30 | 34 | ``` 35 | 36 | [React](../src/Samples/FamilyHideGrandParentsConnections.jsx) -------------------------------------------------------------------------------- /src/Samples/ButtonsPanel.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 3 | import { faCoffee, faSitemap } from '@fortawesome/free-solid-svg-icons' 4 | import { OrgDiagram } from '../Diagrams'; 5 | import { Enabled } from 'basicprimitives'; 6 | 7 | class Sample extends Component { 8 | render() { 9 | const config = { 10 | cursorItem: 0, 11 | highlightItem: 0, 12 | items: [ 13 | { 14 | id: 0, 15 | parent: null, 16 | title: "James Smith", 17 | description: "VP, Public Sector", 18 | image: "./photos/a.png" 19 | }, 20 | { 21 | id: 1, 22 | parent: 0, 23 | title: "Ted Lucas", 24 | description: "VP, Human Resources", 25 | image: "./photos/b.png" 26 | }, 27 | { 28 | id: 2, 29 | parent: 0, 30 | title: "Fritz Stuger", 31 | description: "Business Solutions, US", 32 | image: "./photos/c.png" 33 | } 34 | ], 35 | hasSelectorCheckbox: Enabled.True, 36 | hasButtons: Enabled.True, 37 | buttonsPanelSize: 40, 38 | onButtonsRender: (({ context: itemConfig }) => { 39 | return <> 40 | 47 | 54 | 55 | }) 56 | }; 57 | 58 | return
59 | 60 |
61 | } 62 | } 63 | 64 | export default Sample; 65 | -------------------------------------------------------------------------------- /src/Samples/FirstFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, GroupByType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 2, 10 | linesWidth: 1, 11 | linesColor: "black", 12 | hasSelectorCheckbox: Enabled.True, 13 | normalLevelShift: 20, 14 | dotLevelShift: 20, 15 | lineLevelShift: 20, 16 | normalItemsInterval: 10, 17 | dotItemsInterval: 30, 18 | lineItemsInterval: 30, 19 | arrowsDirection: GroupByType.Parents, 20 | showExtraArrows: false, 21 | items: [ 22 | { id: 1, title: "Thomas Williams", label: "Thomas Williams", description: "1st husband", image: "./photos/t.png" }, 23 | { id: 2, title: "Mary Spencer", label: "Mary Spencer", description: "The Mary",image: "./photos/m.png" }, 24 | { id: 3, title: "David Kirby", label: "David Kirby", description: "2nd Husband", image: "./photos/d.png" }, 25 | { id: 4, parents: [1, 2], title: "Brad Williams", label: "Brad Williams", description: "1st son", image: "./photos/b.png" }, 26 | { id: 5, parents: [2, 3], title: "Mike Kirby", label: "Mike Kirby", description: "2nd son, having 2 spouses", image: "./photos/m.png"}, 27 | { id: 6, title: "Lynette Maloney", label: "Lynette Maloney", description: "Spouse I", image: "./photos/m.png" }, 28 | { id: 11, parents: [5, 6], title: "Steven Powell", label: "Steven Powell", description: "1st son", image: "./photos/s.png" }, 29 | { id: 7, title: "Sara Kemp", label: "Sara Kemp", description: "Spouse II", image: "./photos/s.png" }, 30 | { id: 12, parents: [5, 7], title: "John Smith", label: "John Smith", description: "2ns son", image: "./photos/j.png" }, 31 | { id: 8, parents: [7], title: "Leon Kemp", label: "Leon Kemp", description: "", image: "./photos/l.png" } 32 | ] 33 | }; 34 | 35 | return
36 | 37 |
38 | } 39 | } 40 | 41 | export default Sample; 42 | -------------------------------------------------------------------------------- /docs/CustomLayoutWithInvisibleItems.md: -------------------------------------------------------------------------------- 1 | # Custom layout with Invisible items 2 | The logical parent-children relations between items define chart navigation. When we select the new cursor item, all its children and parents become visible in the diagram so that the user can continue navigation further down or up in the hierarchy. When children or parents are invisible, we display children of children and parents of parents. This feature, combined with custom item types and children layouts, provides excellent flexibility for the custom children's configurations. 3 | 4 | The following example demonstrates how to use invisible items to arrange children attached to one parent item into multiple groups. It has two rows of assistants and two levels of children. To implement this layout, we create two hidden nodes of the regular item type. Set the `isVisible` property of the item configuration object to false to make the node invisible. Children of invisible items logically belong to their parents. In our case, it is the organizational chart's root item. When we set the cursor to the root node, all items in the diagram become logical children of the root node and visible. So the end-user may move the cursor to them directly. 5 | 6 | ```JavaScript 7 | import { OrgDiagram } from basicprimitivesreact; 8 | 21 | ``` 22 | 23 | See custom item types, children layout, and inactive items samples as well. 24 | 25 | [React](../src/Samples/CustomLayoutWithInvisibleItems.jsx) 26 | 27 | ## Skipped Levels 28 | 29 | We can use hidden items to skip levels in the organizational chart as well. The hidden nodes occupy space, so we can use them to shift child items down one row relative to their parents. 30 | 31 | Use the `levelOffset` option of the item configuration object to arrange regular children into rows. 32 | 33 | [React](../src/Samples/SkippedLevels.jsx) 34 | 35 | -------------------------------------------------------------------------------- /docs/PartnerItemTypes.md: -------------------------------------------------------------------------------- 1 | # Partner Item Types 2 | In the organizational structures, we have many exceptions from traditional tree structure visualization. The partner item types allow elevating child nodes to the level of their parents. So visually, they non-distinguishable from their parents and displayed side by side with them. For example, we can use partner item types when we need to show co-CEOs. We add our second CEO item as a child item of the first CEO and set its item type to Partner. Partner item is placed at the same level as its logical parent and shares its parent's children, so visually, children equally belong to both of them. Optionally the parent node and its partner child have a horizontal line connecting them visually into one group. 3 | 4 | Chart supports three types of partner types: 5 | 6 | * GeneralPartner 7 | * LimitedPartner 8 | * AdviserPartner 9 | 10 | They all share their parents' children, and the difference is in how they connect to their logical parent. The following example shows GeneralPartner, which shares children and parents of the logical parent, so visually, they are 100% equal on the org chart. 11 | 12 | For the sake of simplicity, the Partner node can not have its logical children by design, so if you add children to the partner node, they would be auto-converted into partner's assistants. 13 | 14 | Use ItemConfig.adviserPlacementType option to place partners to the left or right side of their logical parent; 15 | 16 | ## General Partner 17 | 18 | [React](../src/Samples/GeneralPartnerItemType.jsx) 19 | 20 | ## Limited Partner 21 | 22 | Limited Partner is a variation of GeneralPartner having no connection to the parent of the logical parent. Limited Partner shares children of the logical parent, but it does not share its parents. 23 | 24 | [React](../src/Samples/LimitedPartnerItemType.jsx) 25 | 26 | ## Adviser Partner 27 | 28 | Adviser Partner item type is a combination of Partner and Adviser types. It has an in-row horizontal connection to its logical parent, it shares children of the logical parent, but it has no links to its parents. 29 | 30 | [React](../src/Samples/AdviserPartnerItemType.jsx) -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # How To Use 2 | ## Create & Update Use Cases 3 | * [First Organizational Chart](FirstOrganizationalChart.md) 4 | * [First Family Chart](FirstFamilyChart.md) 5 | * [Adding new items to chart at runtime](AddingNewItemsToChartAtRuntime.md) 6 | * [Diagram Sizing](DiagramSizing.md) 7 | 8 | ## User Interface Events & Options 9 | * [Selecting cursor item & Mouse click](SelectingCursorItem.md) 10 | * [Selecting highlight item & Mouse over](SelectingHighlightItem.md) 11 | * [Selected items & Check boxes](SelectedItems.md) 12 | * [Buttons Panel](Buttons.md) 13 | * [Item & Group title colors](ItemAndGroupTitleColors.md) 14 | * [Labels](Labels.md) 15 | 16 | ## Integration 17 | * [Drag & Drop](DragNDrop.md) 18 | 19 | ## Organizational Chart Layout Options 20 | * [Regular Children Layout](ChildrenLayout.md) 21 | * [Cross-Branch Alignment](CrossBranchAlignment.md) 22 | * [Adviser & Assistant item types](AdviserAndAssistantItemTypes.md) 23 | * [Partner item types](PartnerItemTypes.md) 24 | * [Multiple root items](MultipleRootItemsInChart.md) 25 | * [Selection path mode](SelectionPathMode.md) 26 | * [Inactive items in layout](InactiveItems.md) 27 | * [Custom layout using invisible items](CustomLayoutWithInvisibleItems.md) 28 | 29 | ## Family Diagram Layout Use Cases 30 | * [Connectors Visualization](FamilyConnectorsVisualization.md) 31 | * [Matrix Layout](MatrixLayoutInFamilyChart.md) 32 | * [Inactive family items](InactiveFamilyItems.md) 33 | * [Family Items Ordering](FamilyChartItemsOrdering.md) 34 | * [Loops Layout Optimization](LoopsInFamily.md) 35 | * [Selection path mode](SelectionPathModeInFamilyChart.md) 36 | 37 | ## Item Template Use Cases 38 | * [Item Template](ItemTemplates.md) 39 | * [Zoom Using Item templates](ZoomWithItemTemplate.md) 40 | * [Zoom Using CSS Scale Transform](ZoomWithCSSScaleTransform.md) 41 | * [Cursor Template](CursorTemplate.md) 42 | * [Highlight Template](HighlightTemplate.md) 43 | 44 | ## Annotations 45 | * [On-screen Connector Annotation](ConnectorAnnotation.md) 46 | * [Shape & Background Annotations](ShapeAndBackgroundAnnotations.md) 47 | * [Level Annotation](LevelAnnotation.md) 48 | * [Highlight Path Annotation](HighlightPathAnnotation.md) 49 | * [Labels cascades in Family Chart](LabelsCascadesInFamilyChart.md) -------------------------------------------------------------------------------- /docs/AddingNewItemsToChartAtRuntime.md: -------------------------------------------------------------------------------- 1 | # Updating configuration properties in ReactJS diagrams 2 | 3 | We do fast updates, so we don't flush and recreate diagrams for every configuration object change. We reuse transformations and visual elements if they are not affected by configuration changes. For example, to render on-screen annotations, the control does not need to layout nodes, so when we add new on-screen annotations, we show them instantly. There are some exceptions when annotations may affect nodes layout, see reference for more details. The same applies to all other visual elements. The component updates the diagram by comparing the current copy of the configuration object with the new one. If any changes are detected, the control triggers the rendering cycle for the affected visual elements only. 4 | 5 | The rendering engine does not track individual items. If we make changes to one of the nodes layout options, then the whole collection of nodes is considered as changed. As a result, the component will re-render the entire diagram. So there is no difference if you change the entire array of nodes or just a single one. The result is the same, we layout nodes and render the whole collection of items again. 6 | 7 | The component ignores changes in node options, not affecting the diagram layout. In other words, if an application changes the `title`, `description`, or `image` item properties, then the component would not trigger layout changes. 8 | 9 | ## The following example demonstrates adding new items to an organizational chart using component state: 10 | 11 | The application stores the collection of items in the application component: `state`. The diagram component updates its layout every time it gets a new configuration object reference. The diagram keeps the internal state between rendering cycles and tracks property changes for individual configuration objects. The configuration object properties may affect different visuals and transformations of the graph. The component tracks every property individually and optimizes rendering time. For the more complex implementation of chart editing functionality, see the matrixed organizational chart structure editor demo at this site. 12 | 13 | [React](../src/Samples/AddingNewItemsToChartAtRuntime.jsx) 14 | -------------------------------------------------------------------------------- /src/Samples/CursorTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.FitToPage, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | hasSelectorCheckbox: Enabled.True, 12 | defaultTemplateName: "contactTemplate", 13 | templates: [{ 14 | name: "contactTemplate", 15 | itemSize: { width: 120, height: 100 }, 16 | minimizedItemSize: { width: 3, height: 3 }, 17 | cursorPadding: { left: 3, top: 3, right: 3, bottom: 3 }, 18 | cursorBorderWidth: 2, 19 | highlightPadding: { left: 4, top: 4, right: 4, bottom: 4 }, 20 | onCursorRender: ({ context: itemConfig }) => { 21 | return
22 |
{itemConfig.badge}
23 |
; 24 | } 25 | }], 26 | items: [ 27 | { 28 | id: 0, 29 | parent: null, 30 | title: "James Smith", 31 | description: "VP, Public Sector", 32 | image: "./photos/a.png", 33 | phone: "(123) 456-78-90", 34 | email: "itema@org.com", 35 | badge: "1", 36 | badgeColor: "blue" 37 | }, 38 | { 39 | id: 1, 40 | parent: 0, 41 | title: "Ted Lucas", 42 | description: "VP, Human Resources", 43 | image: "./photos/b.png", 44 | badge: "2", 45 | badgeColor: "red" 46 | }, 47 | { 48 | id: 2, 49 | parent: 0, 50 | title: "Fritz Stuger", 51 | phone: "(123) 654-78-90", 52 | email: "itemc@org.com", 53 | description: "Business Solutions, US", 54 | image: "./photos/c.png", 55 | badge: "3", 56 | badgeColor: "green" 57 | } 58 | ] 59 | }; 60 | 61 | return
62 | 64 |
65 | } 66 | } 67 | 68 | export default Sample; 69 | -------------------------------------------------------------------------------- /docs/DragNDrop.md: -------------------------------------------------------------------------------- 1 | # Drag & Drop Support 2 | 3 | Our React diagramming components library is compliant with [React Context](https://reactjs.org/docs/context.html) and [React Drag & Drop](http://react-dnd.github.io/react-dnd/about). The only option to achieve this is to render all content using React Virtual DOM without direct DOM manipulations. 4 | 5 | In the best software engineering traditions, our library does not implement any Drag & Drop related functionally. Everything as before is achieved via item templates customizations. 6 | 7 | See [React Drag & Drop](http://react-dnd.github.io/react-dnd/about) library for reference and samples 8 | 9 | ## npm packages 10 | * [react-dnd](https://www.npmjs.com/package/react-dnd) 11 | * [react-dnd-cjs](https://www.npmjs.com/package/react-dnd-html5-backend) 12 | 13 | ## React `DndProvider` does not support nested `backend`s 14 | If you use Drag and Drop in multiple places of your application, be aware that `DndProvider` does not support nested backends. 15 | 16 | ```JavaScript 17 | import React, { Component } from 'react'; 18 | import { App } from './App'; 19 | import { DndProvider } from 'react-dnd'; 20 | import HTML5Backend from 'react-dnd-html5-backend'; 21 | 22 | class Sample extends Component { 23 | render() { 24 | return <> 25 | 26 | 27 | 28 | ; 29 | } 30 | } 31 | 32 | export default Sample; 33 | ``` 34 | 35 | ## Drag & Drop diagram nodes 36 | This demo showcases the drag-and-drop functionality for diagram nodes using **React DnD** hooks. You can easily drag nodes within the diagram and drop them to interact with other nodes. 37 | 38 | * [useDrag](https://react-dnd.github.io/react-dnd/docs/api/use-drag) – Hook to manage the drag state. 39 | * [useDrop](https://react-dnd.github.io/react-dnd/docs/api/use-drop) – Hook to manage the drop state. 40 | 41 | The following example demonstrates how to drag and drop diagram nodes using React hooks: 42 | 43 | [React](../src/Samples/DragNDropHooks.jsx) 44 | 45 | ## Drag & Drop diagram nodes to other components 46 | This example demonstrates dragging diagram nodes and interacting with other components outside the diagram: 47 | 48 | [React](../src/Samples/DragToTrashBinHooks.jsx) 49 | -------------------------------------------------------------------------------- /src/Samples/HighlightTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.FitToPage, 9 | cursorItem: 0, 10 | hasSelectorCheckbox: Enabled.True, 11 | defaultTemplateName: "contactTemplate", 12 | templates: [{ 13 | name: "contactTemplate", 14 | itemSize: { width: 120, height: 100 }, 15 | minimizedItemSize: { width: 3, height: 3 }, 16 | highlightPadding: { left: 4, top: 4, right: 4, bottom: 4 }, 17 | highlightBorderWidth: 2, 18 | onHighlightRender: ({ context: itemConfig }) => { 19 | return
20 |
21 |
22 | {itemConfig.badge} 23 |
24 |
25 |
; 26 | } 27 | }], 28 | items: [ 29 | { 30 | id: 0, 31 | parent: null, 32 | title: "James Smith", 33 | description: "VP, Public Sector", 34 | image: "./photos/a.png", 35 | phone: "(123) 456-78-90", 36 | email: "itema@org.com", 37 | badge: "1", 38 | badgeColor: "blue" 39 | }, 40 | { 41 | id: 1, 42 | parent: 0, 43 | title: "Ted Lucas", 44 | description: "VP, Human Resources", 45 | image: "./photos/b.png", 46 | badge: "2", 47 | badgeColor: "red" 48 | }, 49 | { 50 | id: 2, 51 | parent: 0, 52 | title: "Fritz Stuger", 53 | phone: "(123) 654-78-90", 54 | email: "itemc@org.com", 55 | description: "Business Solutions, US", 56 | image: "./photos/c.png", 57 | badge: "3", 58 | badgeColor: "green" 59 | } 60 | ] 61 | }; 62 | 63 | return
64 | 66 |
67 | } 68 | } 69 | 70 | export default Sample; 71 | -------------------------------------------------------------------------------- /src/Samples/AdviserPartnerItemType.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { AdviserPlacementType, ItemType, Enabled, GroupByType, PageFitMode } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | arrowsDirection: GroupByType.Parents, 12 | hasSelectorCheckbox: Enabled.True, 13 | items: [ 14 | { 15 | id: 0, 16 | parent: null, 17 | title: "James Smith", 18 | description: "VP, Public Sector", 19 | image: "./photos/a.png" 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | title: "Robert Canon", 25 | description: "Adviser Partner", 26 | image: "./photos/z.png", 27 | itemType: ItemType.AdviserPartner, 28 | adviserPlacementType: AdviserPlacementType.Right, 29 | groupTitle: "Partner" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | title: "Fritz Stuger", 35 | description: "Adviser Partner item", 36 | image: "./photos/y.png", 37 | itemType: ItemType.AdviserPartner, 38 | adviserPlacementType: AdviserPlacementType.Left, 39 | groupTitle: "Partner" 40 | }, 41 | { 42 | id: 3, 43 | parent: 0, 44 | title: "Ted Lucas", 45 | description: "VP, Human Resources", 46 | image: "./photos/b.png" 47 | }, 48 | { 49 | id: 4, 50 | parent: 3, 51 | title: "Robert Canon 2", 52 | description: "Adviser Partner item", 53 | image: "./photos/z.png", 54 | itemType: ItemType.AdviserPartner, 55 | adviserPlacementType: AdviserPlacementType.Right, 56 | groupTitle: "Partner" 57 | }, 58 | { 59 | id: 5, 60 | parent: 3, 61 | title: "Ted Lucas 2", 62 | description: "VP, Human Resources", 63 | image: "./photos/b.png" 64 | } 65 | ] 66 | }; 67 | 68 | return
69 | 70 |
71 | } 72 | } 73 | 74 | export default Sample; 75 | -------------------------------------------------------------------------------- /src/Samples/GeneralPartnerItemType.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, GroupByType, Enabled, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | highlightItem: 0, 11 | arrowsDirection: GroupByType.Parents, 12 | hasSelectorCheckbox: Enabled.True, 13 | items: [ 14 | { 15 | id: 0, 16 | parent: null, 17 | title: "James Smith", 18 | description: "VP, Public Sector", 19 | image: "./photos/a.png" 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | title: "Robert Canon", 25 | description: "General Partner", 26 | image: "./photos/z.png", 27 | itemType: ItemType.GeneralPartner, 28 | adviserPlacementType: AdviserPlacementType.Right, 29 | groupTitle: "Partner" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | title: "Fritz Stuger", 35 | description: "General Partner item", 36 | image: "./photos/y.png", 37 | itemType: ItemType.GeneralPartner, 38 | adviserPlacementType: AdviserPlacementType.Left, 39 | groupTitle: "Partner" 40 | }, 41 | { 42 | id: 3, 43 | parent: 0, 44 | title: "Ted Lucas", 45 | description: "VP, Human Resources", 46 | image: "./photos/b.png" 47 | }, 48 | { 49 | id: 4, 50 | parent: 3, 51 | title: "Robert Canon 2", 52 | description: "General Partner item", 53 | image: "./photos/z.png", 54 | itemType: ItemType.GeneralPartner, 55 | adviserPlacementType: AdviserPlacementType.Right, 56 | groupTitle: "Partner" 57 | }, 58 | { 59 | id: 5, 60 | parent: 3, 61 | title: "Ted Lucas 2", 62 | description: "VP, Human Resources", 63 | image: "./photos/b.png" 64 | } 65 | ] 66 | }; 67 | 68 | return
69 | 70 |
71 | } 72 | } 73 | 74 | export default Sample; 75 | -------------------------------------------------------------------------------- /src/Samples/MatrixGroupsInFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { Enabled, GroupByType, PageFitMode } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | cursorItem: 1, 9 | enableMatrixLayout: true, 10 | minimumMatrixSize: 3, 11 | hasSelectorCheckbox: Enabled.False, 12 | arrowsDirection: GroupByType.Children, 13 | pageFitMode: PageFitMode.None, 14 | items: [ 15 | { id: 1, parents: [], title: "1", label: "1", description: "", image: "./photos/z.png", itemTitleColor: "#ff0000" }, 16 | { id: 2, parents: [1, 101, 102, 103], title: "2", label: "2", description: "", image: "./photos/a.png", itemTitleColor: "#ff0000" }, 17 | { id: 3, parents: [1, 101, 102, 103], title: "3", label: "3", description: "", image: "./photos/b.png", itemTitleColor: "#ff0000" }, 18 | { id: 4, parents: [1, 101, 102, 103], title: "4", label: "4", description: "", image: "./photos/c.png", itemTitleColor: "#ff0000" }, 19 | { id: 5, parents: [1, 101, 102, 103], title: "5", label: "5", description: "", image: "./photos/c.png", itemTitleColor: "#ff0000" }, 20 | { id: 6, parents: [1, 101, 102, 103], matrixId: "2", title: "6", label: "6", description: "", image: "./photos/e.png", itemTitleColor: "#ff0000" }, 21 | { id: 7, parents: [1, 101, 102, 103], matrixId: "2", title: "7", label: "7", description: "", image: "./photos/f.png", itemTitleColor: "#ff0000" }, 22 | { id: 8, parents: [1, 101, 102, 103], matrixId: "2", title: "8", label: "8", description: "", image: "./photos/g.png", itemTitleColor: "#ff0000" }, 23 | { id: 9, parents: [1, 101, 102, 103], matrixId: "2", title: "10", label: "10", description: "", image: "./photos/i.png", itemTitleColor: "#ff0000" }, 24 | { id: 10, parents: [1, 101, 102, 103], addToMatrix: false, title: "10", label: "10", description: "", image: "./photos/i.png", itemTitleColor: "#ff0000" }, 25 | { id: 33, parents: [2, 3, 4, 5, 6, 7, 8, 9, 10], title: "33", label: "33", description: "", image: "./photos/m.png", itemTitleColor: "#4b0082" } 26 | ] 27 | }; 28 | 29 | return
30 | 31 |
32 | } 33 | } 34 | 35 | export default Sample; 36 | -------------------------------------------------------------------------------- /src/Samples/SelectingCursorItem.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor() { 7 | super(); 8 | 9 | this.onCursorChanged = this.onCursorChanged.bind(this); 10 | 11 | this.state = { 12 | config: { 13 | pageFitMode: PageFitMode.FitToPage, 14 | cursorItem: 0, 15 | highlightItem: 0, 16 | hasSelectorCheckbox: Enabled.False, 17 | items: [ 18 | { 19 | id: 0, 20 | parent: null, 21 | title: 'James Smith', 22 | description: 'VP, Public Sector', 23 | image: './photos/a.png' 24 | }, 25 | { 26 | id: 1, 27 | parent: 0, 28 | title: 'Ted Lucas', 29 | description: 'VP, Human Resources', 30 | image: './photos/b.png' 31 | }, 32 | { 33 | id: 2, 34 | parent: 0, 35 | title: 'Fritz Stuger', 36 | description: 'Business Solutions, US', 37 | image: './photos/c.png' 38 | } 39 | ] 40 | }, 41 | title: null 42 | } 43 | } 44 | 45 | onCursorChanged(event, data) { 46 | const { context: item } = data; 47 | if (item != null) { 48 | this.setState({ 49 | title: item.title 50 | }) 51 | } 52 | }; 53 | 54 | update(itemid) { 55 | const { config } = this.state; 56 | this.setState({ 57 | title: null, 58 | config: { 59 | ...config, 60 | cursorItem: itemid 61 | } 62 | }) 63 | } 64 | 65 | 66 | render() { 67 | const { config, title } = this.state; 68 | 69 | return <> 70 |

Set cursor to:   71 |   72 |   73 |   74 | 75 |

76 |
77 | 78 |
79 | {title != null ?

User set cursor to item: {title}

: null} 80 | ; 81 | } 82 | } 83 | 84 | export default Sample; 85 | -------------------------------------------------------------------------------- /src/Samples/SelectingHighlightItem.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor() { 7 | super(); 8 | 9 | this.onHighlightChanged = this.onHighlightChanged.bind(this); 10 | 11 | this.state = { 12 | config: { 13 | pageFitMode: PageFitMode.FitToPage, 14 | cursorItem: 0, 15 | highlightItem: 0, 16 | hasSelectorCheckbox: Enabled.False, 17 | items: [ 18 | { 19 | id: 0, 20 | parent: null, 21 | title: 'James Smith', 22 | description: 'VP, Public Sector', 23 | image: './photos/a.png' 24 | }, 25 | { 26 | id: 1, 27 | parent: 0, 28 | title: 'Ted Lucas', 29 | description: 'VP, Human Resources', 30 | image: './photos/b.png' 31 | }, 32 | { 33 | id: 2, 34 | parent: 0, 35 | title: 'Fritz Stuger', 36 | description: 'Business Solutions, US', 37 | image: './photos/c.png' 38 | } 39 | ] 40 | }, 41 | title: null 42 | } 43 | } 44 | 45 | onHighlightChanged(event, data) { 46 | const { context: item } = data; 47 | if (item != null) { 48 | this.setState({ 49 | title: item.title 50 | }) 51 | } 52 | }; 53 | 54 | update(itemid) { 55 | const { config } = this.state; 56 | this.setState({ 57 | title: null, 58 | config: { 59 | ...config, 60 | highlightItem: itemid 61 | } 62 | }) 63 | } 64 | 65 | 66 | render() { 67 | const { config, title } = this.state; 68 | 69 | return <> 70 |

Set highlight for:   71 |   72 |   73 |   74 | 75 |

76 |
77 | 78 |
79 | {title != null ?

User hovers mouse over item {title}

: null} 80 | ; 81 | } 82 | } 83 | 84 | export default Sample; 85 | -------------------------------------------------------------------------------- /src/Diagrams/Schemas/ShapeAnnotationComponentConfigShape.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { 3 | OrientationType, 4 | LineType, 5 | ShapeType, 6 | PlacementType 7 | } from 'basicprimitives'; // Adjust the import path as needed 8 | 9 | const ThicknessShape = PropTypes.shape({ 10 | left: PropTypes.number.isRequired, 11 | top: PropTypes.number.isRequired, 12 | right: PropTypes.number.isRequired, 13 | bottom: PropTypes.number.isRequired 14 | }); 15 | 16 | const SizeShape = PropTypes.shape({ 17 | width: PropTypes.number.isRequired, 18 | height: PropTypes.number.isRequired 19 | }); 20 | 21 | const RectShape = PropTypes.shape({ 22 | x: PropTypes.number.isRequired, 23 | y: PropTypes.number.isRequired, 24 | width: PropTypes.number.isRequired, 25 | height: PropTypes.number.isRequired 26 | }); 27 | 28 | const ShapeAnnotationControlConfigShape = PropTypes.shape({ 29 | /** 30 | * Diagram orientation. 31 | */ 32 | orientationType: PropTypes.oneOf(Object.values(OrientationType)), 33 | 34 | /** 35 | * Shape type. 36 | */ 37 | shapeType: PropTypes.oneOf(Object.values(ShapeType)), 38 | 39 | /** 40 | * Bounding rectangle. 41 | */ 42 | position: RectShape, 43 | 44 | /** 45 | * Offset of the bounding rectangle. 46 | */ 47 | offset: ThicknessShape, 48 | 49 | /** 50 | * Border line width. 51 | */ 52 | lineWidth: PropTypes.number, 53 | 54 | /** 55 | * Corner radius in % or px. 56 | */ 57 | cornerRadius: PropTypes.oneOfType([ 58 | PropTypes.string, 59 | PropTypes.number 60 | ]), 61 | 62 | /** 63 | * Background color opacity. 64 | */ 65 | opacity: PropTypes.number, 66 | 67 | /** 68 | * Border color. 69 | */ 70 | borderColor: PropTypes.string, 71 | 72 | /** 73 | * Fill color. 74 | */ 75 | fillColor: PropTypes.string, 76 | 77 | /** 78 | * Border line pattern. 79 | */ 80 | lineType: PropTypes.oneOf(Object.values(LineType)), 81 | 82 | /** 83 | * Optional label text. 84 | */ 85 | label: PropTypes.string, 86 | 87 | /** 88 | * Label size. 89 | */ 90 | labelSize: SizeShape, 91 | 92 | /** 93 | * Label placement. 94 | */ 95 | labelPlacement: PropTypes.oneOf(Object.values(PlacementType)), 96 | 97 | /** 98 | * Offset between label and shape. 99 | */ 100 | labelOffset: PropTypes.number 101 | }); 102 | 103 | export default ShapeAnnotationControlConfigShape; 104 | -------------------------------------------------------------------------------- /src/Samples/SelectionPathMode.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { SelectionPathMode, PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | selectionPathMode: (SelectionPathMode.None) 11 | } 12 | } 13 | 14 | onSelectionPathModeChanged(selectionPathMode) { 15 | this.setState({ selectionPathMode }); 16 | } 17 | 18 | render() { 19 | const { selectionPathMode } = this.state; 20 | const config = { 21 | selectionPathMode, 22 | pageFitMode: PageFitMode.Page, 23 | hasSelectorCheckbox: Enabled.True, 24 | selectedItems: [3], 25 | items: [ 26 | { 27 | id: 0, 28 | parent: null, 29 | title: "James Smith", 30 | description: "VP, Public Sector", 31 | image: "./photos/a.png" 32 | }, 33 | { 34 | id: 1, 35 | parent: 0, 36 | title: "Ted Lucas", 37 | description: "VP, Human Resources", 38 | image: "./photos/b.png" 39 | }, 40 | { 41 | id: 2, 42 | parent: 1, 43 | title: "Fritz Stuger", 44 | description: "Business Solutions, US", 45 | image: "./photos/c.png" 46 | }, 47 | { 48 | id: 3, 49 | parent: 2, 50 | title: "Robert Canon", 51 | description: "Business Solutions, Canada", 52 | image: "./photos/z.png" 53 | } 54 | ] 55 | }; 56 | 57 | return <> 58 |

Selection Path Mode: 59 |
60 | 70 |
71 | 81 |

82 |
83 | 84 |
85 | 86 | } 87 | } 88 | 89 | export default Sample; 90 | -------------------------------------------------------------------------------- /src/Samples/SelectionPathModeInFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { SelectionPathMode, PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | selectionPathMode: (SelectionPathMode.None) 11 | } 12 | } 13 | 14 | onSelectionPathModeChanged(selectionPathMode) { 15 | this.setState({ selectionPathMode }); 16 | } 17 | 18 | render() { 19 | const { selectionPathMode } = this.state; 20 | const config = { 21 | selectionPathMode, 22 | pageFitMode: PageFitMode.Page, 23 | hasSelectorCheckbox: Enabled.True, 24 | selectedItems: [3], 25 | items: [ 26 | { 27 | id: 0, 28 | parents: null, 29 | title: "James Smith", 30 | description: "VP, Public Sector", 31 | image: "./photos/a.png" 32 | }, 33 | { 34 | id: 1, 35 | parents: [0], 36 | title: "Ted Lucas", 37 | description: "VP, Human Resources", 38 | image: "./photos/b.png" 39 | }, 40 | { 41 | id: 2, 42 | parents: [1], 43 | title: "Fritz Stuger", 44 | description: "Business Solutions, US", 45 | image: "./photos/c.png" 46 | }, 47 | { 48 | id: 3, 49 | parents: [2], 50 | title: "Robert Canon", 51 | description: "Business Solutions, Canada", 52 | image: "./photos/z.png" 53 | } 54 | ] 55 | }; 56 | 57 | return <> 58 |

Selection Path Mode: 59 |
60 | 70 |
71 | 81 |

82 |
83 | 84 |
85 | 86 | } 87 | } 88 | 89 | export default Sample; 90 | -------------------------------------------------------------------------------- /src/Samples/LevelAnnotation.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { Thickness, LevelAnnotationConfig, AnnotationType, Colors, LineType, GroupByType, PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | items: [ 9 | /* JSON noname objects equivalent to OrgItemConfig */ 10 | { id: 0, parent: null, title: "James Smith", description: "VP, Public Sector", image: "./photos/a.png" }, 11 | { id: 1, parent: 0, title: "Ted Lucas", description: "VP, Human Resources", image: "./photos/b.png" }, 12 | { id: 2, parent: 0, title: "Fritz Stuger", description: "Business Solutions, US", image: "./photos/c.png" }, 13 | { id: 3, parent: 2, title: "Robert Canon", description: "Operation, US", image: "./photos/r.png" }, 14 | ], 15 | annotations: [ 16 | { 17 | annotationType: AnnotationType.Level, 18 | levels: [0], 19 | title: "Level 0", 20 | titleColor: Colors.RoyalBlue, 21 | offset: new Thickness(0, 0, 0, -1), 22 | lineWidth: new Thickness(0, 0, 0, 0), 23 | opacity: 0, 24 | borderColor: Colors.Gray, 25 | fillColor: Colors.Gray, 26 | lineType: LineType.Dotted 27 | }, 28 | new LevelAnnotationConfig({ 29 | levels: [1], 30 | title: "Level 1", 31 | titleColor: Colors.Green, 32 | offset: new Thickness(0, 0, 0, -1), 33 | lineWidth: new Thickness(0, 0, 0, 0), 34 | opacity: 0.08, 35 | borderColor: Colors.Gray, 36 | fillColor: Colors.Gray, 37 | lineType: LineType.Dotted 38 | }), 39 | new LevelAnnotationConfig({ 40 | levels: [2], 41 | title: "Level 2", 42 | titleColor: Colors.Red, 43 | offset: new Thickness(0, 0, 0, -1), 44 | lineWidth: new Thickness(0, 0, 0, 0), 45 | opacity: 0, 46 | borderColor: Colors.Gray, 47 | fillColor: Colors.Gray, 48 | lineType: LineType.Solid 49 | }) 50 | ], 51 | cursorItem: 0, 52 | hasSelectorCheckbox: Enabled.True, 53 | normalItemsInterval: 40 /* defines padding around items of background annotations*/, 54 | arrowsDirection: GroupByType.Parents, 55 | pageFitMode: PageFitMode.FitToPage 56 | }; 57 | 58 | return <> 59 |
60 | 61 |
62 | 63 | } 64 | } 65 | 66 | export default Sample; 67 | -------------------------------------------------------------------------------- /src/Samples/MatrixLayoutOfMultipleRootItemsInChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, ChildrenPlacementType, OrientationType, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 1, 10 | highlightItem: null, 11 | hasSelectorCheckbox: Enabled.True, 12 | orientationType: OrientationType.Top, 13 | items: [ 14 | { 15 | id: 0, 16 | title: "invisible", 17 | parent: null, 18 | isVisible: false, 19 | childrenPlacementType: ChildrenPlacementType.Matrix 20 | }, 21 | { 22 | id: 1, 23 | parent: 0, 24 | title: "Item 1", 25 | description: "Some description about item", 26 | image: "./photos/b.png" 27 | }, 28 | { 29 | id: 2, 30 | parent: 0, 31 | title: "Item 2", 32 | description: "Some description about item", 33 | image: "./photos/b.png" 34 | }, 35 | { 36 | id: 3, 37 | parent: 0, 38 | title: "Item 3", 39 | description: "Some description about item", 40 | image: "./photos/b.png" 41 | }, 42 | { 43 | id: 4, 44 | parent: 0, 45 | title: "Item 4", 46 | description: "Some description about item", 47 | image: "./photos/b.png" 48 | }, 49 | { 50 | id: 5, 51 | parent: 0, 52 | title: "Item 5", 53 | description: "Some description about item", 54 | image: "./photos/b.png" 55 | }, 56 | { 57 | id: 6, 58 | parent: 0, 59 | title: "Item 6", 60 | description: "Some description about item", 61 | image: "./photos/b.png" 62 | }, 63 | { 64 | id: 7, 65 | parent: 0, 66 | title: "Item 7", 67 | description: "Some description about item", 68 | image: "./photos/b.png" 69 | }, 70 | { 71 | id: 8, 72 | parent: 0, 73 | title: "Item 8", 74 | description: "Some description about item", 75 | image: "./photos/b.png" 76 | }, 77 | { 78 | id: 9, 79 | parent: 0, 80 | title: "Item 9", 81 | description: "Some description about item", 82 | image: "./photos/b.png" 83 | } 84 | ] 85 | }; 86 | 87 | return
88 | 89 |
90 | } 91 | } 92 | 93 | export default Sample; 94 | -------------------------------------------------------------------------------- /src/Samples/MultipleRootItemsInChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, OrientationType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor() { 7 | super(); 8 | 9 | this.state = { 10 | config: { 11 | pageFitMode: PageFitMode.None, 12 | cursorItem: 0, 13 | highlightItem: 0, 14 | hasSelectorCheckbox: Enabled.True, 15 | orientationType: OrientationType.Top, 16 | items: [ 17 | { 18 | id: 0, 19 | parent: null, 20 | title: "James Smith", 21 | description: "VP, Public Sector", 22 | image: "./photos/a.png" 23 | }, 24 | { 25 | id: 1, 26 | parent: 0, 27 | title: "Ted Lucas", 28 | description: "VP, Human Resources", 29 | image: "./photos/b.png" 30 | }, 31 | { 32 | id: 2, 33 | parent: 0, 34 | title: "Fritz Stuger", 35 | description: "Business Solutions, US", 36 | image: "./photos/c.png" 37 | }, 38 | { 39 | id: 3, 40 | parent: null, 41 | title: "James Smith 2", 42 | description: "VP, Public Sector", 43 | image: "./photos/a.png" 44 | }, 45 | { 46 | id: 4, 47 | parent: 3, 48 | title: "Ted Lucas 2", 49 | description: "VP, Human Resources", 50 | image: "./photos/b.png" 51 | }, 52 | { 53 | id: 5, 54 | parent: 3, 55 | title: "Fritz Stuger 2", 56 | description: "Business Solutions, US", 57 | image: "./photos/c.png" 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | 64 | update(orientationType) { 65 | const { config } = this.state; 66 | this.setState({ 67 | config: { 68 | ...config, 69 | orientationType 70 | } 71 | }) 72 | } 73 | 74 | 75 | render() { 76 | const { config } = this.state; 77 | 78 | return <> 79 |

Set chart orientation to:   80 |   81 |   82 |   83 | 84 |

85 |
86 | 87 |
88 | ; 89 | } 90 | } 91 | 92 | export default Sample; 93 | -------------------------------------------------------------------------------- /src/Samples/MatrixLayoutInFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { Enabled, GroupByType, PageFitMode } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | cursorItem: 1, 9 | enableMatrixLayout: true, 10 | minimumMatrixSize: 3, 11 | hasSelectorCheckbox: Enabled.False, 12 | arrowsDirection: GroupByType.Children, 13 | pageFitMode: PageFitMode.None, 14 | items: [ 15 | { id: 1, parents: [], title: "Corp 1", label: "Corp 1", description: "Parent 1", image: "./photos/p.png" }, 16 | { id: 2, parents: [], title: "Corp 2", label: "Corp 2", description: "Parent 2", image: "./photos/p.png" }, 17 | { id: 101, parents: [], title: "Corp 3", label: "Corp 3", description: "Parent 3", image: "./photos/p.png" }, 18 | { id: 102, parents: [], title: "Corp 4", label: "Corp 4", description: "Parent 4", image: "./photos/p.png" }, 19 | { id: 3, parents: [1, 2, 101, 102], title: "Sibling 1", label: "Sibling 1", description: "Sibling 1", image: "./photos/s.png" }, 20 | { id: 4, parents: [1, 2, 101, 102], title: "Sibling 2", label: "Sibling 2", description: "Sibling 2", image: "./photos/s.png" }, 21 | { id: 5, parents: [1, 2, 101, 102], title: "Sibling 3", label: "Sibling 3", description: "Sibling 3", image: "./photos/s.png" }, 22 | { id: 6, parents: [1, 2, 101, 102], title: "Sibling 4", label: "Sibling 4", description: "Sibling 4", image: "./photos/s.png" }, 23 | { id: 7, parents: [1, 2, 101, 102], title: "Sibling 5", label: "Sibling 5", description: "Sibling 5", image: "./photos/s.png" }, 24 | { id: 8, parents: [1, 2, 101, 102], title: "Sibling 6", label: "Sibling 6", description: "Sibling 6", image: "./photos/s.png" }, 25 | { id: 9, parents: [1, 2, 101, 102], title: "Sibling 7", label: "Sibling 7", description: "Sibling 7", image: "./photos/s.png" }, 26 | { id: 10, parents: [3, 4, 5, 6, 7, 8, 9], title: "Grand Child 1", label: "Grand Child 1", description: "Grand Child 1", image: "./photos/c.png" }, 27 | { id: 11, parents: [3, 4, 5, 6, 7, 8, 9], title: "Grand Child 2", label: "Grand Child 2", description: "Grand Child 2", image: "./photos/c.png" }, 28 | { id: 12, parents: [3, 4, 5, 6, 7, 8, 9], title: "Grand Child 3", label: "Grand Child 3", description: "Grand Child 3", image: "./photos/c.png" }, 29 | { id: 13, parents: [3, 4, 5, 6, 7, 8, 9], title: "Grand Child 4", label: "Grand Child 4", description: "Grand Child 4", image: "./photos/c.png" } 30 | ] 31 | }; 32 | 33 | return
34 | 35 |
36 | } 37 | } 38 | 39 | export default Sample; 40 | -------------------------------------------------------------------------------- /src/Samples/ChildrenPlacementType.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, ChildrenPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | maximumColumnsInMatrix: 3, 10 | cursorItem: 1, 11 | highlightItem: 0, 12 | normalItemsInterval: 20, 13 | cousinsIntervalMultiplier: 1, 14 | defaultTemplateName: "info", 15 | templates: [{ 16 | name: "info", 17 | itemSize: { width: 80, height: 36 }, 18 | minimizedItemSize: { width: 3, height: 3 }, 19 | highlightPadding: { left: 4, top: 4, right: 4, bottom: 4 }, 20 | onItemRender: ({ context: itemConfig }) => { 21 | return
{itemConfig.title}
; 22 | } 23 | }], 24 | hasSelectorCheckbox: Enabled.False, 25 | items: [ 26 | /* matrix layout example */ 27 | { 28 | id: 1, 29 | parent: null, 30 | title: "Matrix Layout", 31 | childrenPlacementType: ChildrenPlacementType.Matrix 32 | }, 33 | { id: 2, parent: 1, title: "Child 1" }, 34 | { id: 3, parent: 1, title: "Child 2" }, 35 | { id: 4, parent: 1, title: "Child 3" }, 36 | { id: 5, parent: 1, title: "Child 4" }, 37 | { id: 6, parent: 1, title: "Child 5" }, 38 | { id: 7, parent: 1, title: "Child 6" }, 39 | { id: 8, parent: 1, title: "Child 7" }, 40 | { id: 9, parent: 1, title: "Child 8" }, 41 | 42 | /* vertical layout example */ 43 | { 44 | id: 101, 45 | parent: null, 46 | title: "Vertical Layout", 47 | childrenPlacementType: ChildrenPlacementType.Vertical 48 | }, 49 | { id: 102, parent: 101, title: "Child 1" }, 50 | { id: 103, parent: 101, title: "Child 2", childrenPlacementType: ChildrenPlacementType.Vertical }, 51 | { id: 104, parent: 103, title: "Sub Child 3" }, 52 | { id: 105, parent: 103, title: "Sub Child 4" }, 53 | { id: 106, parent: 101, title: "Child 5" }, 54 | { id: 107, parent: 101, title: "Child 6" }, 55 | 56 | /* horizontal layout example */ 57 | { 58 | id: 201, 59 | parent: null, 60 | title: "Horizontal Layout", 61 | childrenPlacementType: ChildrenPlacementType.Horizontal 62 | }, 63 | { id: 202, parent: 201, title: "Child 1" }, 64 | { id: 203, parent: 201, title: "Child 2" }, 65 | { id: 204, parent: 201, title: "Child 3" } 66 | ] 67 | }; 68 | 69 | return
70 | 71 |
72 | } 73 | } 74 | 75 | export default Sample; 76 | -------------------------------------------------------------------------------- /src/Samples/FamilyChartPrimaryParent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { AdviserPlacementType, PageFitMode, Enabled, GroupByType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.onClick = this.onClick.bind(this); 10 | 11 | this.state = { 12 | items: [ 13 | { id: 1, title: "Roger Dalton", label: "Roger Dalton", description: "Id: 1", image: "./photos/a.png", itemTitleColor: "navy" }, 14 | { id: 2, parents: [1], title: "Steven Lacombe", label: "Steven Lacombe", description: "Id: 2", image: "./photos/b.png", itemTitleColor: "navy" }, 15 | { id: 3, parents: [1], title: "Bill Dalton", label: "Bill Dalton", description: "Id: 3", image: "./photos/c.png", itemTitleColor: "navy", relativeItem: 2, placementType: AdviserPlacementType.Right, position: 1 }, 16 | { id: 4, title: "Ann Smith", label: "Ann Smith", description: "Id: 4", image: "./photos/a.png", itemTitleColor: "navy" }, 17 | { id: 5, parents: [4], title: "Nancy Smith", label: "Nancy Smith", description: "Id: 5", image: "./photos/c.png", itemTitleColor: "navy" }, 18 | { id: 6, parents: [4], title: "Helly Smith", label: "Helly Smith", description: "Id: 6", image: "./photos/a.png", itemTitleColor: "navy", relativeItem: 5, placementType: AdviserPlacementType.Right, position: 1 }, 19 | { id: 7, parents: [2, 6], title: "Kelly Smith", label: "Kelly Smith", description: "Id: 7", image: "./photos/c.png", itemTitleColor: "navy" }, 20 | { id: 8, parents: [3, 5], primaryParent: 5, title: "Sally Smith", label: "Sally Smith", description: "Id: 8", image: "./photos/a.png" } 21 | ] 22 | } 23 | } 24 | 25 | onClick() { 26 | const { items } = this.state; 27 | 28 | const newItems = items.map(item => { 29 | if (item.id === 8) { 30 | var { primaryParent } = item; 31 | primaryParent = (primaryParent === 3) ? 5 : 3; 32 | return { 33 | ...item, 34 | primaryParent 35 | } 36 | } 37 | return item; 38 | }) 39 | this.setState({ 40 | items: newItems 41 | }) 42 | } 43 | 44 | render() { 45 | const { items } = this.state; 46 | const config = { 47 | items, 48 | pageFitMode: PageFitMode.None, 49 | cursorItem: 2, 50 | linesWidth: 1, 51 | linesColor: "black", 52 | hasSelectorCheckbox: Enabled.False, 53 | arrowsDirection: GroupByType.Parents, 54 | showExtraArrows: false 55 | }; 56 | 57 | return <> 58 | 59 |
60 | 61 |
62 | 63 | } 64 | } 65 | 66 | export default Sample; 67 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/GroupTitleTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | import RotatedText from './RotatedText'; 4 | import { highestContrast } from 'basicprimitives'; 5 | 6 | class GroupTitleTemplate extends AbstractTemplate { 7 | constructor(options) { 8 | super(); 9 | this.render = this.render.bind(this); 10 | 11 | this.options = options; 12 | 13 | this.style = { 14 | position: "absolute", 15 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 16 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 17 | WebkitUserSelect: "none", 18 | WebkitTouchCallout: "none", 19 | KhtmlUserSelect: "none", 20 | MozUserSelect: "none", 21 | msUserSelect: "none", 22 | userSelect: "none", 23 | boxSizing: "content-box", 24 | 25 | MozBorderRadius: "4px", 26 | WebkitBorderRadius: "4px", 27 | KhtmlBorderRadius: "4px", 28 | BorderRadius: "4px", 29 | 30 | background: "royalblue", 31 | borderWidth: 0, 32 | color: "white", 33 | padding: 0, 34 | width: "100%", 35 | height: "100%", 36 | left: "-1px", 37 | top: "-1px" 38 | } 39 | } 40 | 41 | render(data) { 42 | const { 43 | groupTitleFontSize, 44 | groupTitleFontFamily, 45 | groupTitleFontWeight, 46 | groupTitleFontStyle, 47 | itemTitleSecondFontColor, 48 | itemTitleFirstFontColor, 49 | groupTitleOrientation, 50 | groupTitleHorizontalAlignment, 51 | groupTitleVerticalAlignment, 52 | groupTitleColor 53 | } = this.options; 54 | 55 | const { 56 | context: itemConfig, 57 | width, 58 | height 59 | } = data; 60 | 61 | const backgroundColor = itemConfig.groupTitleColor || groupTitleColor; 62 | const label = (itemConfig.groupTitle || "").replace("\n", "
"); 63 | const color = highestContrast(backgroundColor, itemTitleSecondFontColor, itemTitleFirstFontColor); 64 | 65 | const orientations = ['Horizontal', 'RotateLeft', 'RotateRight', 'Horizontal'], 66 | horizontalAlignments = ['center', 'left', 'right'], 67 | verticalAlignments = ['top', 'middle', 'bottom']; 68 | const style = { 69 | ...this.style, 70 | backgroundColor, 71 | color, 72 | fontSize: groupTitleFontSize, 73 | fontFamily: groupTitleFontFamily, 74 | fontWeight: groupTitleFontWeight, 75 | fontStyle: groupTitleFontStyle 76 | } 77 | return
78 | {label} 85 |
; 86 | } 87 | }; 88 | 89 | export default GroupTitleTemplate; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basicprimitivesreact", 3 | "sideEffects": false, 4 | "version": "6.6.1", 5 | "main": "dist/umd/index.js", 6 | "module": "dist/esm/index.js", 7 | "babel": { 8 | "presets": [ 9 | "@babel/preset-env", 10 | "@babel/preset-react" 11 | ], 12 | "plugins": [ 13 | "@babel/plugin-proposal-class-properties" 14 | ] 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/BasicPrimitives/react" 19 | }, 20 | "homepage": "https://basicprimitives.github.io/react/", 21 | "license": "SEE LICENSE IN license.pdf", 22 | "dependencies": { 23 | "basicprimitives": "6.6.1", 24 | "resize-observer-polyfill": "^1.5.1" 25 | }, 26 | "devDependencies": { 27 | "@babel/cli": "^7.25.6", 28 | "@babel/core": "^7.25.2", 29 | "@babel/node": "^7.25.0", 30 | "@babel/plugin-proposal-class-properties": "^7.18.6", 31 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 32 | "@babel/preset-env": "^7.25.4", 33 | "@babel/preset-react": "^7.24.7", 34 | "@eslint/js": "^9.9.0", 35 | "@fortawesome/fontawesome-svg-core": "^6.6.0", 36 | "@fortawesome/free-solid-svg-icons": "^6.6.0", 37 | "@fortawesome/react-fontawesome": "^0.2.2", 38 | "@types/react": "^18.3.3", 39 | "@types/react-dom": "^18.3.0", 40 | "@vitejs/plugin-react": "^4.3.1", 41 | "eslint": "^9.9.0", 42 | "eslint-plugin-react": "^7.35.0", 43 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 44 | "eslint-plugin-react-refresh": "^0.4.9", 45 | "globals": "^15.9.0", 46 | "react": "^18.3.1", 47 | "react-dnd": "^16.0.1", 48 | "react-dnd-html5-backend": "^16.0.1", 49 | "react-dom": "^18.3.1", 50 | "vite": "^5.4.1" 51 | }, 52 | "scripts": { 53 | "dev": "vite", 54 | "build": "vite build", 55 | "lint": "eslint .", 56 | "preview": "vite preview", 57 | "publish:npm": "SET NODE_ENV=production && if exist dist rmdir /s /q dist && mkdir dist && npx babel src/Diagrams --presets=\"module:@babel/preset-react\" --out-dir dist/esm --copy-files && npx babel src/Diagrams --presets=\"module:@babel/preset-env\",\"module:@babel/preset-react\" --out-dir dist/umd --copy-files" 58 | }, 59 | "keywords": [ 60 | "data analytics", 61 | "data visualization", 62 | "visualization", 63 | "diagrams", 64 | "diagram layout", 65 | "chart", 66 | "charting library", 67 | "diagram", 68 | "diagrams visualization", 69 | "diagram editor", 70 | "diagram generator", 71 | "tree", 72 | "hierarchy", 73 | "organizational chart", 74 | "multiple inheritance visualization", 75 | "family tree", 76 | "dependency diagram", 77 | "dependencies diagram", 78 | "financial ownership diagram", 79 | "layered graph", 80 | "directed acyclic graph", 81 | "component", 82 | "react", 83 | "javascript", 84 | "component library", 85 | "react component" 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /src/Samples/LabelsCascadesInFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { Enabled, AnnotationType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | cursorItem: 2, 9 | linesWidth: 1, 10 | linesColor: "black", 11 | hasSelectorCheckbox: Enabled.False, 12 | normalLevelShift: 20, 13 | dotLevelShift: 20, 14 | lineLevelShift: 20, 15 | normalItemsInterval: 5, 16 | dotItemsInterval: 5, 17 | lineItemsInterval: 5, 18 | items: [ 19 | { id: 1, parents: [2], title: "Thomas Williams", label: "Thomas Williams", description: "1, 1st husband", image: "./photos/t.png" }, 20 | { id: 2, parents: [2], title: "Mary Spencer", label: "Mary Spencer", description: "2, The Mary", image: "./photos/m.png" }, 21 | { id: 3, parents: [2], title: "David Kirby", label: "David Kirby", description: "3, 2nd Husband", image: "./photos/d.png" }, 22 | { id: 4, parents: [2], title: "Brad Williams", label: "Brad Williams", description: "4, 1st son", image: "./photos/b.png" }, 23 | { id: 5, parents: [2], title: "Mike Kirby", label: "Mike Kirby", description: "5, 2nd son, having 2 spouses", image: "./photos/m.png" } 24 | ], 25 | annotations: [ 26 | { 27 | annotationType: AnnotationType.Label, 28 | fromItem: 2, 29 | toItems: [1, 3, 4, 5], 30 | title:
100%
31 | }, 32 | { 33 | annotationType: AnnotationType.Label, 34 | fromItem: 2, 35 | toItems: [1, 3, 4], 36 | title:
60%
37 | }, 38 | { 39 | annotationType: AnnotationType.Label, 40 | fromItem: 2, 41 | toItems: [5], 42 | title:
40%
43 | }, 44 | { 45 | annotationType: AnnotationType.Label, 46 | fromItem: 2, 47 | toItems: [1, 3], 48 | title:
20%
49 | }, 50 | { 51 | annotationType: AnnotationType.Label, 52 | fromItem: 2, 53 | toItems: [4], 54 | title:
80%
55 | }, 56 | { 57 | annotationType: AnnotationType.Label, 58 | fromItem: 2, 59 | toItems: [1], 60 | title:
35%
61 | }, 62 | { 63 | annotationType: AnnotationType.Label, 64 | fromItem: 2, 65 | toItems: [3], 66 | title:
65%
67 | } 68 | ] 69 | }; 70 | 71 | return
72 | 73 |
74 | } 75 | } 76 | 77 | export default Sample; 78 | -------------------------------------------------------------------------------- /src/Samples/LoopsInFamilyChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { AnnotationType, GroupByType, ConnectorType, ElbowType, LineType, Colors, PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | annotations: [ 9 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [3], title:
10%
}, 10 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [5], title:
30%
}, 11 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [6], title:
50%
}, 12 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [7], title:
10%
}, 13 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [3, 5], title:
40%
}, 14 | { annotationType: AnnotationType.Label, fromItem: 1, toItems: [3, 5, 6, 7], title:
100%
}, 15 | { annotationType: AnnotationType.Label, fromItem: 2, toItems: [1], title:
100%
} 16 | ], 17 | items: [ 18 | { id: 1, parents: [3, 5, 6, 7], title: "James", label: "James", description: "James is First and he is child of Brad, Sara & Lynette", image: "./photos/j.png" }, 19 | { id: 2, parents: [1], title: "Brad", label: "Brad", description: "", image: "./photos/b.png" }, 20 | { id: 3, parents: [2], title: "Thomas", label: "Thomas", description: "", image: "./photos/t.png" }, 21 | { id: 4, parents: [3], title: "David", label: "David", description: "", image: "./photos/d.png" }, 22 | { id: 5, parents: [4], title: "Lynette", label: "Lynette", description: "", image: "./photos/l.png" }, 23 | { id: 6, parents: [4], title: "Sara", label: "Sara", description: "", image: "./photos/s.png" }, 24 | { id: 7, title: "Parent", label: "Parent", description: "Parent node of James", image: "./photos/j.png" } 25 | ], 26 | arrowsDirection: GroupByType.Parents, 27 | showExtraArrows: true, 28 | extraArrowsMinimumSpace: 30, 29 | connectorType: ConnectorType.Squared, 30 | elbowType: ElbowType.Round, 31 | bevelSize: 4, 32 | elbowDotSize: 4, 33 | linesType: LineType.Solid, 34 | linesColor: Colors.Black, 35 | linesWidth: 1, 36 | 37 | /* Intervals */ 38 | normalLevelShift: 20, 39 | dotLevelShift: 20, 40 | lineLevelShift: 20, 41 | 42 | normalItemsInterval: 20, 43 | dotItemsInterval: 10, 44 | lineItemsInterval: 10, 45 | 46 | cursorItem: 1, 47 | pageFitMode: PageFitMode.None, 48 | hasSelectorCheckbox: Enabled.True 49 | }; 50 | 51 | return <> 52 |
53 | 54 |
55 | 56 | } 57 | } 58 | 59 | export default Sample; 60 | -------------------------------------------------------------------------------- /docs/AdviserAndAssistantItemTypes.md: -------------------------------------------------------------------------------- 1 | # Custom Placement of Children 2 | ## Adviser & Assistant Item Types: 3 | 4 | The organizational chart structure is a regular tree. That means that every node can only have one logical parent in the hierarchy. That makes the organizational chart conceptually easy to work with for the end-user who edits the organizational chart and the software developer to maintain the database's required structures and the application. 5 | 6 | In reality, pure hierarchical relations are rare. So in our organizational chart, we provide the means to represent non-hierarchical relations by using different child item types and on-screen annotations. 7 | 8 | The component has the following child positions in diagram layout relative to the parent node: 9 | * Regular 10 | * Adviser 11 | * Assistant 12 | * Sub Adviser 13 | * Sub Assistant 14 | * General Partner 15 | * Limited Partner 16 | * Adviser Partner 17 | 18 | All of them affect child placement relative to its parent in the hierarchy. The following example demonstrates Adviser and Assistant types. The adviser item is placed at the same level as its parent and connected to it horizontally. The assistant occupies the row between the parent and all other regular children. It has a horizontal connection to the vertical line going from the parent to the remaining children. 19 | 20 | Use the `adviserPlacementType` option to place an adviser or assistant on the parent's node hierarchy's left or right side. 21 | 22 | Use the `levelOffset` option to arrange assistants into multiple rows. See the regular children layout sample. 23 | 24 | [React](../src/Samples/AdviserAndAssistantItemTypes.jsx) 25 | 26 | ## Sub Adviser & Sub Assistant item types 27 | 28 | Sub Adviser & Sub Assistant item types are variations of regular Adviser & Assistant types. The only difference is that they have the connection line going from the top edge of the node, and they are shift down one level relative to their parents. 29 | 30 | Use the `adviserPlacementType` option to place them on the parent's hierarchy's left or right side. 31 | 32 | [React](../src/Samples/SubAdviserAndSubAssistantItemTypes.jsx) 33 | 34 | ## Adviser child nodes placement above parent's node children 35 | 36 | If the adviser node has its children, then control adds extra levels, so it places advisers children at rows above the parent's children. You can alter this layout schema with the `placeAdvisersAboveChildren` option. If you set it to false, control would place advisers and the parent's children at the same row of the diagram. 37 | 38 | [React](../src/Samples/PlaceAdvisersAboveChildren.jsx) 39 | 40 | ## Assistant child nodes placement above parent's node children 41 | If the assistant node has its children, then control pushes the parent's children down, placing assistant children at rows above the parent's children. You can alter this layout schema with the `placeAssistantsAboveChildren` option. If you set it to false, then the parent's and assistant's node children would be rendered side by side at the same level. 42 | 43 | [React](../src/Samples/PlaceAssistantsAboveChildren.jsx) -------------------------------------------------------------------------------- /src/Samples/FamilyChartItemsOrdering.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { AdviserPlacementType, PageFitMode, Enabled, GroupByType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.onClick = this.onClick.bind(this); 10 | 11 | this.state = { 12 | items: [ 13 | { id: 10, relativeItem: 2, placementType: AdviserPlacementType.Left, position: 1, title: "Roger Dalton", label: "Roger Dalton", description: "Id: 10", image: "./photos/a.png", itemTitleColor: "#4169e1" }, 14 | { id: 2, title: "Steven Lacombe", label: "Steven Lacombe", description: "Id: 2", image: "./photos/b.png", itemTitleColor: "#4b0082" }, 15 | { id: 11, relativeItem: 2, placementType: AdviserPlacementType.Right, position: 1, title: "Bill Dalton", label: "Bill Dalton", description: "Id: 11", image: "./photos/c.png", itemTitleColor: "#4b0082" }, 16 | { id: 1, parents: [11], title: "David Dalton", label: "David Dalton", description: "Id: 1", image: "./photos/c.png", itemTitleColor: "#4b0082" }, 17 | { id: 3, parents: [10], title: "Ann Smith", label: "Ann Smith", description: "Id: 3", image: "./photos/a.png", itemTitleColor: "#4169e1" }, 18 | { id: 4, parents: [2], title: "Nancy Smith", label: "Nancy Smith", description: "Id: 4", image: "./photos/c.png", itemTitleColor: "#4b0082" }, 19 | { id: 5, parents: [2], title: "Helly Smith", label: "Helly Smith", description: "Id: 5", image: "./photos/a.png", itemTitleColor: "#4169e1" }, 20 | { id: 6, parents: [1, 4], title: "Kelly Smith", label: "Kelly Smith", description: "Id: 6", image: "./photos/c.png", itemTitleColor: "#4b0082" }, 21 | { id: 7, parents: [5, 3], title: "Sally Smith", label: "Sally Smith", description: "Id: 7", image: "./photos/a.png", itemTitleColor: "#4169e1" } 22 | ] 23 | } 24 | } 25 | 26 | onClick() { 27 | const { items } = this.state; 28 | 29 | const newItems = items.map(item => { 30 | if (item.id === 10 || item.id === 11) { 31 | let { placementType } = item; 32 | if (placementType === AdviserPlacementType.Right) { 33 | placementType = AdviserPlacementType.Left; 34 | } else { 35 | placementType = AdviserPlacementType.Right; 36 | } 37 | return { 38 | ...item, 39 | placementType 40 | } 41 | } 42 | return item; 43 | }) 44 | this.setState({ 45 | items: newItems 46 | }) 47 | } 48 | 49 | render() { 50 | const { items } = this.state; 51 | const config = { 52 | items, 53 | pageFitMode: PageFitMode.None, 54 | cursorItem: 2, 55 | linesWidth: 1, 56 | linesColor: "black", 57 | hasSelectorCheckbox: Enabled.False, 58 | arrowsDirection: GroupByType.Parents, 59 | showExtraArrows: false 60 | }; 61 | 62 | return <> 63 | 64 |
65 | 66 |
67 | 68 | } 69 | } 70 | 71 | export default Sample; 72 | -------------------------------------------------------------------------------- /src/Samples/GroupTitleTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram, RotatedText } from '../Diagrams'; 3 | import { PageFitMode, Enabled, Colors, TextOrientationType, OrgItemConfig } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | var style = { 8 | position: "absolute", 9 | fontSize: "12px", 10 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 11 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 12 | WebkitUserSelect: "none", 13 | WebkitTouchCallout: "none", 14 | KhtmlUserSelect: "none", 15 | MozUserSelect: "none", 16 | msUserSelect: "none", 17 | userSelect: "none", 18 | boxSizing: "content-box", 19 | 20 | MozBorderRadius: "4px", 21 | WebkitBorderRadius: "4px", 22 | KhtmlBorderRadius: "4px", 23 | BorderRadius: "4px", 24 | 25 | background: "royalblue", 26 | borderWidth: 0, 27 | color: "white", 28 | padding: 0, 29 | width: "100%", 30 | height: "100%", 31 | left: "-1px", 32 | top: "-1px" 33 | } 34 | const config = { 35 | pageFitMode: PageFitMode.FitToPage, 36 | cursorItem: 0, 37 | highlightItem: 0, 38 | hasSelectorCheckbox: Enabled.True, 39 | itemTitleFirstFontColor: Colors.Yellow, 40 | itemTitleSecondFontColor: Colors.Blue, 41 | groupTitleOrientation: TextOrientationType.RotateRight, 42 | 43 | items: [ 44 | new OrgItemConfig({ 45 | id: 0, 46 | parent: null, 47 | title: "James Smith", 48 | description: "VP, Public Sector", 49 | groupTitle: "Group 1", 50 | image: "./photos/a.png", 51 | itemTitleColor: Colors.Black 52 | }), 53 | new OrgItemConfig({ 54 | id: 1, 55 | parent: 0, 56 | title: "Ted Lucas", 57 | description: "VP, Human Resources", 58 | image: "./photos/b.png", 59 | itemTitleColor: Colors.Green, 60 | groupTitle: "Group 2", 61 | groupTitleColor: Colors.Gray 62 | }), 63 | new OrgItemConfig({ 64 | id: 2, 65 | parent: 0, 66 | title: "Fritz Stuger", 67 | description: "Business Solutions, US", 68 | image: "./photos/c.png", 69 | itemTitleColor: Colors.Yellow, 70 | groupTitle: "Group 2" 71 | }) 72 | ], 73 | onGroupTitleRender: ((data) => { 74 | var {context: itemConfig, width, height} = data; 75 | return
{ 76 | event.stopPropagation(); 77 | alert(`User clicked on group title for node ${itemConfig.title}`) 78 | }}> 79 | {itemConfig.groupTitle} 86 |
87 | }) 88 | }; 89 | 90 | return
91 | 92 |
93 | } 94 | } 95 | 96 | export default Sample; 97 | -------------------------------------------------------------------------------- /docs/Buttons.md: -------------------------------------------------------------------------------- 1 | # Buttons panel 2 | 3 | The component provides options to preserve some space around nodes to render custom controls or buttons. Buttons rendered in diagram layout provides better UI discoverability compared to context popup panel. 4 | 5 | Enabling buttons panel visibility at component scope with `hasButtons` property: 6 | * `Enabled.Auto` - Buttons visible only for cursor item. 7 | * `Enabled.True` - Every normal item has buttons visible. 8 | * `Enabled.False` - No buttons. 9 | 10 | ```JavaScript 11 | import { OrgDiagram } from basicprimitivesreact; 12 | import { Enabled } from basicprimitives; 13 | 17 | ``` 18 | 19 | Enabling buttons panel visibility individually per `ItemConfig` with similar 'hasButtons' property: 20 | * `Enabled.Auto` - Buttons panel visibility depends on component scope `hasButtons` value. 21 | * `Enabled.True` - Has buttons visible. 22 | * `Enabled.False` - No buttons panel. 23 | 24 | ```JavaScript 25 | import { OrgDiagram } from basicprimitivesreact; 26 | import { Enabled } from basicprimitives; 27 | 40 | ``` 41 | 42 | Size the buttons panel set in component `Config` `buttonsPanelSize` property. It is regular numeric value in `px`. 43 | 44 | ```JavaScript 45 | import { OrgDiagram } from basicprimitivesreact; 46 | 50 | ``` 51 | 52 | Populate buttons panel content per component or item template with `onButtonsRender` callback function. See item template definition in the following example: 53 | 54 | ```JavaScript 55 | import React, { Component } from 'react'; 56 | import { OrgDiagram } from basicprimitivesreact; 57 | import { Enabled } from 'basicprimitives'; 58 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 59 | import { faCoffee } from '@fortawesome/free-solid-svg-icons' 60 | { 64 | return <> 65 | 72 | 73 | }) 74 | } 75 | /> 76 | ``` 77 | 78 | Please, pay attention that every button `onClick` event handler should suppress even propagation. It is needed to avoid chart cursor item change and the following layout update. 79 | 80 | ```JavaScript 81 | import { OrgDiagram } from basicprimitivesreact; 82 | { 84 | event.stopPropagation(); 85 | alert(`User clicked on Coffee button for node ${itemConfig.title}`) 86 | }} 87 | } 88 | /> 89 | ``` 90 | 91 | [React](../src/Samples/ButtonsPanel.jsx) 92 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/RotatedText.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class RotatedText extends Component { 5 | static propTypes = { 6 | width: PropTypes.number.isRequired, 7 | height: PropTypes.number.isRequired, 8 | orientation: PropTypes.oneOf(['Horizontal', 'RotateLeft', 'RotateRight']), 9 | horizontalAlignment: PropTypes.oneOf(['left', 'center', 'right']), 10 | verticalAlignment: PropTypes.oneOf(['top', 'middle', 'bottom']) 11 | }; 12 | 13 | static defaultProps = { 14 | orientation: 'RotateRight', 15 | horizontalAlignment: 'center', 16 | verticalAlignment: 'middle' 17 | }; 18 | 19 | getTransform(orientation) { 20 | let result = ""; 21 | switch (orientation) { 22 | case 'RotateLeft': 23 | result = "rotate(-90deg)"; 24 | break; 25 | case 'RotateRight': 26 | result = "rotate(90deg)"; 27 | break; 28 | default: 29 | break; 30 | } 31 | return result; 32 | } 33 | 34 | render() { 35 | const { 36 | children, 37 | width, 38 | height, 39 | orientation, 40 | horizontalAlignment, 41 | verticalAlignment 42 | } = this.props; 43 | 44 | const transform = this.getTransform(orientation); 45 | 46 | let size = null; 47 | if (orientation === "Horizontal") { 48 | size = { 49 | width: width + "px", 50 | height: height + "px", 51 | maxWidth: width + "px", 52 | maxHeight: height + "px" 53 | } 54 | } else { 55 | size = { 56 | width: height + "px", 57 | height: width + "px", 58 | maxWidth: height + "px", 59 | maxHeight: width + "px", 60 | left: Math.round(width / 2.0 - height / 2.0) + "px", 61 | top: Math.round(height / 2.0 - width / 2.0) + "px" 62 | } 63 | } 64 | var style = { 65 | position: "absolute", 66 | padding: 0, 67 | margin: 0, 68 | lineHeight: 1, 69 | textAlign: horizontalAlignment, 70 | WebkitTransformOrigin: "center center", 71 | MozTransformOrigin: "center center", 72 | OTransformOrigin: "center center", 73 | msTransformOrigin: "center center", 74 | WebkitTransform: transform, 75 | MozTransform: transform, 76 | OTransform: transform, 77 | msTransform: transform, 78 | transform, 79 | textOverflow: "ellipsis", 80 | whiteSpace: "nowrap", 81 | overflow: "hidden", 82 | tableLayout: "fixed", 83 | ...size 84 | }; 85 | 86 | return ( 87 | verticalAlignment === 'top' ? 88 |
89 | {children} 90 |
91 | : 92 | 93 | 94 | 95 | 104 | 105 | 106 |
102 | {children} 103 |
107 | ); 108 | } 109 | } 110 | 111 | export default RotatedText; 112 | -------------------------------------------------------------------------------- /docs/ChildrenLayout.md: -------------------------------------------------------------------------------- 1 | # Children Layout 2 | Children Placement Layout can be defined individually per item or globally for all chart items. Following chart and item config properties are used to define the layout of children: 3 | 4 | * `childrenPlacementType` - this property is available for the chart and individual items. It defines the shape of children with the `ChildrenPlacementType` enumeration, which has the following options `Vertical`, `Horizontal` & `Matrix` 5 | * `leavesPlacementType` - this option is available only at the global chart level. It controls children's layout with no own children, so it is only for children of the hierarchy's last level. 6 | * `maximumColumnsInMatrix` - by default, matrixed children form square formation. If square formation grows beyond the screen's width, it becomes inconvenient since the end-user needs to scroll that matrix both vertically and horizontally. Use this option to limit the maximum number of columns so that matrix would grow vertically only. 7 | 8 | ```JavaScript 9 | import { OrgDiagram } from basicprimitivesreact; 10 | import { ChildrenPlacementType } from basicprimitives; 11 | 27 | ``` 28 | 29 | [React](../src/Samples/ChildrenPlacementType.jsx) 30 | 31 | # Placing children into multiple horizontal levels 32 | To programmatically place children nodes into multiple rows, use the `levelOffset` property. Child nodes would be grouped by that property and placed in rows. If level offsets defined for children have gaps, then the control would preserve empty row, so different branches and teams of the same organizations would be properly aligned. See the matrixed layout demo for the matrixed team structure in the organization. 33 | 34 | 35 | If you use the `levelOffset` property, then the children's last row is only shaped into matrixed or vertical formation by the `childrenPlacementType` property setting. 36 | 37 | ```JavaScript 38 | import { OrgDiagram } from basicprimitivesreact; 39 | import { ChildrenPlacementType } from basicprimitives; 40 | 69 | ``` 70 | 71 | [React](../src/Samples/ChildrenAndAssistantsLevelOffset.jsx) 72 | -------------------------------------------------------------------------------- /src/Samples/SelectedItems.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor() { 7 | super(); 8 | 9 | this.onSelectionChanged = this.onSelectionChanged.bind(this); 10 | 11 | this.state = { 12 | config: { 13 | pageFitMode: PageFitMode.FitToPage, 14 | cursorItem: 1, 15 | hasSelectorCheckbox: Enabled.True, 16 | selectedItems: [1, 3], 17 | items: [ 18 | { 19 | id: 1, 20 | parent: null, 21 | title: 'James Smith', 22 | description: 'VP, Public Sector', 23 | image: './photos/a.png' 24 | }, 25 | { 26 | id: 2, 27 | parent: 1, 28 | title: 'Ted Lucas', 29 | description: 'VP, Human Resources', 30 | image: './photos/b.png' 31 | }, 32 | { 33 | id: 3, 34 | parent: 1, 35 | title: 'Fritz Stuger', 36 | description: 'Business Solutions, US', 37 | image: './photos/c.png' 38 | } 39 | ] 40 | } 41 | } 42 | } 43 | 44 | onSelectionChanged(event, currentSelectedItems, newSelectedItems) { 45 | const { config } = this.state; 46 | this.setState({ 47 | config: { 48 | ...config, 49 | selectedItems: newSelectedItems 50 | } 51 | }) 52 | 53 | return true; // Cancel state change in Component and rendering cycle 54 | }; 55 | 56 | update(event, itemid) { 57 | const { config } = this.state; 58 | const { selectedItems } = config; 59 | const isChecked = event.target.checked; 60 | if (isChecked) { 61 | this.setState({ 62 | config: { 63 | ...config, 64 | selectedItems: [...selectedItems, itemid] 65 | } 66 | }) 67 | } else { 68 | this.setState({ 69 | config: { 70 | ...config, 71 | // eslint-disable-next-line 72 | selectedItems: (selectedItems.filter(id => id != itemid)) 73 | } 74 | }) 75 | } 76 | } 77 | 78 | 79 | render() { 80 | const { config } = this.state; 81 | const { items, selectedItems } = config; 82 | const itemsHash = items.reduce((agg, item) => { agg[item.id] = item; return agg; }, {}); 83 | const message = selectedItems.map(id => itemsHash[id].title).join(", "); 84 | const selectedItemsHash = selectedItems.reduce((agg, id) => { agg[id] = true; return agg; }, {}); 85 | 86 | return <> 87 |

Select following items:   88 | { 89 | items.map(item => 90 | this.update(event, item.id)} type="checkbox" checked={selectedItemsHash[item.id] ? 'checked' : ''} /> 91 | {item.title} 92 |   93 | 94 | ) 95 | } 96 |

97 |
98 | 99 |
100 | {message.length > 0 ?

User selected following items: {message}

: null} 101 | ; 102 | } 103 | } 104 | 105 | export default Sample; 106 | -------------------------------------------------------------------------------- /docs/ConnectorAnnotation.md: -------------------------------------------------------------------------------- 1 | # Connector Annotations 2 | 3 | Connector annotation is an on-screen direct connection line between two nodes of the diagram. It supports simple conflict resolution. If multiple connector annotations overlap each other between the same pair of nodes, the control offsets them and draws them parallel. 4 | 5 | The following sample demonstrates connection annotation drawn in the offbeat style. This way, connection annotation should not overlap diagram's base connection lines and block the primary diagram hierarchy view. The offbeat connector annotation compensates for the lack of space between nodes via drawing its curve outside of connected nodes. 6 | 7 | ## Styles 8 | * `ConnectorPlacementType.Offbeat` - Free hand drawing of connector annotation 9 | * `ConnectorPlacementType.Straight` - Straight line between nodes 10 | 11 | ## Shapes 12 | In general, shape of connector annotation may indicate various relations, so component supports following simple use case: 13 | * `ConnectorShapeType.OneWay` - The arrow line between `fromItem` and `toItem` nodes 14 | * `ConnectorShapeType.TwoWay` - Two opposite direction arrow lines between `fromItem` and `toItem` nodes 15 | * `ConnectorShapeType.BothWay` - One line having arrows on both ends 16 | 17 | ## Labels 18 | Connector annotations labels are plain text or JSX elements. Please, pay attention that component does not resolve connector annotations labels overlapping, so many annotations labels can clutter view and diagram in general. 19 | 20 | [React](../src/Samples/ConnectorAnnotation.jsx) 21 | 22 | # Connector Annotations Drag & Drop 23 | 24 | The following sample demonstrates how drag-and-drop can be used to create or modify connector annotations in a diagram. 25 | 26 | * Drag nodes to create new annotations. 27 | * Drag annotation endpoints to reconnect them to different nodes. 28 | * Drag an endpoint to the origin node to delete the annotation. 29 | 30 | [React](../src/Samples/ConnectorAnnotationDragNDropHooks.jsx) 31 | 32 | ## Drag and Drop Implementation 33 | 34 | The drag-and-drop behavior is implemented using **React DnD** library: 35 | 36 | - **Node drag source:** allows users to drag nodes and create new annotations by dropping on other nodes. 37 | - **Endpoint drag source:** allows users to reconnect existing annotations by dragging endpoints to different nodes. 38 | - **Custom drag layer:** provides real-time visual feedback by rendering a temporary live connector annotation between drag start and current mouse position. 39 | 40 | ## Limitations 41 | 42 | Due to [React Drag & Drop](http://react-dnd.github.io/react-dnd/about) being implemented using the HTML5 drag and drop API, there is an important technical limitation: 43 | 44 | - While dragging an annotation or its endpoint, the original annotation remains visible in the diagram. 45 | - Simultaneously, the custom drag layer renders a live preview of the dragged annotation. 46 | - As a result, both the original annotation and its live copy are temporarily visible during drag. 47 | 48 | The root cause is that HTML5 drag-and-drop requires the dragged DOM element to remain mounted during the drag operation. If we attempt to modify or remove the dragged annotation from OrgDiagram while dragging, the corresponding DOM element is unmounted from the document, which aborts the drag-and-drop operation. In order to overcome this problem, we have to use non-native drag & drop frameworks. 49 | 50 | -------------------------------------------------------------------------------- /src/Diagrams/Templates/LevelTitleTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AbstractTemplate from './AbstractTemplate'; 3 | import RotatedText from './RotatedText'; 4 | import { OrientationType, TextOrientationType } from 'basicprimitives'; 5 | 6 | class LevelTitleTemplate extends AbstractTemplate { 7 | constructor(options, orientation) { 8 | super(); 9 | this.render = this.render.bind(this); 10 | 11 | this.options = options; 12 | this.orientation = orientation; 13 | 14 | this.style = { 15 | position: "absolute", 16 | fontFamily: "Trebuchet MS, Tahoma, Verdana, Arial, sans-serif", 17 | WebkitTapHighlightColor: "rgba(0,0,0,0)", 18 | WebkitUserSelect: "none", 19 | WebkitTouchCallout: "none", 20 | KhtmlUserSelect: "none", 21 | MozUserSelect: "none", 22 | msUserSelect: "none", 23 | userSelect: "none", 24 | boxSizing: "content-box", 25 | 26 | MozBorderRadius: "4px", 27 | WebkitBorderRadius: "4px", 28 | KhtmlBorderRadius: "4px", 29 | BorderRadius: "4px", 30 | 31 | background: "royalblue", 32 | borderWidth: 0, 33 | color: "white", 34 | padding: 0, 35 | width: "100%", 36 | height: "100%", 37 | left: "-1px", 38 | top: "-1px" 39 | } 40 | } 41 | 42 | render(data) { 43 | var { 44 | levelTitleFontSize, 45 | levelTitleFontFamily, 46 | levelTitleFontWeight, 47 | levelTitleFontStyle, 48 | levelTitleOrientation, 49 | levelTitleHorizontalAlignment, 50 | levelTitleVerticalAlignment, 51 | levelTitleFontColor, 52 | levelTitleColor 53 | } = this.options; 54 | 55 | if(levelTitleOrientation === TextOrientationType.Auto) { 56 | switch (this.orientation) { 57 | case OrientationType.Top: 58 | levelTitleOrientation = TextOrientationType.RotateRight; 59 | break; 60 | case OrientationType.Bottom: 61 | levelTitleOrientation = TextOrientationType.RotateRight; 62 | break; 63 | default: 64 | break; 65 | } 66 | } 67 | 68 | const { 69 | context: annotationConfig, 70 | width, 71 | height 72 | } = data; 73 | 74 | const backgroundColor = annotationConfig.titleColor || levelTitleColor; 75 | const label = (annotationConfig.title || "").replace("\n", "
"); 76 | const color = annotationConfig.titleFontColor || levelTitleFontColor; 77 | 78 | const orientations = ['Horizontal', 'RotateLeft', 'RotateRight', 'Horizontal'], 79 | horizontalAlignments = ['center', 'left', 'right'], 80 | verticalAlignments = ['top', 'middle', 'bottom']; 81 | const style = { 82 | ...this.style, 83 | backgroundColor, 84 | color, 85 | fontSize: levelTitleFontSize, 86 | fontFamily: levelTitleFontFamily, 87 | fontWeight: levelTitleFontWeight, 88 | fontStyle: levelTitleFontStyle 89 | } 90 | return
91 | {label} 98 |
; 99 | } 100 | }; 101 | 102 | export default LevelTitleTemplate; -------------------------------------------------------------------------------- /src/Samples/FamilyHideGrandParentsConnections.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { AnnotationType, Colors, PageFitMode, Enabled, GroupByType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | hideGrandParentsConnectors: false 11 | } 12 | } 13 | 14 | onChanged(hideGrandParentsConnectors) { 15 | this.setState({ hideGrandParentsConnectors }); 16 | } 17 | 18 | render() { 19 | const { hideGrandParentsConnectors } = this.state; 20 | const config = { 21 | hideGrandParentsConnectors, 22 | groupTitlePanelSize: 40, 23 | groupTitleFontSize: "14px", 24 | groupTitleColor: "green", 25 | fontFamily: "Areal", 26 | items: [ 27 | { id: 1, title: "Thomas Williams", label: "Thomas Williams", groupTitle: "Great Grand Parent", description: "Great Grand Parent", image: "./photos/g.png" }, 28 | { id: 2, parents: [1], title: "Mary Spencer", label: "Mary Spencer", description: "Spouse", image: "./photos/s.png" }, 29 | { id: 3, parents: [1], title: "David Kirby", label: "David Kirby", groupTitle: "Grand Parent", description: "Grand Parent", image: "./photos/g.png" }, 30 | { id: 4, parents: [1, 3], title: "Brad Williams", label: "Brad Williams", groupTitle: "Parent", description: "Parent", image: "./photos/p.png" }, 31 | { id: 5, parents: [1, 4], title: "Mike Kirby", groupTitle: "The node", label: "Mike Kirby", description: "Item connected to grand parents", image: "./photos/c.png" }, 32 | { id: 6, parents: [2, 5], title: "Lynette Maloney", label: "Lynette Maloney", description: "Grand Child", image: "./photos/c.png" } 33 | 34 | ], 35 | annotations: [ 36 | { 37 | annotationType: AnnotationType.HighlightPath, 38 | items: [5, 1], 39 | color: Colors.Red, 40 | lineWidth: 2, 41 | opacity: 1, 42 | showArrows: true 43 | } 44 | ], 45 | pageFitMode: PageFitMode.None, 46 | cursorItem: 5, 47 | linesWidth: 1, 48 | linesColor: "black", 49 | hasSelectorCheckbox: Enabled.False, 50 | normalLevelShift: 20, 51 | dotLevelShift: 20, 52 | lineLevelShift: 20, 53 | normalItemsInterval: 10, 54 | dotItemsInterval: 20, 55 | lineItemsInterval: 20, 56 | arrowsDirection: GroupByType.Parents, 57 | showExtraArrows: false 58 | }; 59 | 60 | return <> 61 |

Hide direct connections to grand parents: 62 |
63 | 73 |
74 | 84 |

85 |
86 | 87 |
88 | 89 | } 90 | } 91 | 92 | export default Sample; 93 | -------------------------------------------------------------------------------- /docs/CursorTemplate.md: -------------------------------------------------------------------------------- 1 | # Cursor template 2 | Our component is a generic collection control, which supports basic features like single item selection, highlight, and multiple items checking. 3 | The cursor item is a single item selection. The navigation of the diagram depends on the current cursor item. The cursor is supposed to have some visual elements. By default, it is `div` having a solid `2px` border line around it. 4 | 5 | ## Properties: 6 | The component customizes the visual representation of items with `templates`. Every template has customization properties for item content, cursor, and highlight visualizations. By default, if properties are nulls, then the component uses built in default functionality. The following properties customize cursor template: 7 | * `cursorPadding` - Reserves space around item, for example: `{left: 3, top: 3, right: 50, bottom: 3}` will provide extra `50px` on right side of item for cursor content. 8 | * `cursorBorderWidth` - some legacy property, it is used to align cursor position around item properly. 9 | * `onCursorRender` - callback method to render cursor for the current cursor item of the diagram 10 | 11 | See the Item template for more details. 12 | 13 | ## Cursor item & preserving space for context control panel 14 | The cursor template's general idea is to provide a convenient API to place the context control panel as close to the current cursor node as possible. The conventional approach puts the control panel on the diagram's side and changes its content as the user selects a new cursor item. That approach cutoff screen space out of the diagram layout. A similar approach is to draw a context menu panel on top of the diagram on the selected node's side, but this will obstruct other diagram nodes' view. The compromise design expands space around the cursor node and places context controls into that space, so the cursor template provides a padding option to preserve that required space around the cursor node. 15 | 16 | ```JavaScript 17 | import { OrgDiagram } from basicprimitivesreact; 18 | { 24 | return
; 25 | } 26 | }] 27 | } 28 | /> 29 | ``` 30 | 31 | ## Avoiding conflicts between custom controls & annotations 32 | User controls inside the cursor item template serve as the in-layout annotation, non-blocking neighboring items in the chart. If you consider adding the same UI elements into every visible node, then you need to customize the item template instead. 33 | 34 | Use the z-index style attribute to layer controls properly so other diagram visuals do not block them. 35 | 36 | Every time we select a cursor node, the control recalculates the layout, which takes time. The component has many optimizations in this regard. If nothing helps, use dynamic nodes loading, limiting the total number of simultaneously layout nodes. See the Dynamic Data Loading demo. We permanently show only the top three levels or the diagram and dynamically load and discard nodes in all other rows as the end-user enters and leaves them. 37 | 38 | ## Custom cursor template border 39 | The following example demonstrates how to create a custom cursor border having item specific color and tag element. 40 | 41 | [React](../src/Samples/CursorTemplate.jsx) 42 | 43 | -------------------------------------------------------------------------------- /src/Samples/PlaceAdvisersAboveChildren.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { Enabled, PageFitMode, GroupByType, OrgItemConfig, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | placeAdvisersAboveChildren: false 11 | } 12 | } 13 | 14 | onplaceAdvisersAboveChildrenChanged(placeAdvisersAboveChildren) { 15 | this.setState({ placeAdvisersAboveChildren }); 16 | } 17 | 18 | render() { 19 | const { placeAdvisersAboveChildren } = this.state; 20 | const config = { 21 | placeAdvisersAboveChildren, 22 | hasSelectorCheckbox: Enabled.False, 23 | pageFitMode: PageFitMode.None, 24 | arrowsDirection: GroupByType.Children, 25 | items: [ 26 | new OrgItemConfig({ 27 | id: 0, 28 | parent: null, 29 | title: "James Smith", 30 | description: "Parent Item", 31 | image: "./photos/a.png" 32 | }), 33 | new OrgItemConfig({ 34 | id: 1, 35 | parent: 0, 36 | itemType: ItemType.Adviser, 37 | adviserPlacementType: AdviserPlacementType.Right, 38 | title: "Robert Canon", 39 | description: "Adviser item", 40 | groupTitle: "Adviser", 41 | image: "./photos/b.png" 42 | }), 43 | new OrgItemConfig({ 44 | id: 3, 45 | parent: 1, 46 | title: "Fritz Stuger", 47 | description: "Regular Item", 48 | image: "./photos/d.png" 49 | }), 50 | new OrgItemConfig({ 51 | id: 4, 52 | parent: 1, 53 | title: "Ted Lucas", 54 | description: "Regular Item", 55 | image: "./photos/d.png" 56 | }), 57 | new OrgItemConfig({ 58 | id: 5, 59 | parent: 0, 60 | title: "James Nunnally", 61 | description: "Regular Item", 62 | groupTitle: "Regular", 63 | image: "./photos/d.png" 64 | }), 65 | new OrgItemConfig({ 66 | id: 6, 67 | parent: 0, 68 | title: "Harry Harter", 69 | description: "Regular Item", 70 | groupTitle: "Regular", 71 | image: "./photos/d.png" 72 | }) 73 | ] 74 | }; 75 | 76 | return <> 77 |

Place Advisers Above Children: 78 |
79 | 89 |
90 | 100 |

101 |
102 | 103 |
104 | 105 | } 106 | } 107 | 108 | export default Sample; 109 | -------------------------------------------------------------------------------- /src/Samples/PlaceAssistantsAboveChildren.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { Enabled, PageFitMode, GroupByType, OrgItemConfig, ItemType, AdviserPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | placeAssistantsAboveChildren: false 11 | } 12 | } 13 | 14 | onPlaceAssistantsAboveChildrenChanged(placeAssistantsAboveChildren) { 15 | this.setState({ placeAssistantsAboveChildren }); 16 | } 17 | 18 | render() { 19 | const { placeAssistantsAboveChildren } = this.state; 20 | const config = { 21 | placeAssistantsAboveChildren, 22 | hasSelectorCheckbox: Enabled.False, 23 | pageFitMode: PageFitMode.None, 24 | arrowsDirection: GroupByType.Children, 25 | items: [ 26 | new OrgItemConfig({ 27 | id: 0, 28 | parent: null, 29 | title: "James Smith", 30 | description: "Parent Item", 31 | image: "./photos/a.png" 32 | }), 33 | new OrgItemConfig({ 34 | id: 5, 35 | parent: 0, 36 | title: "Harry Harter", 37 | description: "Regular Item", 38 | groupTitle: "Regular", 39 | image: "./photos/d.png" 40 | }), 41 | new OrgItemConfig({ 42 | id: 6, 43 | parent: 0, 44 | title: "Fritz Stuger", 45 | description: "Regular Item", 46 | groupTitle: "Regular", 47 | image: "./photos/d.png" 48 | }), 49 | new OrgItemConfig({ 50 | id: 8, 51 | parent: 0, 52 | itemType: ItemType.Assistant, 53 | adviserPlacementType: AdviserPlacementType.Right, 54 | title: "Ted Lucas", 55 | description: "Assitant Item", 56 | groupTitle: "Assistant", 57 | image: "./photos/c.png" 58 | }), 59 | new OrgItemConfig({ 60 | id: 10, 61 | parent: 8, 62 | title: "James Nunnally", 63 | description: "Regular Item", 64 | image: "./photos/d.png" 65 | }), 66 | new OrgItemConfig({ 67 | id: 11, 68 | parent: 8, 69 | title: "Robert Canon", 70 | description: "Regular Item", 71 | image: "./photos/d.png" 72 | }) 73 | ] 74 | }; 75 | 76 | return <> 77 |

Place Assistants Above Children: 78 |
79 | 89 |
90 | 100 |

101 |
102 | 103 |
104 | 105 | } 106 | } 107 | 108 | export default Sample; 109 | -------------------------------------------------------------------------------- /src/Samples/ItemTemplateLabel.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, Colors } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 0, 10 | hasSelectorCheckbox: Enabled.True, 11 | normalItemsInterval: 10, 12 | normalLevelShift: 44, 13 | padding: { left: 10, top: 20, right: 10, bottom: 10 }, 14 | items: [ 15 | { id: 0, parent: null, label: "100%", isVisible: true, description: "Chief Executive Officer (CEO)", email: "davidalt@name.com", image: "./photos/q.png", itemTitleColor: "#4169e1", phone: "(352) 206-7599", title: "David Dalton" }, 16 | { id: 1, parent: 0, label: "50%", isVisible: true, description: "Co-Presidents, Platform Products & Services Division", email: "jeanwhit@name.com", image: "./photos/w.png", itemTitleColor: "#4b0082", phone: "(505) 791-1689", title: "Jeanna White" }, 17 | { id: 2, parent: 0, label: "50%", isVisible: true, description: "Sr. VP, Server & Tools Division", email: "jameholt@name.com", image: "./photos/e.png", itemTitleColor: "#4b0082", phone: "(262) 215-7998", title: "James Holt" }, 18 | { id: 5, parent: 1, label: "25%", isVisible: true, description: "Sr. VP, Software Server System", email: "georduon@name.com", image: "./photos/x.png", itemTitleColor: "#4b0082", phone: "(434) 406-2189", title: "George Duong" }, 19 | { id: 6, parent: 1, label: "25%", isVisible: true, description: "VP, Developer Division", email: "ashlrue@name.com", image: "./photos/n.png", itemTitleColor: "#4b0082", phone: "(515) 324-4969", title: "Ashley Rue" }, 20 | { id: 7, parent: 2, label: "25%", isVisible: true, description: "VP, Enterprise Access and Security Products Division (EASP)", email: "bonnwede@name.com", image: "./photos/i.png", itemTitleColor: "#4b0082", phone: "(412) 265-2782", title: "Bonnie Wedel" }, 21 | { id: 8, parent: 2, label: "25%", isVisible: true, description: "GM, Core File Solutions", email: "melihous@name.com", image: "./photos/p.png", itemTitleColor: "#4b0082", phone: "(630) 887-1188", title: "Melissa Houser" } 22 | ], 23 | defaultTemplateName: "LabelTemplate", 24 | templates: [{ 25 | name: "LabelTemplate", 26 | itemSize: { width: 220, height: 120 }, 27 | minimizedItemSize: { width: 3, height: 3 }, 28 | highlightPadding: { left: 2, top: 2, right: 2, bottom: 2 }, 29 | onItemRender: ({ context: itemConfig }) => { 30 | const itemTitleColor = itemConfig.itemTitleColor != null ? itemConfig.itemTitleColor : Colors.RoyalBlue; 31 | return
32 |
33 |
{itemConfig.title}
34 |
35 |
36 | {itemConfig.title} 37 |
38 |
{itemConfig.phone}
39 |
{itemConfig.email}
40 |
{itemConfig.description}
41 |
{itemConfig.label}
42 |
; 43 | } 44 | } 45 | ] 46 | }; 47 | return
48 | 49 |
; 50 | } 51 | } 52 | 53 | export default Sample; 54 | -------------------------------------------------------------------------------- /docs/SelectingHighlightItem.md: -------------------------------------------------------------------------------- 1 | # Highlight item & Mouse over feedback 2 | Highlight provides visual feedback for mouseover and indicates the current node of keyboard focus in the chart. It is a lightweight operation that does not change the diagram layout. Your application can use highlight API to display some node context information in the side panel. 3 | 4 | Use `OrgConfig.highlightItem` property to set or get the currently highlighted node in the diagram. 5 | 6 | When the end-user moves the mouse cursor, the control may highlight nodes within some space around the cursor defined with the option `highlightGravityRadius`. It is handy when we need to click on a small marker. Still, at the same time, it may decrease the control's performance if the radius is too big because the component finds the nearest node within the area of that radius, and that radius may cover hundreds or thousands of nodes. 7 | 8 | If the user clicks outside of the highlighted item, then control moves the cursor item to it. See `cursorItem` property of the `OrgConfig` or `FamConfig` configuration objects. 9 | 10 | Every node must have a unique id. See `id` property of `OrgItemConfig` configuration object. Use those ids when you set the highlightItem option value. 11 | 12 | 13 | If you need to hide highlight, you have to set the highlightItem option to null. If you need to disable highlight functionality in control, it is useless on mobile devices; you have to change the `navigationMode` option to `NavigationMode.CursorOnly`. 14 | 15 | ```JavaScript 16 | import { OrgDiagram } from basicprimitivesreact; 17 | import { NavigationMode } from basicprimitives; 18 | 24 | ``` 25 | 26 | ## Keyboard navigation 27 | The control is keyboard focusable. So when it gets focus with TAB or mouse click, there is a blue "outline" around it indicating active keyboard focus. 28 | 29 | The control supports keyboard arrows keys to choose highlighted node and `Enter` key to set cursor for it. So when the component gets focused, you have to see the blue outline, then you have to use Arrows keys to highlight the item you want to move the cursor to, and then press Enter key to set the cursor to it. Don't forget that if you need to expand marker nodes, you need to move the cursor node close. 30 | 31 | Suppose your node templates contain HTML elements supporting keyboard focus and keyboard interactivity. All of them are going to have their TAB order along with the component itself. 32 | 33 | Use buttons panel to place HTML elements having a keyboard or mouse interactivity elements above all other diagram elements, so they are not blocked. See the context buttons sample for more details. 34 | 35 | ## Events 36 | The following example shows how to set highlighted items programmatically and handle notifications about the user's mouse cursor moves in the chart with `onHighlightChange` event. It has a preceding `onHighlightChanging` event, which is needed to make application state changes and avoid unnecessary components' internal state change and the following layout updates. 37 | 38 | If you want to avoid internal component state update and the following rendering cycle, then you have to return 'true' from the `onHighlightChanging` event handler: 39 | 40 | ```JavaScript 41 | import { OrgDiagram } from basicprimitivesreact; 42 | { 43 | const { context } = data; 44 | setHighlightItem(context.id); 45 | // Return true in order to suppress set highlight item change in control 46 | // it will be updated via subsequent state change of application 47 | return true; 48 | }} /> 49 | ``` 50 | 51 | [React](../src/Samples/SelectingHighlightItem.jsx) 52 | -------------------------------------------------------------------------------- /src/Samples/ShapeAnnotation.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { OrgDiagram } from '../Diagrams'; 3 | import { Size, Thickness, ShapeAnnotationConfig, BackgroundAnnotationConfig, AnnotationType, Colors, PlacementType, ShapeType, LineType, GroupByType, PageFitMode, Enabled } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | items: [ 9 | /* JSON noname objects equivalent to OrgItemConfig */ 10 | { id: 0, parent: null, title: "James Smith", description: "VP, Public Sector", image: "./photos/a.png" }, 11 | { id: 1, parent: 0, title: "Ted Lucas", description: "VP, Human Resources", image: "./photos/b.png" }, 12 | { id: 2, parent: 0, title: "Fritz Stuger", description: "Business Solutions, US", image: "./photos/c.png" }, 13 | { id: 3, parent: 0, title: "Mike Wazowski", description: "Business Analyst, Canada", image: "./photos/o.png" }, 14 | { id: 4, parent: 3, title: "Best Friend", description: "Business Analyst, Mexico", image: "./photos/f.png" } 15 | ], 16 | annotations: [ 17 | /* JSON noname object equivalent to ConnectorAnnotationConfig */ 18 | { 19 | annotationType: AnnotationType.Shape, 20 | items: [0], 21 | label: <>
1
Oval, 24 | labelSize: new Size(100, 30), 25 | labelPlacement: PlacementType.Right, 26 | shapeType: ShapeType.Oval, 27 | borderColor: Colors.Green, 28 | offset: new Thickness(2, 2, 2, 2), 29 | lineWidth: 2, 30 | selectItems: true, 31 | lineType: LineType.Dashed 32 | }, 33 | /* JSON noname object equivalent to ConnectorAnnotationConfig */ 34 | { 35 | annotationType: AnnotationType.Shape, 36 | items: [2, 3], 37 | label: <>
2
Cross Out, 40 | labelSize: { width: 100, height: 30 }, 41 | labelPlacement: PlacementType.Right, 42 | shapeType: ShapeType.CrossOut, 43 | borderColor: Colors.Navy, 44 | offset: { left: 2, top: 2, right: 2, bottom: 2 }, 45 | lineWidth: 2, 46 | selectItems: true, 47 | lineType: LineType.Dashed 48 | }, 49 | /* prototype based instantiation */ 50 | new ShapeAnnotationConfig({ 51 | items: [1], 52 | label: <>
3
Triangle, 55 | labelSize: new Size(100, 30), 56 | labelPlacement: PlacementType.Bottom, 57 | shapeType: ShapeType.Triangle, 58 | borderColor: Colors.Red, 59 | offset: new Thickness(2, 2, 2, 2), 60 | lineWidth: 2, 61 | selectItems: true, 62 | lineType: LineType.Dashed 63 | }) 64 | , 65 | new BackgroundAnnotationConfig({ 66 | items: [2, 3], 67 | borderColor: "#f8e5f9", 68 | fillColor: "#e5f9f8", 69 | lineWidth: 2, 70 | selectItems: true, 71 | includeChildren: true, 72 | lineType: LineType.Dotted, 73 | offset: new Thickness(20, 20, 20, 20) 74 | }) 75 | ], 76 | cursorItem: 0, 77 | hasSelectorCheckbox: Enabled.True, 78 | normalItemsInterval: 40 /* defines padding around items of background annotations*/, 79 | arrowsDirection: GroupByType.Parents, 80 | pageFitMode: PageFitMode.None 81 | }; 82 | 83 | return <> 84 |
85 | 86 |
87 | 88 | } 89 | } 90 | 91 | export default Sample; 92 | -------------------------------------------------------------------------------- /docs/FamilyConnectorsVisualization.md: -------------------------------------------------------------------------------- 1 | # Family Connectors Visualization 2 | The core difference between the Family diagram and Organizational Chart is the support of multiple parents. This feature derives a lot of complexity and problems. If we look at the organizational chart, we may see that connection lines play no role in the visualization. We don't need to visually trace connection lines between nodes with our eyes to understand their mutual relations. This fact dramatically simplifies the reading of the diagram. The relative node placement on a hierarchical chart, additional spaces between branches, gives a strong visual indication of mutual relations between nodes. When we look at family diagrams supporting multiple parents, this is not the case anymore. Connectors are not secondary elements anymore, now they provide information about relations between nodes, and their excessive number creates visual clutter in the diagram, which makes their visualization virtually useless. Look at the following example of the complete bipartite graph. It has every parent node connected with every child node. 3 | 4 | ![Complete Bipartite Graph](images/cbp88.png) 5 | 6 | The complete bipartite graph is an extreme example of family relations. Still, in the case of support of the multiple parents, it is a valid use case, so to eliminate this connection lines mass and make relations more understandable, the control automatically groups connectors into bundles, so it produces the following set of links: 7 | 8 | ![Complete Bipartite Graph Bundled](images/cbp88bundled.png) 9 | 10 | This visualization is better, but it uncovers another problem. Many parents and children do not let us see them together within screen boundaries, so to make the diagram more compact, our component supports the clustering of the nodes into matrixed formation to occupy the least space possible. To enable nodes clustering into matrixed shape, set the enableMatrixLayout option to true. 11 | 12 | ```JavaScript 13 | import { FamDiagram } from basicprimitivesreact; 14 | 19 | ``` 20 | 21 | [React](../src/Samples/MatrixLayoutInFamilyChart.jsx) 22 | 23 | Another typical problem in connectors visualization is excessive grandparents relations. It is the situation when an item has direct links to all its grandparents. Usually, when we draw the family diagram, we are more interested in showing dependencies over actual relations. We know that the grandparent precedes the parent, the great grandparent precedes the grandparent, and so on. So this precedence defines the indirect link between the child node and grand-grandparent. So direct relation visualization between the child node and its grandparents can be omitted from the diagram and replaced with dynamic annotations. Look at the following example where every child references all preceding parents: 24 | 25 | ![Excessive grand parents relations](images/cbp88everyparent.png) 26 | 27 | As you may see, the control already eliminated many connections via making bundles, so we don't see every connection between nodes, but still, this diagram has a lot of connections to trace. To hide direct connections to grandparents, set option the hideGrandParentsConnectors to true and get the following layout: 28 | 29 | ```JavaScript 30 | import { FamDiagram } from basicprimitivesreact; 31 | 35 | ``` 36 | 37 | [React](../src/Samples/FamilyHideGrandParentsConnections.jsx) 38 | 39 | So we got a relatively clean relations diagram between nodes. We still have all relations in place. The only difference is that grandparent's connections go through actual parents, so we need to visualize them dynamically with highlight path annotations. As we browse the diagram nodes, we can highlight all directly linked parents and children with Connector Path Annotations and set dynamically custom Item Template for them. -------------------------------------------------------------------------------- /docs/FirstOrganizationalChart.md: -------------------------------------------------------------------------------- 1 | # First Organizational Chart for ReactJS 2 | 3 | Basic Primitives Diagrams for React - data visualization component library that implements organizational chart and multi-parent dependency tree diagrams. It renders nodes using ReactJS Virtual DOM without direct DOM manipulations, so it compliant with all React features and popular react extensions like [React Drag & Drop](http://react-dnd.github.io/react-dnd/about) and [React Context](https://reactjs.org/docs/context.html) 4 | 5 | Our site contains samples and demo published on GitHub. The recommended way to get familiar with our library is to clone our GitHub [react](https://github.com/BasicPrimitives/react) repository and run it locally. It contains simple, single-page examples. If you need to see more complex end to end applications developed in ReactJS using hooks and Redux state management, then clone our GitHub [react-demos](https://github.com/BasicPrimitives/react-demo) repository 6 | 7 | ## NPM packages: 8 | Use the following commands to import our components from npm packages. 9 | ```JavaScript 10 | import React, { Component } from 'react'; 11 | import { OrgDiagram } from 'basicprimitivesreact'; 12 | import { Enabled, PageFitMode } from 'basicprimitives'; 13 | ``` 14 | * `basicprimitivesreact` - react components 15 | * `basicprimitives` - the core of the library which contains common configuration objects and enumerations. 16 | 17 | [React](../src/Samples/FirstOrganizationalChart.jsx) 18 | 19 | ## Create React App Sample 20 | The diagramming components work in React applications created with [`create-react-app`](https://facebook.github.io/create-react-app/), use the following steps to create and run the first diagram: 21 | 22 | ``` 23 | npx create-react-app test1 24 | cd test1 25 | 26 | yarn add basicprimitivesreact 27 | 28 | yarn start 29 | ``` 30 | 31 | Add following changes into App.js 32 | 33 | ```JavaScript 34 | import React from 'react'; 35 | import { OrgDiagram } from 'basicprimitivesreact'; 36 | import { PageFitMode, Enabled } from 'basicprimitives'; 37 | 38 | var photos = { 39 | a: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAA8CAIAAACrV36WAAAAAXNSR0IArs4c6QAAAARn' + 40 | 'QU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGnSURBVGhD7dnBbQJBDAVQk1o2QjlQwKYGzpSwKQfq4IxIC' + 41 | 'RTB9jLZHCJFwWv7/7EiDt6zmX2yPYMHNq01eb7n5flI36JiIXWpbFW2kAwgsdVblS0kA0hs9db/ZWs+vW/Wno9PxPE3dh' + 42 | 'ls6Od+HI1XT1d64Sb8R5utEulwdbA8VY+LZ/kqkfF456pBHxDz5Xxze/p2vsxukBbAshTVOE0PO4B2cUlWKrgUTKsrV0e' + 43 | 'ut3RVU/cm5aKKqPXVbjuIDPtDUh2JImq1+jmjkupIFNFStXadHncWXkecpb3393me4oJZnionXyjLV6W4QFZEleHCWNG+' + 44 | '0eKggQJiRVV6vhAXwoqrul0AC1H1uuIsTLUyukYH1jBL7WJ8lgq6oqwkVXSQDrLSVEFXjJWoirlCrFRVyBVhJasirgCr6' + 45 | '5tEv7a5A5jL0tcN7vNl9OVcHqtXRbocVr+Kc9k3H/3qPL69Ise7dh0SsS+2JmtFddgvdy/gGbY7Jdp2GRcyrlu1BfUjxt' + 46 | 'iPRm/lqVbGHOMHnU39zQm0I/UbBLA+GVosJHGVrcoWkgEktnoLydYXkF/LiXG21MwAAAAASUVORK5CYII=' 47 | }; 48 | 49 | function App() { 50 | const config = { 51 | pageFitMode: PageFitMode.AutoSize, 52 | autoSizeMinimum: { width: 100, height: 100 }, 53 | cursorItem: 0, 54 | highlightItem: 0, 55 | hasSelectorCheckbox: Enabled.True, 56 | items: [ 57 | { 58 | id: 0, 59 | parent: null, 60 | title: 'James Smith', 61 | description: 'VP, Public Sector', 62 | image: photos.a 63 | }, 64 | { 65 | id: 1, 66 | parent: 0, 67 | title: 'Ted Lucas', 68 | description: 'VP, Human Resources', 69 | image: photos.a 70 | }, 71 | { 72 | id: 2, 73 | parent: 0, 74 | title: 'Fritz Stuger', 75 | description: 'Business Solutions, US', 76 | image: photos.a 77 | } 78 | ] 79 | }; 80 | 81 | return ( 82 |
83 | 84 |
85 | ); 86 | } 87 | 88 | export default App; 89 | ``` 90 | -------------------------------------------------------------------------------- /src/Samples/MultipleFamiliesOrdering.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { FamDiagram } from '../Diagrams'; 3 | import { PageFitMode, Enabled, GroupByType, AdviserPlacementType, AnnotationType, Size, ConnectorShapeType, Colors, LineType, ConnectorPlacementType } from 'basicprimitives'; 4 | 5 | class Sample extends Component { 6 | render() { 7 | const config = { 8 | pageFitMode: PageFitMode.None, 9 | cursorItem: 2, 10 | linesWidth: 1, 11 | linesColor: "black", 12 | hasSelectorCheckbox: Enabled.False, 13 | arrowsDirection: GroupByType.Parents, 14 | showExtraArrows: false, 15 | items: [ 16 | { id: 1, title: "David Dalton", groupTitle: "Family 1", label: "David Dalton", description: "1, Chief Executive Officer (CEO)", phone: "352-206-7599", email: "davidalt@name.com", image: "./photos/q.png", itemTitleColor: "#4169e1" }, 17 | { id: 2, relativeItem: 1, placementType: AdviserPlacementType.Right, position: 1, title: "Steven Lacombe", label: "Steven Lacombe", description: "2, GM, Platform Strategy", phone: "805-800-7397", email: "stevlaco@name.com", image: "./photos/a.png", itemTitleColor: "#4b0082" }, 18 | { id: 3, parents: [1, 2], title: "Nancy Smith", label: "Nancy Smith", description: "3, GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" }, 19 | { id: 4, parents: [3], title: "Ann Smith", label: "Nancy Smith", description: "4, GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" }, 20 | { id: 5, parents: [3], title: "Ella Smith", label: "Nancy Smith", description: "5, GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" }, 21 | 22 | { id: 10, relativeItem: 1, placementType: AdviserPlacementType.Right, position: 1, title: "David Dalton", groupTitle: "Family 2", label: "David Dalton", description: "Chief Executive Officer (CEO)", phone: "352-206-7599", email: "davidalt@name.com", image: "./photos/q.png", itemTitleColor: "#4169e1" }, 23 | { id: 20, relativeItem: 10, placementType: AdviserPlacementType.Right, position: 1, title: "Steven Lacombe", label: "Steven Lacombe", description: "GM, Platform Strategy", phone: "805-800-7397", email: "stevlaco@name.com", image: "./photos/a.png", itemTitleColor: "#4b0082" }, 24 | { id: 30, parents: [10, 20], title: "Nancy Smith", label: "Nancy Smith", description: "GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" }, 25 | { id: 40, parents: [30], title: "Ann Smith", label: "Nancy Smith", description: "GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" }, 26 | { id: 50, parents: [30], title: "Ella Smith", label: "Nancy Smith", description: "GM, Strategic Marketing and Communications", phone: "631-787-3495", email: "nancsmit@name.com", image: "./photos/s.png", itemTitleColor: "#4b0082" } 27 | ], 28 | annotations: [ 29 | { 30 | annotationType: AnnotationType.Connector, 31 | fromItem: 3, 32 | toItem: 30, 33 | label:
3
, 34 | labelSize: new Size(40, 20), 35 | connectorShapeType: ConnectorShapeType.OneWay, 36 | color: Colors.Red, 37 | offset: 0, 38 | lineWidth: 2, 39 | lineType: LineType.Dashed, 40 | connectorPlacementType: ConnectorPlacementType.Offbeat, 41 | selectItems: false 42 | } 43 | ] 44 | }; 45 | 46 | return
47 | 48 |
49 | } 50 | } 51 | 52 | export default Sample; 53 | --------------------------------------------------------------------------------