How to apply vertical text orientation to cells in React Spreadsheet?
This knowledge base article explains how to apply vertical text orientation to cells in a React Spreadsheet. This can be achieved by using the Cell Template feature of the Spreadsheet component. A sample implementation is provided below with detailed steps.
In the example, vertical orientation is applied to the cells in the first row of the sheet. A button labeled “Click to Rotate” is added at the end of the Home Tab to trigger the rotation. To rotate the text by 90 degrees, a child element containing the corresponding cell value is added to each cell in the first row using the Cell Template feature. This approach ensures that only the added child element (text) is rotated, instead of the entire cell element.
After selecting any cell and clicking the “Click to Rotate” button, a class e-rotate is added to the template element within each selected cell. This class includes the necessary CSS styles to rotate the text by 90 degrees. Additionally, users can select a single cell or the entire first row and click “Click to Rotate” to apply vertical text orientation.
The height of the first row is adjusted using the setRowsHeight method to ensure the rotated content is fully visible.
To preserve the rotated text orientation when exporting the spreadsheet as an Excel file, the default save action is canceled in the beforeSave event by setting args.cancel to true. The spreadsheet data is saved as a JSON object, and a server-side function saveCustom is invoked via a fetch call to handle the custom save process. This approach saves the spreadsheet data as an Excel file with rotated text, while also passing the collection of rotated cell addresses as custom parameters during the fetch call.
On the server side, the address collection of cells with rotated text is retrieved, and the rotation is applied based on the provided address collection.
[Client - Side]
import { createRoot } from 'react-dom/client';
import './index.css';
import * as React from 'react';
import { SheetsDirective, SheetDirective, ColumnsDirective, RangesDirective, RangeDirective, getCellIndexes, getRangeIndexes, getCellAddress } from '@syncfusion/ej2-react-spreadsheet';
import { ColumnDirective } from '@syncfusion/ej2-react-spreadsheet';
import { SpreadsheetComponent } from '@syncfusion/ej2-react-spreadsheet';
import { defaultData } from './data';
function Default() {
let spreadsheet;
let isCustomSave = false;
const headerRange = 'A1:F1';
const rotatedCells = [];
const boldRight = { fontWeight: 'bold', textAlign: 'right' };
const bold = { fontWeight: 'bold' };
const onCreated = () => {
spreadsheet.addToolbarItems(
'Home', [{ type: 'Separator' }, {
text: "Click to Rotate", tooltipText: 'Rotate Text',
click: () => { rotateHandler(); }
}]);
// Apply styles to the specified range in the active sheet.
spreadsheet.cellFormat({ fontWeight: 'bold', textAlign: 'center', verticalAlign: 'middle' }, 'A1:F1');
// Apply format to the specified range in the active sheet.
spreadsheet.numberFormat('$#,##0.00', 'F2:F31');
};
// Add the template to the first row range to rotate the content in each cell of the first row
const addTemplate = (args) => {
return (<span className="e-header-templat">{args.value}</span>);
}
const rotateHandler = () => {
// The index of the first (header) row
const firstRowIdx = 0;
// Get the selected cell range's indexes
const selectedRangeIndexes = getRangeIndexes(spreadsheet.getActiveSheet().selectedRange);
// Ensure the row index corresponds to the first row
for (let rowIdx = selectedRangeIndexes[0]; rowIdx <= selectedRangeIndexes[2] && rowIdx === firstRowIdx; rowIdx++) {
for (let colIdx = selectedRangeIndexes[1]; colIdx <= selectedRangeIndexes[3]; colIdx++) {
// Get the cell element
const cellEle = spreadsheet.getCell(rowIdx, colIdx);
// Added the class name to the template element within the cell to apply rotation using CSS
const templateEle = cellEle && cellEle.querySelector('span.e-header-templat');
if (templateEle) {
templateEle.parentElement.classList.add('e-rotate');
}
const cellAddress = getCellAddress(rowIdx, colIdx);
if (rotatedCells.indexOf(getCellAddress) === -1) {
rotatedCells.push(cellAddress);
}
}
}
// Set the required row height for the header row to make the rotated content fully visible
spreadsheet.setRowHeight(150, firstRowIdx, spreadsheet.activeSheetIndex);
}
const beforeSave = (args) => {
// To proceed with a custom save if any cell is rotated
if (rotatedCells.length > 0 && !isCustomSave) {
// To prevent the default save process
args.cancel = true;
// To prevent saveAsJson() from being called in a loop within the beforeSave event when invoking saveAsJson()
isCustomSave = true;
// Save the spreadsheet data as a JSON object
spreadsheet.saveAsJson().then((json) => {
const formData = new FormData();
formData.append('FileName', "Sample");
formData.append('saveType', 'Xlsx');
// Passing the JSON data to perform the save operation.
formData.append('JSONData', JSON.stringify(json.jsonObject.Workbook));
formData.append('PdfLayoutSettings', JSON.stringify({ FitSheetOnOnePage: false }));
// Passing the rotated cells' address collection as custom parameters
formData.append('customParams', rotatedCells);
// Use fetch to invoke our custom save process and save the spreadsheet data as an Excel file with rotated text
fetch('https://localhost:44354/api/spreadsheet/SaveCustom', {
method: 'POST',
body: formData
}).then((response) => {
if (response.ok) {
console.log("The spreadsheet is saved as an Excel file with rotated text in the 'Files' folder on the server");
}
});
isCustomSave = false;
});
}
}
return (<div className='control-pane'>
<div className='control-section spreadsheet-control'>
<SpreadsheetComponent openUrl='https://localhost:44354/api/spreadsheet/open' saveUrl='https://localhost:44354/api/spreadsheet/save' ref={(ssObj) => { spreadsheet = ssObj; }} created={onCreated} beforeSave={beforeSave}>
<SheetsDirective>
<SheetDirective name="Car Sales Report">
<RangesDirective>
<RangeDirective dataSource={defaultData}></RangeDirective>
<RangeDirective template={addTemplate} address={headerRange}></RangeDirective>
</RangesDirective>
<ColumnsDirective>
<ColumnDirective width={180}></ColumnDirective>
<ColumnDirective width={130}></ColumnDirective>
<ColumnDirective width={130}></ColumnDirective>
<ColumnDirective width={180}></ColumnDirective>
<ColumnDirective width={130}></ColumnDirective>
<ColumnDirective width={120}></ColumnDirective>
</ColumnsDirective>
</SheetDirective>
</SheetsDirective>
</SpreadsheetComponent>
</div>
</div>);
}
export default Default;
const root = createRoot(document.getElementById('sample'));
root.render(<Default />);
[Server - Side]
[HttpPost]
[Route("Open")]
public IActionResult Open([FromForm] IFormCollection openRequest)
{
OpenRequest open = new();
open.File = openRequest.Files[0];
var openbook = Content(Workbook.Open(open));
return openbook;
}
[HttpPost]
[Route("Save")]
public IActionResult Save([FromForm] SaveSettings saveSettings)
{
return Workbook.Save(saveSettings);
}
[HttpPost]
[Route("saveCustom")]
public string saveCustom([FromForm] SaveSettings saveSettings, [FromForm] string customParams)
{
// Get the addresses of cells with rotated text as a collection
var rotatedCells = customParams.Split(',');
ExcelEngine excelEngine = new ExcelEngine();
IApplication application = excelEngine.Excel;
// Save the workbook as Stream.
Stream fileStream = Workbook.Save<Stream>(saveSettings);
// Using XLSIO, we are opening the file stream and saving the file in the server under Files folder.
IWorkbook workbook = application.Workbooks.Open(fileStream);
IWorksheet worksheet = workbook.Worksheets[0];
for (var idx = 0; idx < rotatedCells.Length; idx++)
{
IRange range = worksheet.Range[rotatedCells[idx]];
// Specify the angle of rotation for the text in the cell
range.CellStyle.Rotation = 90;
}
using (Stream memoryStream = new MemoryStream())
{
// Saves workbook as stream
workbook.SaveAs(memoryStream);
memoryStream.Position = 0; // Reset the position to the beginning of the stream
fileStream = memoryStream;
// Get the path to save the Excel file with rotated text in the 'Files' folder
string basePath = _env.ContentRootPath + "\\Files\\" + saveSettings.FileName + ".xlsx";
var file = System.IO.File.Create(basePath);
fileStream.Seek(0, SeekOrigin.Begin)
fileStream.CopyTo(file); // to convert the stream to file options.
file.Dispose();
fileStream.Dispose();
return string.Empty;
}
}
[index.css]
.e-spreadsheet .e-cell.e-cell-template .e-rotate {
display: inline-block;
transform: rotate(-90deg);
}
Client-Side Sample: https://stackblitz.com/edit/react-gjnk4j-qkrn4c?file=index.js
Server-Side Sample (WebAPI): https://www.syncfusion.com/downloads/support/directtrac/general/ze/WebAPI_(1)_11415574122
Note: Launch the server-side WebAPI before running the client-side sample.
Output: