Shape Text

SpreadJS allows you to add text in shapes. By using textEffect, and textFrame you will have the possibility to format the added text to your liking.

You can customize the properties of autoShapes using the Shape API: text: Gets or sets the text of the shape. You can use the textDirection property of the textFrame in the Style: this property is used to get or set the text direction of the shape. The TextDirection enum defines the direction of the text in the 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.jsx'; let spread = null; export function AppFunc() { const [showShapeTxtPanel, setShowShapeTxtPanel] = React.useState(false); const updateShapeStyle = (action, value) => { let sheet = 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) { _setShapeStyle(shape, action, value); } }); sheet.repaint(); } } const _setShapeStyle = (shape, action, value) => { if (action === 'text') { shape.text(value); } else { var shapeStyle = shape.style(); 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 === 'textDirection') { const textDirection = parseInt(value); shapeStyle.textFrame.textDirection = textDirection; switch (textDirection) { case GC.Spread.Sheets.Shapes.TextDirection.vert: case GC.Spread.Sheets.Shapes.TextDirection.wordArtVertRtl: case GC.Spread.Sheets.Shapes.TextDirection.eaVert: shapeStyle.textFrame.hAlign = GC.Spread.Sheets.HorizontalAlign.right; break; case GC.Spread.Sheets.Shapes.TextDirection.vert270: case GC.Spread.Sheets.Shapes.TextDirection.wordArtVert: case GC.Spread.Sheets.Shapes.TextDirection.eaVertLtr: shapeStyle.textFrame.hAlign = GC.Spread.Sheets.HorizontalAlign.left; break; } } else if (action === 'hAlign') { shapeStyle.textFrame.hAlign = parseInt(value); } else if (action === 'vAlign') { shapeStyle.textFrame.vAlign = parseInt(value); } else if (action === 'resizeToFitText') { shapeStyle.textFrame.resizeToFitText = value === '1'; } shape.style(shapeStyle); } } const setShapePropVisibility = (isShow) => { setShowShapeTxtPanel(isShow) } const initSpread = (currSpread) => { spread = currSpread; spread.getActiveSheet().shapes.add("rectangle", GC.Spread.Sheets.Shapes.AutoShapeType.rectangle, 40, 20, 150, 150); spread.getActiveSheet().shapes.add("rightTriangle", GC.Spread.Sheets.Shapes.AutoShapeType.rightTriangle, 40, 230, 150, 150); spread.getActiveSheet().shapes.add("hexagon", GC.Spread.Sheets.Shapes.AutoShapeType.hexagon, 250, 20, 150, 150); spread.getActiveSheet().shapes.add("oval", GC.Spread.Sheets.Shapes.AutoShapeType.oval, 250, 230, 150, 150); 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; } }); setShapePropVisibility(isShapeSelected); } else { setShapePropVisibility(false); } }) } return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => initSpread(spread)}> </SpreadSheets> </div> <Panel showShapeTxtPanel={showShapeTxtPanel} updateShapeStyle={(action, value) => { updateShapeStyle(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.jsx'; const Component = React.Component; export class App extends Component { constructor(props) { super(props); this.spread = null; this.autoGenerateColumns = false; this.state = { showShapeTxtPanel: false } } render() { const { showShapeTxtPanel } = this.state; return (<div class="sample-tutorial"> <div class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => this.initSpread(spread)}> </SpreadSheets> </div> <Panel showShapeTxtPanel={showShapeTxtPanel} updateShapeStyle={(action, value) => { this.updateShapeStyle(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(); } } _setShapeStyle(shape, action, value) { if (action === 'text') { shape.text(value); } else { var shapeStyle = shape.style(); 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 === 'textDirection') { const textDirection = parseInt(value); shapeStyle.textFrame.textDirection = textDirection; switch (textDirection) { case GC.Spread.Sheets.Shapes.TextDirection.vert: case GC.Spread.Sheets.Shapes.TextDirection.wordArtVertRtl: case GC.Spread.Sheets.Shapes.TextDirection.eaVert: shapeStyle.textFrame.hAlign = GC.Spread.Sheets.HorizontalAlign.right; break; case GC.Spread.Sheets.Shapes.TextDirection.vert270: case GC.Spread.Sheets.Shapes.TextDirection.wordArtVert: case GC.Spread.Sheets.Shapes.TextDirection.eaVertLtr: shapeStyle.textFrame.hAlign = GC.Spread.Sheets.HorizontalAlign.left; break; } } else if (action === 'hAlign') { shapeStyle.textFrame.hAlign = parseInt(value); } else if (action === 'vAlign') { shapeStyle.textFrame.vAlign = parseInt(value); } else if (action === 'resizeToFitText') { shapeStyle.textFrame.resizeToFitText = value === '1'; } shape.style(shapeStyle); } } setShapePropVisibility(isShow) { this.setState({ showShapeTxtPanel: isShow}) } initSpread(spread) { this.spread = spread; spread.getActiveSheet().shapes.add("rectangle", GC.Spread.Sheets.Shapes.AutoShapeType.rectangle, 40, 20, 150, 150); spread.getActiveSheet().shapes.add("rightTriangle", GC.Spread.Sheets.Shapes.AutoShapeType.rightTriangle, 40, 230, 150, 150); spread.getActiveSheet().shapes.add("hexagon", GC.Spread.Sheets.Shapes.AutoShapeType.hexagon, 250, 20, 150, 150); spread.getActiveSheet().shapes.add("oval", GC.Spread.Sheets.Shapes.AutoShapeType.oval, 250, 230, 150, 150); 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); } else { self.setShapePropVisibility(false); } }) } }
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-shapes" export default function Panel(props) { const horizontalAlign = { left: 0, center: 1, right: 2 }; const verticalAlign = { top: 0, center: 1, bottom: 2 }; const resizeToFitTextOptions = { false: 0, true: 1 } const [panelInfo, setPanelInfo] = React.useState({ text: `abcdefgHIJKLMN 12356789 SpreadJSSpreadJS`, textColor: "#FFFF00", transparencyTxt: 0.5, font: "bold 15px Georgia", hAlign: 1, vAlign: 1, resizeToFitText: 0, textDirection: 0, horizontalAlignList: getEnumList(horizontalAlign), verticalAlignList: getEnumList(verticalAlign), resizeToFitTextList: getEnumList(resizeToFitTextOptions), }); const textDirectionList = [ {text: 'Horizontal', value: GC.Spread.Sheets.Shapes.TextDirection.horz }, {text: 'Vertical(Ltr)', value: GC.Spread.Sheets.Shapes.TextDirection.eaVertLtr}, {text: 'Vertical', value: GC.Spread.Sheets.Shapes.TextDirection.eaVert}, {text: 'Rotate all text 90°', value: GC.Spread.Sheets.Shapes.TextDirection.vert}, {text: 'Rotate all text 270°', value: GC.Spread.Sheets.Shapes.TextDirection.vert270}, {text: 'Stacked', value: GC.Spread.Sheets.Shapes.TextDirection.wordArtVert}, {text: 'Stacked(Rtl)', value: GC.Spread.Sheets.Shapes.TextDirection.wordArtVertRtl} ]; const { updateShapeStyle, showShapeTxtPanel } = props; return (<div class="options-container"> <div class="option-row"> Try selecting a shape and change the text properties to see the outcome: </div> <div id="divideLine" class="divide-line"></div> { showShapeTxtPanel && <div class="option-row"> <label>Text: </label> <textarea value={panelInfo.text} type="text" placeholder="Shape text" rows="3" onChange={(event) => { setPanelInfo({ ...panelInfo, text: event.target.value }); }}></textarea> <input type="button" onClick={() => { updateShapeStyle('text', panelInfo.text) }} value="Set" /> <label>Text Direction:</label> <select value={panelInfo.textDirection} onChange={(event) => { setPanelInfo({ ...panelInfo, textDirection: parseInt(event.target.value, 10) }); }}> {textDirectionList.map(({ text, value }) => { return <option value={value} key={value}>{text}</option> })} </select > <input type="button" onClick={() => { updateShapeStyle('textDirection', panelInfo.textDirection) }} value="Set" /> <label>Text Color: </label> <input value={panelInfo.textColor} type="color" onChange={(event) => { setPanelInfo({ ...panelInfo, textColor: event.target.value }); }} /> <input type="button" onClick={() => { updateShapeStyle('color', panelInfo.textColor) }} value="Set" /> <label>Text Transparency: </label> <input value={panelInfo.transparencyTxt} type="text" onChange={(event) => { setPanelInfo({ ...panelInfo, transparencyTxt: parseFloat(event.target.value) }); }} /> <input type="button" onClick={() => { updateShapeStyle('transparencyTxt', panelInfo.transparencyTxt) }} value="Set" /> <label>Text Font: </label> <input value={panelInfo.font} type="text" onChange={(event) => { setPanelInfo({ ...panelInfo, font: event.target.value }); }} /> <input type="button" onClick={() => { updateShapeStyle('font', panelInfo.font) }} value="Set" /> <label> Horizontal Align:</label> <select value={panelInfo.hAlign} onChange={(event) => { setPanelInfo({ ...panelInfo, hAlign: parseInt(event.target.value, 10) }); }}> {panelInfo.horizontalAlignList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select> <input type="button" onClick={() => { updateShapeStyle('hAlign', panelInfo.hAlign) }} value="Set" /> <label>Vertical Align:</label> <select value={panelInfo.vAlign} onChange={(event) => { setPanelInfo({ ...panelInfo, vAlign: parseInt(event.target.value, 10) }); }}> {panelInfo.verticalAlignList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { updateShapeStyle('vAlign', panelInfo.vAlign) }} value="Set" /> <label>Resize To Fit Text:</label> <select value={panelInfo.resizeToFitText} onChange={(event) => { setPanelInfo({ ...panelInfo, resizeToFitText: event.target.value }); }}> {panelInfo.resizeToFitTextList.map(({ name, value }) => { return <option value={value} key={name}>{name}</option> })} </select > <input type="button" onClick={() => { updateShapeStyle('resizeToFitText', panelInfo.resizeToFitText) }} 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);