Shape Arrow Head

In SpreadJS, you can add connector shapes using different kind of arrows, including: the normal arrow (no side is pointed), the basic arrow (with both sides pointed), the open arrow, the stealth arrow, the oval arrow and the diamond arrow.

You can customize the properties of connectorShapes using the ConnectorShape API: style: Gets or sets the style of the connector shape.
import * as React from 'react'; import { createRoot } from 'react-dom/client'; import './styles.css'; import { AppFunc } from './app-func'; // import { App } from './app-class'; // 1. Functional Component sample createRoot(document.getElementById('app')).render(<AppFunc />); // 2. Class Component sample // createRoot(document.getElementById('app')).render(<App />);
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-shapes" import { SpreadSheets } from '@mescius/spread-sheets-react'; import { Panel } from "./panel"; let spread = null; export function AppFunc() { const [showConnectorPropPanel, setShowConnectorPropPanel] = React.useState(false); const updateConnectorShapeStyle = (action, value) => { let sheet = spread.getActiveSheet(); let activeShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected() && sp instanceof GC.Spread.Sheets.Shapes.ConnectorShape; }); if (activeShape.length > 0) { activeShape.forEach((shape) => { _setConnectorShapeStyle(shape, action, value); }); sheet.repaint(); } } const _setConnectorShapeStyle = (shape, action, value) => { let shapeStyle = shape.style(); let shapeStyleLine = shapeStyle.line; switch (action) { case "beginStyle": { shapeStyleLine.beginArrowheadStyle = value; break; } case "beginWidth": { shapeStyleLine.beginArrowheadWidth = value; break; } case "beginLength": { shapeStyleLine.beginArrowheadLength = value; break; } case "endStyle": { shapeStyleLine.endArrowheadStyle = value; break; } case "endWidth": { shapeStyleLine.endArrowheadWidth = value; break; } case "endLength": { shapeStyleLine.endArrowheadLength = value; break; } } shape.style(shapeStyle); } const setConnectorPropVisibility = (isShow) => { setShowConnectorPropPanel(isShow); } const initSpread = (currSpread) => { spread = currSpread; var line1 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.straight, 20, 40, 200, 110); var line2 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.straight, 20, 110, 200, 190); var line3 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.elbow, 300, 40, 500, 90); var line4 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.elbow, 300, 130, 500, 190); var lineShapeStyle = line1.style(); lineShapeStyle.line.width = 3; lineShapeStyle.line.color = "#82BC00"; line1.style(lineShapeStyle); line2.style(lineShapeStyle); line3.style(lineShapeStyle); line4.style(lineShapeStyle); bindSpreadEvent(); } const bindSpreadEvent = () => { spread.bind(GC.Spread.Sheets.Events.ShapeSelectionChanged, function () { let sheet = spread.getActiveSheet(); var selectedShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); var isShapeSelected = false, isConnectorSelected = false; if (selectedShape.length > 0) { selectedShape.forEach((shape) => { if (!isShapeSelected && shape instanceof GC.Spread.Sheets.Shapes.Shape) { isShapeSelected = true; } else if (!isConnectorSelected && shape instanceof GC.Spread.Sheets.Shapes.ConnectorShape) { isConnectorSelected = true; } }); setConnectorPropVisibility(isConnectorSelected); } else { setConnectorPropVisibility(false); } }) } return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => initSpread(spread)}> </SpreadSheets> </div> <Panel showConnectorPropPanel={showConnectorPropPanel} updateConnectorShapeStyle={(action, value) => { updateConnectorShapeStyle(action, value) }}> </Panel> </div>); }
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-shapes" import { SpreadSheets } from '@mescius/spread-sheets-react'; import { Panel } from "./panel"; const Component = React.Component; export class App extends Component { constructor(props) { super(props); this.spread = null; this.autoGenerateColumns = false; this.state = { showBorderPanel: false, showConnectorPropPanel: false, showShapeTxtPanel: false, showShapeFillPropPanel: false } } render() { const { showBorderPanel, showConnectorPropPanel, showShapeTxtPanel, showShapeFillPropPanel } = this.state; return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => this.initSpread(spread)}> </SpreadSheets> </div> <Panel showBorderPanel={showBorderPanel} showConnectorPropPanel={showConnectorPropPanel} showShapeTxtPanel={showShapeTxtPanel} showShapeFillPropPanel={showShapeFillPropPanel} insertShape={(addShapeType) => { this.insertShape(addShapeType) }} insertConnectShape={(addConnectorShapeType) => { this.insertConnectShape(addConnectorShapeType) }} updateShapeStyle={(action, value) => { this.updateShapeStyle(action, value) }} updateShapeBorderStyle={(action, value) => { this.updateShapeBorderStyle(action, value) }} updateConnectorShapeStyle={(action, value) => { this.updateConnectorShapeStyle(action, value) }}> </Panel> </div>); } updateShapeStyle(action, value) { let sheet = this.spread.getActiveSheet(); let activeShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); if (activeShape.length > 0) { activeShape.forEach((shape) => { if (shape instanceof GC.Spread.Sheets.Shapes.Shape) { this._setShapeStyle(shape, action, value); } }); sheet.repaint(); } } updateShapeBorderStyle(action, value) { let sheet = this.spread.getActiveSheet(); let activeShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); if (activeShape.length > 0) { activeShape.forEach(function (shape) { let shapeStyle = shape.style(); shapeStyle.line[action] = value; shape.style(shapeStyle); }); sheet.repaint(); } } updateConnectorShapeStyle(action, value) { let sheet = this.spread.getActiveSheet(); let activeShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected() && sp instanceof GC.Spread.Sheets.Shapes.ConnectorShape; }); if (activeShape.length > 0) { activeShape.forEach((shape) => { this._setConnectorShapeStyle(shape, action, value); }); sheet.repaint(); } } _setConnectorShapeStyle(shape, action, value) { let shapeStyle = shape.style(); let shapeStyleLine = shapeStyle.line; switch (action) { case "beginStyle": { shapeStyleLine.beginArrowheadStyle = value; break; } case "beginWidth": { shapeStyleLine.beginArrowheadWidth = value; break; } case "beginLength": { shapeStyleLine.beginArrowheadLength = value; break; } case "endStyle": { shapeStyleLine.endArrowheadStyle = value; break; } case "endWidth": { shapeStyleLine.endArrowheadWidth = value; break; } case "endLength": { shapeStyleLine.endArrowheadLength = value; break; } } shape.style(shapeStyle); } _setShapeStyle(shape, action, value) { if (action === 'rotate') { shape.rotate(value); } else if (action === 'text') { shape.text(value); } else { var shapeStyle = shape.style(); if (action === 'background') { shapeStyle.fill.color = value; } else if (action === 'transparency') { shapeStyle.fill.transparency = value; } else if (action === 'color') { shapeStyle.textEffect.color = value; } else if (action === 'transparencyTxt') { shapeStyle.textEffect.transparency = value; } else if (action === 'font') { shapeStyle.textEffect.font = value; } else if (action === 'hAlign') { shapeStyle.textFrame.hAlign = parseInt(value); } else if (action === 'vAlign') { shapeStyle.textFrame.vAlign = parseInt(value); } shape.style(shapeStyle); } } insertShape(addShapeType) { let sheet = this.spread.getActiveSheet(), shapes = sheet.shapes, total = shapes.all().length; let x = 40 + (total % 2) * 250, y = parseInt(total / 2) * 200 + 20; let shape = shapes.add('', addShapeType, x, y); this._setShapeStyle(shape, 'hAlign', 1); } insertConnectShape(addConnectorShapeType) { let sheet = this.spread.getActiveSheet(), shapes = sheet.shapes, total = shapes.all().length; let x = 40 + (total % 2) * 250, y = parseInt(total / 2) * 200 + 20; shapes.addConnector('', parseInt(addConnectorShapeType), x, y, x + 200, y + 200); } setBorderPropVisibility(isShow) { this.setState({ showBorderPanel: isShow }); } setConnectorPropVisibility(isShow) { this.setState({ showConnectorPropPanel: isShow }); } setShapePropVisibility(isShow) { this.setState({ showShapeTxtPanel: isShow, showShapeFillPropPanel: isShow }) } initSpread(spread) { this.spread = spread; var line1 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.straight, 20, 40, 200, 110); var line2 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.straight, 20, 110, 200, 190); var line3 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.elbow, 300, 40, 500, 90); var line4 = spread.getActiveSheet().shapes.addConnector("line", GC.Spread.Sheets.Shapes.ConnectorType.elbow, 300, 130, 500, 190); var lineShapeStyle = line1.style(); lineShapeStyle.line.width = 3; lineShapeStyle.line.color = "#82BC00"; line1.style(lineShapeStyle); line2.style(lineShapeStyle); line3.style(lineShapeStyle); line4.style(lineShapeStyle); this.bindSpreadEvent(); } bindSpreadEvent() { let spread = this.spread, self = this; spread.bind(GC.Spread.Sheets.Events.ShapeSelectionChanged, function () { let sheet = spread.getActiveSheet(); var selectedShape = sheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); var isShapeSelected = false, isConnectorSelected = false; if (selectedShape.length > 0) { selectedShape.forEach((shape) => { if (!isShapeSelected && shape instanceof GC.Spread.Sheets.Shapes.Shape) { isShapeSelected = true; } else if (!isConnectorSelected && shape instanceof GC.Spread.Sheets.Shapes.ConnectorShape) { isConnectorSelected = true; } }); self.setShapePropVisibility(isShapeSelected); self.setConnectorPropVisibility(isConnectorSelected); self.setBorderPropVisibility(true); } else { self.setShapePropVisibility(false); self.setConnectorPropVisibility(false); self.setBorderPropVisibility(false); } }) } }
import * as React from 'react'; import GC from '@mescius/spread-sheets'; export function Panel(props) { const arrowheadLength = { "short": 0, "medium": 1, "long": 2 }; const arrowheadWidth = { "narrow": 0, "medium": 1, "wide": 2 }; const [panelInfo, setPanelInfo] = React.useState({ beginStyle: 3, beginWidth: 1, beginLength: 2, endStyle: 3, endWidth: 1, endLength: 2, arrowHeadLengthList: getEnumList(arrowheadLength), arrowHeadWidthList: getEnumList(arrowheadWidth), arrowHeadStyle: getEnumList(GC.Spread.Sheets.Shapes.ArrowheadStyle) }); React.useEffect(() => { setPanelInfo({ beginStyle: 3, beginWidth: 1, beginLength: 2, endStyle: 3, endWidth: 1, endLength: 2, arrowHeadLengthList: getEnumList(arrowheadLength), arrowHeadWidthList: getEnumList(arrowheadWidth), arrowHeadStyle: getEnumList(GC.Spread.Sheets.Shapes.ArrowheadStyle) }); }, [props.showConnectorPropPanel]); return (<div class="options-container"> <div class="option-row"> Select a shape and change the arrow-head properties to see the effect. </div> <div id="divideLine" class="divide-line"></div> { props.showConnectorPropPanel && <div v-show="showConnectorPropPanel" class="option-row"> <label for="beginArrowheadStyle">Begin Arrowhead Style:</label> <select value={panelInfo.beginStyle} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, beginStyle: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadStyle.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select> <input onClick={() => { props.updateConnectorShapeStyle('beginStyle', panelInfo.beginStyle) }} type="button" class='arrow-action-button' value="Set" /> <label>Begin Arrowhead Width:</label> <select value={panelInfo.beginWidth} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, beginWidth: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadWidthList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { props.updateConnectorShapeStyle('beginWidth', panelInfo.beginWidth) }} class='arrow-action-button' value="Set" /> <label>Begin Arrowhead Length:</label> <select value={panelInfo.beginLength} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, beginLength: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadLengthList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { props.updateConnectorShapeStyle('beginLength', panelInfo.beginLength) }} class='arrow-action-button' value="Set" /> <label for="endArrowheadStyle">End Arrowhead Style:</label> <select value={panelInfo.endStyle} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, endStyle: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadStyle.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { props.updateConnectorShapeStyle('endStyle', panelInfo.endStyle) }} class='arrow-action-button' value="Set" /> <label>End Arrowhead Width:</label> <select value={panelInfo.endWidth} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, endWidth: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadWidthList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { props.updateConnectorShapeStyle('endWidth', panelInfo.endWidth) }} class='arrow-action-button' value="Set" /> <label>End Arrowhead Length:</label> <select value={panelInfo.endLength} class="shapeSelect" onChange={(event) => { setPanelInfo({ ...panelInfo, endLength: parseInt(event.target.value, 10) }); }}> {panelInfo.arrowHeadLengthList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { props.updateConnectorShapeStyle('endLength', panelInfo.endLength) }} class='arrow-action-button' value="Set" /> </div > } </div >); } function getEnumList(enumObject) { let names = []; for (var name in enumObject) { if (name === "none" || (parseInt(name, 10)) == name) { continue; } names.push({ name: name, value: enumObject[name] }); } names.sort(function (a, b) { return a.name > b.name ? 1 : -1 }); return names; }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/react/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <!-- SystemJS --> <script src="$DEMOROOT$/en/react/node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('$DEMOROOT$/en/lib/react/license.js').then(function () { System.import('./src/app'); }); </script> </head> <body> <div id="app"></div> </body> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 280px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding-left: 5px; } .divide-line { width: 100%; height: 1px; background: #cbcbcb; margin-top: 10px; margin-bottom: 3px; } .title { text-align: center; font-weight: bold; } label { display: block; margin-top: 15px; margin-bottom: 5px; } p { padding: 2px 10px; background-color: #F4F8EB; } input { width: 160px; margin-left: 10px; display: inline; } input[type=button] { width: 50px; margin-left: 1px; } select { width: 160px; margin-left: 10px; display: inline; } textarea { width: 160px; margin-left: 10px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } #app{ width: 100%; height: 100%; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/index.js', '@mescius/spread-sheets-shapes': 'npm:@mescius/spread-sheets-shapes/index.js', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js', 'react': 'npm:react/cjs/react.production.js', 'react-dom': 'npm:react-dom/cjs/react-dom.production.js', 'react-dom/client': 'npm:react-dom/cjs/react-dom-client.production.js', 'scheduler': 'npm:scheduler/cjs/scheduler.production.js', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);