Custom Shape

SpreadJS gives you the ability to create custom shapes from a model that utilizes formulas. In this example, a custom shape model is created in a similar way to canvas drawing graphics to draw a car damage diagram.

Description
app.jsx
app-func.jsx
app-class.jsx
index.html
styles.css
Copy to CodeMine

You can create a custom shape with a model, as shown below:

    var model = {
    left: 100,
    top: 100,
    width: 100,
    height: 100,
    options: {
      fill: {
        type: 1,
        color: "red",
        transparency: "0.5",
      }
    },
    path: [
      [
       ["M", 9, 36],
       ["L", 84, 2],
       ["L", 116, 0],
       ["L", 140, 0],
       ["L", 134, 74],
       ["L", 140, 112],
       ["L", 12, 112],
       ["L", 7, 105],
       ["L", 4, 96],
       ["L", 1, 81],
       ["L", 2, 64],
       ["L", 5, 49],
       ["Z"]
      ]
    ]
    };
    sheet.shapes.add('name', model);

For path, the meaning of each parameter is:

  • "M": means moveTo(x,y)
  • "L": means lineTo(x,y)
  • "B": means bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
  • "Q": means quadraticCurveTo(cpx, cpy, x, y)
  • "A": means arc(x, y, r, startAngle, endAngle)
  • "A":' means arcTo(x1, y1, x2, y2, r)
  • "Z": means closePath
You can create a custom shape with a model, as shown below: For path, the meaning of each parameter is: "M": means moveTo(x,y) "L": means lineTo(x,y) "B": means bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) "Q": means quadraticCurveTo(cpx, cpy, x, y) "A": means arc(x, y, r, startAngle, endAngle) "A":' means arcTo(x1, y1, x2, y2, r) "Z": means closePath
import * as React from 'react'; import * as ReactDOM from 'react-dom'; import './styles.css'; import { AppFunc } from './app-func'; // import { App } from './app-class'; // 1. Functional Component sample ReactDOM.render(<AppFunc />, document.getElementById('app')); // 2. Class Component sample // ReactDOM.render(<App />, document.getElementById('app'));
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-shapes"; import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react'; function initDamageAreaShapes(spread) { let sheet = spread.getSheet(0); let damageAreaSheet = spread.getSheet(1); damageAreaSheet.setColumnWidth(0,120); var table = damageAreaSheet.tables.add("table1", 0, 0, 7, 4, GC.Spread.Sheets.Tables.TableThemes.medium4); table.filterButtonVisible(false); var damageAreas = ["Area", "Left", "Top", "Color"], carFront = ["carFront", 50, 50, "orange"], carFrontDoor = ["carFrontDoor", 197, 23, "orange"], carBackDoor = ["carBackDoor", 332, 23, "orange"], carBack = ["carBack", 405, 24, "orange"], carFrontWheel = ["carFrontWheel", 45, 64, "orange"], carBackWheel = ["carBackWheel", 361, 64, "orange"]; damageAreaSheet.setArray(0, 0, [ damageAreas, carFront, carFrontDoor, carBackDoor, carBack, carFrontWheel, carBackWheel ]); let carFrontModel = { left: "=Damage_Areas!B2", top: "=Damage_Areas!C2", width: 157, height: 85, options: { fill: { type: 1, color: "=Damage_Areas!D2", transparency: "0.5" } }, path: [ [ ["M", 6, 48], ["L", 21, 29], ["L", 59, 20], ["L", 136, 14], ["L", 157, 8], ["L", 150, 24], ["L", 148, 47], ["L", 150, 69], ["L", 157, 85], ["L", 140, 85], //Wheel well ["L", 136, 71], ["L", 128, 58], ["L", 119, 52], ["L", 107, 47], ["L", 94, 46], ["L", 83, 47], ["L", 68, 52], ["L", 60, 61], ["L", 54, 70], ["L", 50, 85], ["L", 21, 85], ["L", 13, 71], ["L", 2, 67], ["Z"] ] ] }; let carFrontDoorModel = { left: "=Damage_Areas!B3", top: "=Damage_Areas!C3", width: 140, height: 112, options: { fill: { type: 1, color: "=Damage_Areas!D3", transparency: "0.5" } }, path: [ [ ["M", 9, 36], ["L", 84, 2], ["L", 116, 0], ["L", 140, 0], ["L", 134, 74], ["L", 140, 112], ["L", 12, 112], ["L", 7, 105], ["L", 4, 96], ["L", 1, 81], ["L", 2, 64], ["L", 5, 49], ["Z"] ] ] }; let carBackDoorModel = { left: "=Damage_Areas!B4", top: "=Damage_Areas!C4", width: 121, height: 111, options: { fill: { type: 1, color: "=Damage_Areas!D4", transparency: "0.5" } }, path: [ [ ["M", 6, 0], ["L", 71, 1], ["L", 95, 20], ["L", 115, 44], ["L", 121, 53], ["L", 117, 62], ["L", 105, 68], ["L", 87, 85], ["L", 78, 100], ["L", 75, 111], ["L", 5, 111], ["L", 2, 97], ["L", 0, 79], ["L", 1, 61], ["L", 3, 38], ["Z"] ] ] }; let carBackModel = { left: "=Damage_Areas!B5", top: "=Damage_Areas!C5", width: 168, height: 110, options: { fill: { type: 1, color: "=Damage_Areas!D5", transparency: "0.5" } }, path: [ [ ["M", 0, 0], ["L", 51, 9], ["L", 110, 34], ["L", 154, 43], ["L", 163, 49], ["L", 166, 55], ["L", 152, 55], ["L", 149, 73], ["L", 168, 80], ["L", 168, 91], ["L", 164, 97], ["L", 159, 97], ["L", 153, 110], ["L", 100, 110], //Wheel well ["L", 96, 97], ["L", 86, 84], ["L", 69, 74], ["L", 60, 72], ["L", 50, 73], ["L", 40, 73], ["L", 32, 78], ["L", 24, 85], ["L", 19, 92], ["L", 14, 101], ["L", 13, 110], ["L", 3, 110], ["L", 9, 96], ["L", 17, 81], ["L", 31, 69], ["L", 44, 61], ["L", 49, 55], ["L", 44, 44], ["L", 29, 27], ["L", 14, 12], ["Z"] ] ] }; let carFrontWheelModel = { left: "=Damage_Areas!B6", top: "=Damage_Areas!C6", width: 168, height: 168, options: { fill: { type: 1, color: "=Damage_Areas!D6", transparency: "0.5" } }, path: [ [ ["A", 100, 75, 40, 0, 2 * Math.PI], ["Z"] ] ] }; let carBackWheelModel = { left: "=Damage_Areas!B7", top: "=Damage_Areas!C7", width: 168, height: 168, options: { fill: { type: 1, color: "=Damage_Areas!D7", transparency: "0.5" } }, path: [ [ ["A", 100, 75, 40, 0, 2 * Math.PI], ["Z"] ] ] }; sheet.shapes.add('carFront', carFrontModel); sheet.shapes.add('carFrontDoor', carFrontDoorModel); sheet.shapes.add('carBackDoor', carBackDoorModel); sheet.shapes.add('carBack', carBackModel); sheet.shapes.add('carFrontWheel', carFrontWheelModel); sheet.shapes.add('carBackWheel', carBackWheelModel); } export function AppFunc() { const initSpread = (spread) => { initDamageAreaShapes(spread); let activeSheet = spread.getActiveSheet(); let workbookShapes = activeSheet.shapes.all(); for (let s = 0; s < workbookShapes.length; s++) { workbookShapes[s].allowMove(false); workbookShapes[s].allowResize(false); } } return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => initSpread(spread)}> <Worksheet name={"Car Insurance Claim"}></Worksheet > <Worksheet name={"Damage_Areas"}></Worksheet > </SpreadSheets> </div> </div>); }
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-shapes"; import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react'; const Component = React.Component; function initDamageAreaShapes(spread) { let sheet = spread.getSheet(0); let damageAreaSheet = spread.getSheet(1); damageAreaSheet.setColumnWidth(0,120); var table = damageAreaSheet.tables.add("table1", 0, 0, 7, 4, GC.Spread.Sheets.Tables.TableThemes.medium4); table.filterButtonVisible(false); var damageAreas = ["Area", "Left", "Top", "Color"], carFront = ["carFront", 50, 50, "orange"], carFrontDoor = ["carFrontDoor", 197, 23, "orange"], carBackDoor = ["carBackDoor", 332, 23, "orange"], carBack = ["carBack", 405, 24, "orange"], carFrontWheel = ["carFrontWheel", 45, 64, "orange"], carBackWheel = ["carBackWheel", 361, 64, "orange"]; damageAreaSheet.setArray(0, 0, [ damageAreas, carFront, carFrontDoor, carBackDoor, carBack, carFrontWheel, carBackWheel ]); let carFrontModel = { left: "=Damage_Areas!B2", top: "=Damage_Areas!C2", width: 157, height: 85, options: { fill: { type: 1, color: "=Damage_Areas!D2", transparency: "0.5" } }, path: [ [ ["M", 6, 48], ["L", 21, 29], ["L", 59, 20], ["L", 136, 14], ["L", 157, 8], ["L", 150, 24], ["L", 148, 47], ["L", 150, 69], ["L", 157, 85], ["L", 140, 85], //Wheel well ["L", 136, 71], ["L", 128, 58], ["L", 119, 52], ["L", 107, 47], ["L", 94, 46], ["L", 83, 47], ["L", 68, 52], ["L", 60, 61], ["L", 54, 70], ["L", 50, 85], ["L", 21, 85], ["L", 13, 71], ["L", 2, 67], ["Z"] ] ] }; let carFrontDoorModel = { left: "=Damage_Areas!B3", top: "=Damage_Areas!C3", width: 140, height: 112, options: { fill: { type: 1, color: "=Damage_Areas!D3", transparency: "0.5" } }, path: [ [ ["M", 9, 36], ["L", 84, 2], ["L", 116, 0], ["L", 140, 0], ["L", 134, 74], ["L", 140, 112], ["L", 12, 112], ["L", 7, 105], ["L", 4, 96], ["L", 1, 81], ["L", 2, 64], ["L", 5, 49], ["Z"] ] ] }; let carBackDoorModel = { left: "=Damage_Areas!B4", top: "=Damage_Areas!C4", width: 121, height: 111, options: { fill: { type: 1, color: "=Damage_Areas!D4", transparency: "0.5" } }, path: [ [ ["M", 6, 0], ["L", 71, 1], ["L", 95, 20], ["L", 115, 44], ["L", 121, 53], ["L", 117, 62], ["L", 105, 68], ["L", 87, 85], ["L", 78, 100], ["L", 75, 111], ["L", 5, 111], ["L", 2, 97], ["L", 0, 79], ["L", 1, 61], ["L", 3, 38], ["Z"] ] ] }; let carBackModel = { left: "=Damage_Areas!B5", top: "=Damage_Areas!C5", width: 168, height: 110, options: { fill: { type: 1, color: "=Damage_Areas!D5", transparency: "0.5" } }, path: [ [ ["M", 0, 0], ["L", 51, 9], ["L", 110, 34], ["L", 154, 43], ["L", 163, 49], ["L", 166, 55], ["L", 152, 55], ["L", 149, 73], ["L", 168, 80], ["L", 168, 91], ["L", 164, 97], ["L", 159, 97], ["L", 153, 110], ["L", 100, 110], //Wheel well ["L", 96, 97], ["L", 86, 84], ["L", 69, 74], ["L", 60, 72], ["L", 50, 73], ["L", 40, 73], ["L", 32, 78], ["L", 24, 85], ["L", 19, 92], ["L", 14, 101], ["L", 13, 110], ["L", 3, 110], ["L", 9, 96], ["L", 17, 81], ["L", 31, 69], ["L", 44, 61], ["L", 49, 55], ["L", 44, 44], ["L", 29, 27], ["L", 14, 12], ["Z"] ] ] }; let carFrontWheelModel = { left: "=Damage_Areas!B6", top: "=Damage_Areas!C6", width: 168, height: 168, options: { fill: { type: 1, color: "=Damage_Areas!D6", transparency: "0.5" } }, path: [ [ ["A", 100, 75, 40, 0, 2 * Math.PI], ["Z"] ] ] }; let carBackWheelModel = { left: "=Damage_Areas!B7", top: "=Damage_Areas!C7", width: 168, height: 168, options: { fill: { type: 1, color: "=Damage_Areas!D7", transparency: "0.5" } }, path: [ [ ["A", 100, 75, 40, 0, 2 * Math.PI], ["Z"] ] ] }; sheet.shapes.add('carFront', carFrontModel); sheet.shapes.add('carFrontDoor', carFrontDoorModel); sheet.shapes.add('carBackDoor', carBackDoorModel); sheet.shapes.add('carBack', carBackModel); sheet.shapes.add('carFrontWheel', carFrontWheelModel); sheet.shapes.add('carBackWheel', carBackWheelModel); } export class App extends Component { constructor(props) { super(props); this.spread = null; } render() { return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => this.initSpread(spread)}> <Worksheet name={"Car Insurance Claim"}></Worksheet > <Worksheet name={"Damage_Areas"}></Worksheet > </SpreadSheets> </div> </div>); } initSpread(spread) { initDamageAreaShapes(spread); let activeSheet = spread.getActiveSheet(); let workbookShapes = activeSheet.shapes.all(); for (let s = 0; s < workbookShapes.length; s++) { workbookShapes[s].allowMove(false); workbookShapes[s].allowResize(false); } } }
<!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: 100%; height: 100%; overflow: hidden; float: left; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } #app { 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-shapes': 'npm:@mescius/spread-sheets-shapes/index.js', '@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/index.js', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js', 'react': 'npm:react/umd/react.production.min.js', 'react-dom': 'npm:react-dom/umd/react-dom.production.min.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);