SpreadJS supports rounded worksheet ranges with Range.setBorderRadius, Range.borderRadius, and Style.borderRadius. Border radius can be used to create card-like cell areas, tab headers, status steps, and other worksheet UI elements with rounded corners.
Border radius can be applied to a range in different ways:
Outer applies the radius only to the outside corners of the target range. This is useful when multiple cells should look like one rounded block.
All applies the radius to every cell in the target range. This is useful when each cell should keep its own rounded shape.
Passing undefined clears the border radius from the target range.
The borderRadius value can be a string with one to four space-separated tokens. The tokens represent the corners in this order: top-left, top-right, bottom-right, and bottom-left.
One token applies the same radius to all four corners: "14" means 14 14 14 14.
Two tokens apply the first value to top-left and bottom-right, and the second value to top-right and bottom-left: "14 8" means 14 8 14 8.
Three tokens apply the first value to top-left, the second value to top-right and bottom-left, and the third value to bottom-right: "14 8 4" means 14 8 4 8.
Four tokens set each corner separately in top-left, top-right, bottom-right, bottom-left order: "14 8 4 0" means top-left is 14, top-right is 8, bottom-right is 4, and bottom-left is 0.
You can also set border radius through a style when you want reusable cell formatting:
Border radius clips the rendered cell shape, including the cell background and diagonal borders.
var spreadNS = GC.Spread.Sheets;
var SheetArea = spreadNS.SheetArea.viewport;
var LineStyle = spreadNS.LineStyle;
var spread;
var sheet;
var showcaseSheet;
// Initializes the workbook, builds both sheets, and wires the style panel.
window.onload = function () {
spread = new spreadNS.Workbook(document.getElementById("ss"), { sheetCount: 2 });
spread.options.newTabVisible = false;
sheet = spread.getSheet(0);
showcaseSheet = spread.getSheet(1);
spread.suspendPaint();
try {
setupSheet();
buildSamples();
setupShowcaseSheet(showcaseSheet);
buildShowcase(showcaseSheet);
bindPanel();
bindSheetPanelVisibility();
sheet.setSelection(3, 2, 7, 3);
spread.setActiveSheetIndex(0);
updatePanelFromSelection();
updatePanelVisibility();
} finally {
spread.resumePaint();
}
};
// Builds the editable border-radius sample sheet.
function setupSheet() {
sheet.name("Border Radius");
sheet.defaults.rowHeight = 24;
sheet.setRowCount(28);
sheet.setColumnCount(16);
sheet.setRowHeight(1, 36);
for (var col = 1; col <= 14; col++) {
sheet.setColumnWidth(col, 48);
}
}
function buildSamples() {
addTitle();
addCardSample();
addTabsSample();
addStatusStepsSample();
}
function addTitle() {
sheet.getCell(1, 1)
.value("Select a range, then adjust its style in the panel.")
.font("13px Calibri")
.foreColor("#64748b")
.vAlign(spreadNS.VerticalAlign.center);
}
function addCardSample() {
addSectionLabel(3, "Card");
addSpanCard(3, 2);
addRangeCard(3, 6);
addBackgroundOnlyCard(3, 10);
}
function addSpanCard(row, col) {
var range = sheet.getRange(row, col, 7, 3);
range.backColor("#f1f6ea");
range.setBorder(new spreadNS.LineBorder("#6f8f45", LineStyle.medium), { outline: true });
setRangeBorderRadius(range, "14", { outer: true });
centerBlock(row, col, 7, 3, "Outer Radius");
range.foreColor("#2f3a25");
}
function addRangeCard(row, col) {
var range = sheet.getRange(row, col, 7, 3);
range.backColor("#f3eff8");
range.setBorder(new spreadNS.LineBorder("#9a86b8", LineStyle.medium), { outline: true });
setRangeBorderRadius(range, "14", { all: true });
range.hAlign(spreadNS.HorizontalAlign.center);
range.vAlign(spreadNS.VerticalAlign.center);
range.font("600 12px Calibri");
range.foreColor("#4b3f5c");
sheet.getCell(row + 3, col + 1).value("All Cell Radius");
}
function addBackgroundOnlyCard(row, col) {
var range = sheet.getRange(row, col, 7, 3);
range.backColor("#DCEFEF");
setRangeBorderRadius(range, "20", { outer: true });
centerBlock(row, col, 7, 3, "Fill Only");
range.foreColor("#46514a");
}
function addTabsSample() {
addSectionLabel(12, "Tabs");
addTab(12, 2, "Overview", false);
addTab(12, 4, "Details", false);
addTab(12, 6, "Data", true);
addTab(12, 8, "Settings", false);
addTab(12, 10, "History", false);
}
function addTab(row, col, text, active) {
var range = sheet.getRange(row, col, 1, 2);
range.backColor(active ? "#F6ECE8" : "#FCFAF9");
range.setBorder(new spreadNS.LineBorder("#E7DAD6", LineStyle.medium), { outline: true });
setRangeBorderRadius(range, "0 10 0 0", { all: true });
centerBlock(row, col, 1, 2, text);
range.foreColor(active ? "#6B4E47" : "#8A756E");
if(active){
range.borderBottom(new spreadNS.LineBorder("#B67868", LineStyle.medium));
}
}
function addStatusStepsSample() {
addSectionLabel(15, "Steps");
addStatusStep(15, 2, "1 Plan", "#EEF3F8", "#8CA3BE", "#3E5168", "14");
addStatusStep(15, 6, "2 Build", "#f8f3e8", "#b89b5e", "#5c4a22", "14");
addStatusStep(15, 10, "3 Ship", "#eef1ee", "#8a958d", "#3f4942", "14");
}
function addStatusStep(row, col, text, backColor, borderColor, foreColor, radius) {
var range = sheet.getRange(row, col, 2, 3);
range.backColor(backColor);
range.setBorder(new spreadNS.LineBorder(borderColor, LineStyle.medium), { outline: true });
setRangeBorderRadius(range, radius, { outer: true });
centerBlock(row, col, 2, 3, text);
range.foreColor(foreColor);
}
function centerBlock(row, col, rowCount, colCount, text) {
var range = sheet.getRange(row, col, rowCount, colCount);
range.hAlign(spreadNS.HorizontalAlign.center);
range.vAlign(spreadNS.VerticalAlign.center);
range.font("600 12px Calibri");
sheet.getCell(row, col).value(text);
if (rowCount > 1 || colCount > 1) {
sheet.addSpan(row, col, rowCount, colCount);
}
}
function addSectionLabel(row, text) {
sheet.getCell(row, 1)
.value(text)
.font("600 12px Calibri")
.foreColor("#64748b")
.hAlign(spreadNS.HorizontalAlign.left)
.vAlign(spreadNS.VerticalAlign.center);
}
// Builds the read-only CRM layout sheet used to showcase rounded worksheet UI.
function setupShowcaseSheet(targetSheet) {
targetSheet.name("CRM Layout");
targetSheet.options.gridline = { showVerticalGridline: false, showHorizontalGridline: false };
targetSheet.options.rowHeaderVisible = false;
targetSheet.options.colHeaderVisible = false;
targetSheet.defaults.rowHeight = 20;
targetSheet.setRowCount(34);
targetSheet.setColumnCount(39);
for (var col = 0; col < 39; col++) {
targetSheet.setColumnWidth(col, 34);
}
for (var row = 0; row < 34; row++) {
targetSheet.setRowHeight(row, 20);
}
}
function buildShowcase(targetSheet) {
targetSheet.suspendPaint();
try {
targetSheet.getRange(0, 0, 34, 39).backColor("#ffffff");
addInfoPanel(targetSheet, 1, 1, 9, 10, [
["Company no", "20"],
["Company name", "Land of Toys Inc."],
["Type", ""],
["Phone", "2125557818"],
["Fax", ""],
["Email", "kwai@landoftoysinc.com"],
["Web", "www.landoftoysinc.com"],
["Our rep", "Joe Bloggs"],
["Inactive", ""]
]);
addInfoPanel(targetSheet, 1, 12, 9, 10, [
["Address", "897 Long Airport Avenue"],
["", ""],
["", ""],
["Town / City", "NYC"],
["County / State", "NY"],
["Zip / Postcode", "10022"],
["Country", "USA"],
["Area", ""],
["Identity", "Classic Models"]
]);
addNotesPanel(targetSheet, 1, 23, 9, 15);
addTabStrip(targetSheet, 11, 1);
addEnquiriesTable(targetSheet, 13, 1, 15, 37);
targetSheet.setSelection(30, 1, 1, 1);
} finally {
targetSheet.resumePaint();
}
}
function addInfoPanel(targetSheet, row, col, rowCount, colCount, fields) {
var labelCols = Math.floor(colCount * 0.36);
var valueCols = colCount - labelCols;
var panelRange = targetSheet.getRange(row, col, rowCount, colCount);
panelRange.backColor("#eff5fb");
for (var i = 0; i < fields.length; i++) {
var currentRow = row + i;
targetSheet.getRange(currentRow, col, 1, labelCols)
.backColor("#d8e3ee")
.foreColor("#315b83")
.font("600 12px Calibri")
.hAlign(spreadNS.HorizontalAlign.right)
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5");
targetSheet.getCell(currentRow, col + labelCols - 1).value(fields[i][0]);
targetSheet.getRange(currentRow, col + labelCols, 1, valueCols)
.backColor("#ffffff")
.foreColor("#1f2937")
.font("600 12px Calibri")
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5");
targetSheet.getCell(currentRow, col + labelCols).value(fields[i][1]);
targetSheet.getCell(currentRow, col + labelCols - 1).borderRight(new spreadNS.LineBorder("#dbe4ee", LineStyle.thin));
if (i < fields.length - 1) {
setRowBottomBorder(targetSheet, currentRow, col, colCount, new spreadNS.LineBorder("#dbe4ee", LineStyle.thin));
}
}
applyRoundedOutline(panelRange, "12", "#9fbddd", targetSheet);
}
function addNotesPanel(targetSheet, row, col, rowCount, colCount) {
var panelRange = targetSheet.getRange(row, col, rowCount, colCount);
panelRange.backColor("#edf4fb");
var headerRange = targetSheet.getRange(row, col, 1, colCount);
headerRange.backColor("#84a9ca").foreColor("#ffffff").font("600 12px Calibri").cellPadding("0 5");
targetSheet.addSpan(row, col, 1, colCount);
targetSheet.getCell(row, col).value("Communications / notes");
var headers = ["Type", "Date", "From", "To", "Details"];
var widths = [2, 2, 5, 4, 2];
var offset = 0;
for (var i = 0; i < headers.length; i++) {
targetSheet.addSpan(row + 1, col + offset, 1, widths[i]);
targetSheet.getRange(row + 1, col + offset, 1, widths[i])
.backColor("#f5f7fa")
.foreColor("#4f6f91")
.font("600 12px Calibri")
.hAlign(spreadNS.HorizontalAlign.left)
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5");
targetSheet.getCell(row + 1, col + offset).value(headers[i]);
offset += widths[i];
}
setSegmentRowInteriorBorders(targetSheet, row + 1, col, widths, new spreadNS.LineBorder("#bed0e3", LineStyle.thin));
var records = [
["Call", "26/05", "Kwai Lee", "Joe Bloggs", "Quote"],
["Email", "27/05", "Joe Bloggs", "Kwai Lee", "Info"],
["Note", "28/05", "Kwai Lee", "Support", "Specs"],
["Call", "30/05", "Joe Bloggs", "Kwai Lee", "Follow"]
];
for (var dataRow = row + 2; dataRow < row + rowCount - 1; dataRow++) {
var backColor = dataRow % 2 === 0 ? "#f4f8fc" : "#ffffff";
targetSheet.getRange(dataRow, col, 1, colCount)
.backColor(backColor)
.foreColor("#24364a")
.font("600 11px Calibri")
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5")
.setBorder(new spreadNS.LineBorder("#c2d5e8", LineStyle.thin), { bottom: true });
addNotesRecord(targetSheet, dataRow, col, widths, records[dataRow - row - 2] || ["", "", "", "", ""]);
}
var footerRange = targetSheet.getRange(row + rowCount - 1, col, 1, colCount);
footerRange.backColor("#84a9ca").foreColor("#ffffff").font("600 12px Calibri");
targetSheet.addSpan(row + rowCount - 1, col, 1, colCount);
targetSheet.getCell(row + rowCount - 1, col).value("");
applyRoundedOutline(panelRange, "12", "#9fbddd", targetSheet);
}
function addNotesRecord(targetSheet, row, col, widths, values) {
var currentCol = col;
for (var i = 0; i < widths.length; i++) {
targetSheet.addSpan(row, currentCol, 1, widths[i]);
targetSheet.getCell(row, currentCol).value(values[i]);
currentCol += widths[i];
}
}
function addTabStrip(targetSheet, row, col) {
var tabs = ["Contacts", "Addresses", "Enquiries", "Jobs", "Tasks", "Products", "Quotes", "Orders", "Shipping", "Account", "Documents"];
var widths = [3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3];
var startCol = col;
var stripRange = targetSheet.getRange(row, col, 1, 37);
stripRange.backColor("#f7fbff").setBorder(new spreadNS.LineBorder("#bdd2e6", LineStyle.medium), { outline: true });
setRangeBorderRadius(stripRange, "8", { outer: true }, targetSheet);
for (var i = 0; i < tabs.length; i++) {
var active = tabs[i] === "Enquiries";
targetSheet.addSpan(row, startCol, 1, widths[i]);
var tabRange = targetSheet.getRange(row, startCol, 1, widths[i]);
tabRange.backColor(active ? "#84a9ca" : "#f7fbff")
.foreColor(active ? "#ffffff" : "#4f6f91")
.font("600 12px Calibri")
.hAlign(spreadNS.HorizontalAlign.center)
.vAlign(spreadNS.VerticalAlign.center)
.setBorder(new spreadNS.LineBorder("#bdd2e6", LineStyle.thin), { outline: true });
setRangeBorderRadius(tabRange, "4", { outer: true }, targetSheet);
targetSheet.getCell(row, startCol).value(tabs[i]);
startCol += widths[i];
}
}
function addEnquiriesTable(targetSheet, row, col, rowCount, colCount) {
var panelRange = targetSheet.getRange(row, col, rowCount, colCount);
panelRange.backColor("#f8fbfe");
var titleRange = targetSheet.getRange(row, col, 1, colCount);
titleRange.backColor("#84a9ca").foreColor("#ffffff").font("600 12px Calibri");
setRangeBorderRadius(titleRange, "10 10 0 0", { outer: true }, targetSheet);
targetSheet.addSpan(row, col, 1, colCount);
targetSheet.getCell(row, col).value("Enquiries from this company").cellPadding("0 5");
var headers = [
{ text: "", width: 1 },
{ text: "Ref no", width: 2 },
{ text: "Date", width: 2 },
{ text: "Status", width: 2 },
{ text: "Contact", width: 7 },
{ text: "Our rep", width: 6 },
{ text: "Referred by", width: 7 },
{ text: "Enquiry value", width: 4 },
{ text: "Requirements", width: 6 }
];
var currentCol = col;
for (var i = 0; i < headers.length; i++) {
targetSheet.addSpan(row + 1, currentCol, 1, headers[i].width);
targetSheet.getRange(row + 1, currentCol, 1, headers[i].width)
.backColor("#f5f7fa")
.foreColor("#4f6f91")
.font("600 12px Calibri")
.hAlign(spreadNS.HorizontalAlign.left)
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5");
targetSheet.getCell(row + 1, currentCol).value(headers[i].text);
currentCol += headers[i].width;
}
setHeaderInteriorBorders(targetSheet, row + 1, col, headers, new spreadNS.LineBorder("#bed0e3", LineStyle.thin));
var records = [
[">", "1", "26/05/16", "Hot", "Kwai Lee", "Joe Bloggs", "Google Adwords", "250.00", "1929 Model T Ford"],
[">", "3", "26/05/16", "Hot", "Kwai Lee", "Joe Bloggs", "Customer", "2,300.00", "Rolls Royce Flying Spur (1: 15 scale)"],
[">", "4", "26/05/16", "Cold", "Kwai Lee", "Joe Bloggs", "Google Adwords", "195.00", "Chevy Nova 1972 (plinth mounted)"],
[">", "2", "26/05/16", "Deal", "Kwai Lee", "Joe Bloggs", "Web site", "495.00", "1968 Camaro with softtop (rare)"]
];
for (var recordIndex = 0; recordIndex < records.length; recordIndex++) {
addTableRecord(targetSheet, row + 2 + recordIndex, col, headers, records[recordIndex]);
}
for (var dataRow = row + 2; dataRow < row + rowCount - 1; dataRow++) {
targetSheet.getRange(dataRow, col, 1, colCount)
.backColor(dataRow % 2 === 0 ? "#eef5fc" : "#ffffff")
.foreColor("#24364a")
.font("600 11px Calibri")
.vAlign(spreadNS.VerticalAlign.center)
.cellPadding("0 5")
.setBorder(new spreadNS.LineBorder("#c2d5e8", LineStyle.thin), { bottom: true });
}
var footerRange = targetSheet.getRange(row + rowCount - 1, col, 1, colCount);
footerRange.backColor("#84a9ca").foreColor("#ffffff").font("600 12px Calibri");
setRangeBorderRadius(footerRange, "0 0 10 10", { outer: true }, targetSheet);
targetSheet.addSpan(row + rowCount - 1, col, 1, colCount);
targetSheet.getCell(row + rowCount - 1, col).value("");
applyRoundedOutline(panelRange, "10", "#9fbddd", targetSheet);
}
// Shared helpers for rendering table-like rows and rounded panel outlines.
function addTableRecord(targetSheet, row, col, headers, values) {
var currentCol = col;
for (var i = 0; i < headers.length; i++) {
targetSheet.addSpan(row, currentCol, 1, headers[i].width);
targetSheet.getCell(row, currentCol).value(values[i]);
currentCol += headers[i].width;
}
}
function applyRoundedOutline(range, radius, borderColor, targetSheet) {
range.setBorder(new spreadNS.LineBorder(borderColor, LineStyle.medium), { outline: true });
setRangeBorderRadius(range, radius, { outer: true }, targetSheet);
}
function setSegmentRowInteriorBorders(targetSheet, row, col, widths, border) {
setRowBottomBorder(targetSheet, row, col, sumWidths(widths), border);
setInternalVerticalBorders(targetSheet, row, col, widths, border);
}
function setHeaderInteriorBorders(targetSheet, row, col, headers, border) {
var widths = [];
for (var i = 0; i < headers.length; i++) {
widths.push(headers[i].width);
}
setSegmentRowInteriorBorders(targetSheet, row, col, widths, border);
}
function setRowBottomBorder(targetSheet, row, col, colCount, border) {
targetSheet.getRange(row, col, 1, colCount).setBorder(border, { bottom: true });
}
function setInternalVerticalBorders(targetSheet, row, col, widths, border) {
var currentCol = col;
for (var i = 0; i < widths.length - 1; i++) {
currentCol += widths[i];
targetSheet.getCell(row, currentCol - 1).borderRight(border);
}
}
function sumWidths(widths) {
var total = 0;
for (var i = 0; i < widths.length; i++) {
total += widths[i];
}
return total;
}
// Wires the right-side style panel to the active selection.
function bindPanel() {
getElement("backgroundNoFill").addEventListener("change", function () {
updateBackColorState();
applyBackgroundColorSettings();
});
getElement("backgroundColor").addEventListener("change", function () {
getElement("backgroundNoFill").checked = false;
updateBackColorState();
applyBackgroundColorSettings();
});
getElement("borderStyle").addEventListener("change", function () {
updateBorderColorState();
applyBorderStyleSettings();
});
getElement("borderColor").addEventListener("change", applyBorderColorSettings);
getElement("applyRadius").addEventListener("click", applyRadiusSettings);
sheet.bind(spreadNS.Events.SelectionChanged, updatePanelFromSelection);
updateBackColorState();
updateBorderColorState();
}
function bindSheetPanelVisibility() {
spread.bind(spreadNS.Events.ActiveSheetChanged, updatePanelVisibility);
}
function updatePanelVisibility() {
var optionsContainer = getElement("optionsContainer");
if (!optionsContainer) {
return;
}
var isShowcaseSheet = spread.getActiveSheetIndex() === 1;
optionsContainer.style.display = isShowcaseSheet ? "none" : "block";
refreshSpreadLayout();
if (!isShowcaseSheet) {
updatePanelFromSelection();
}
}
function refreshSpreadLayout() {
spread.refresh();
setTimeout(function () {
spread.refresh();
}, 0);
}
function updateBackColorState() {
getElement("backgroundColor").disabled = getElement("backgroundNoFill").checked;
}
function updateBorderColorState() {
getElement("borderColor").disabled = getElement("borderStyle").value === "none";
}
// Reads the selected range and mirrors its style back into the panel controls.
function updatePanelFromSelection() {
var selection = getActiveSelection();
var style = getSelectionStartStyle(selection);
var border = getSelectionOutlineBorder(selection);
var backgroundColor = normalizeHexColor(style && style.backColor);
if (backgroundColor) {
getElement("backgroundNoFill").checked = false;
getElement("backgroundColor").value = backgroundColor;
} else {
getElement("backgroundNoFill").checked = true;
}
if (border) {
getElement("borderStyle").value = getLineStyleName(border.style);
if (normalizeHexColor(border.color)) {
getElement("borderColor").value = normalizeHexColor(border.color);
}
} else {
getElement("borderStyle").value = "none";
}
updateBackColorState();
updateBorderColorState();
}
function getSelectionStartStyle(selection) {
return sheet.getActualStyle(selection.row, selection.col, SheetArea)
|| sheet.getStyle(selection.row, selection.col, SheetArea);
}
function getSelectionOutlineBorder(selection) {
var lastRow = selection.row + selection.rowCount - 1;
var lastCol = selection.col + selection.colCount - 1;
var borderNames = ["borderTop", "borderRight", "borderBottom", "borderLeft"];
var samples = [
[selection.row, selection.col],
[selection.row, lastCol],
[lastRow, lastCol],
[lastRow, selection.col]
];
for (var index = 0; index < samples.length; index++) {
var style = sheet.getStyle(samples[index][0], samples[index][1], SheetArea);
if (!style) {
continue;
}
for (var borderIndex = 0; borderIndex < borderNames.length; borderIndex++) {
var border = style[borderNames[borderIndex]];
if (border) {
return border;
}
}
}
return null;
}
function getLineStyleName(styleValue) {
if (styleValue === undefined || styleValue === null || styleValue === LineStyle.none || styleValue === LineStyle.empty) {
return "none";
}
for (var name in LineStyle) {
if (LineStyle.hasOwnProperty(name) && LineStyle[name] === styleValue) {
return name;
}
}
return "none";
}
function normalizeHexColor(color) {
if (!color) {
return "";
}
var hex = String(color).toLowerCase();
if (/^#[0-9a-f]{6}$/.test(hex)) {
return hex;
}
if (/^#[0-9a-f]{3}$/.test(hex)) {
return "#" + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2) + hex.charAt(3) + hex.charAt(3);
}
return "";
}
// Applies panel changes to the current selection.
function applyBackgroundColorSettings(focusSpread) {
var selection = getActiveSelection();
var range = sheet.getRange(selection.row, selection.col, selection.rowCount, selection.colCount);
sheet.suspendPaint();
try {
if (getElement("backgroundNoFill").checked) {
clearRangeBackColor(range);
} else {
range.backColor(getElement("backgroundColor").value);
}
sheet.setSelection(selection.row, selection.col, selection.rowCount, selection.colCount);
} finally {
sheet.resumePaint();
}
if (focusSpread !== false) {
spread.focus();
}
}
function clearRangeBackColor(range) {
forEachCell(range, function (row, col) {
var style = sheet.getStyle(row, col, SheetArea) || new spreadNS.Style();
delete style.backColor;
sheet.setStyle(row, col, style, SheetArea);
});
}
function applyBorderStyleSettings() {
var selection = getActiveSelection();
var range = sheet.getRange(selection.row, selection.col, selection.rowCount, selection.colCount);
sheet.suspendPaint();
try {
applyBorderStyle(range);
sheet.setSelection(selection.row, selection.col, selection.rowCount, selection.colCount);
} finally {
sheet.resumePaint();
}
spread.focus();
}
function applyBorderColorSettings(focusSpread) {
var selection = getActiveSelection();
var range = sheet.getRange(selection.row, selection.col, selection.rowCount, selection.colCount);
sheet.suspendPaint();
try {
applyBorderColor(range);
sheet.setSelection(selection.row, selection.col, selection.rowCount, selection.colCount);
} finally {
sheet.resumePaint();
}
if (focusSpread !== false) {
spread.focus();
}
}
function applyRadiusSettings() {
var selection = getActiveSelection();
var range = sheet.getRange(selection.row, selection.col, selection.rowCount, selection.colCount);
sheet.suspendPaint();
try {
applyRadius(range);
sheet.setSelection(selection.row, selection.col, selection.rowCount, selection.colCount);
} finally {
sheet.resumePaint();
}
spread.focus();
}
function applyBorderStyle(range) {
var lineStyle = LineStyle[getElement("borderStyle").value];
updateOutlineBorder(range, function (border) {
return new spreadNS.LineBorder(getBorderColor(border), lineStyle);
});
}
function applyBorderColor(range) {
var borderColor = getElement("borderColor").value;
updateOutlineBorder(range, function (border) {
return new spreadNS.LineBorder(borderColor, getBorderStyle(border));
});
}
function updateOutlineBorder(range, updateBorder) {
var lastRow = range.row + range.rowCount - 1;
var lastCol = range.col + range.colCount - 1;
for (var col = range.col; col <= lastCol; col++) {
updateCellBorder(range.row, col, "borderTop", updateBorder);
updateCellBorder(lastRow, col, "borderBottom", updateBorder);
}
for (var row = range.row; row <= lastRow; row++) {
updateCellBorder(row, range.col, "borderLeft", updateBorder);
updateCellBorder(row, lastCol, "borderRight", updateBorder);
}
}
function updateCellBorder(row, col, borderName, updateBorder) {
var style = sheet.getStyle(row, col, SheetArea) || new spreadNS.Style();
style[borderName] = updateBorder(style[borderName]);
sheet.setStyle(row, col, style, SheetArea);
}
function getBorderColor(border) {
return border && border.color ? border.color : getElement("borderColor").value;
}
function getBorderStyle(border) {
return border && border.style !== undefined ? border.style : LineStyle[getElement("borderStyle").value];
}
// Applies radius values using the current API when available, with a style fallback for older builds.
function applyRadius(range) {
var option = getSelectedRadiusOption();
setRangeBorderRadius(range, undefined, { all: true });
if (option === "none") {
return;
}
var setting = {};
setting[option] = true;
setRangeBorderRadius(range, getRadiusValue(), setting);
}
function getActiveSelection() {
var selection = sheet.getSelections()[0];
var row = selection.row < 0 ? 0 : selection.row;
var col = selection.col < 0 ? 0 : selection.col;
var rowCount = selection.row < 0 ? sheet.getRowCount() : selection.rowCount;
var colCount = selection.col < 0 ? sheet.getColumnCount() : selection.colCount;
return {
row: row,
col: col,
rowCount: rowCount,
colCount: colCount
};
}
// Border-radius compatibility helpers.
function setRangeBorderRadius(range, value, options, targetSheet) {
targetSheet = targetSheet || sheet;
if (typeof range.setBorderRadius === "function") {
range.setBorderRadius(value, options);
return;
}
if (options && options.outer) {
setOuterBorderRadius(range, value, targetSheet);
return;
}
setAllBorderRadius(range, value, targetSheet);
}
function setAllBorderRadius(range, value, targetSheet) {
targetSheet = targetSheet || sheet;
if (typeof range.borderRadius === "function") {
range.borderRadius(value);
return;
}
forEachCell(range, function (row, col) {
setCellBorderRadius(row, col, value, targetSheet);
});
}
function setOuterBorderRadius(range, value, targetSheet) {
targetSheet = targetSheet || sheet;
setAllBorderRadius(range, undefined, targetSheet);
if (value === undefined || value === null || value === "") {
return;
}
var radii = parseRadiusValue(value);
var lastRow = range.row + range.rowCount - 1;
var lastCol = range.col + range.colCount - 1;
var cells = {};
addOuterCorner(cells, range.row, range.col, 0, radii[0]);
addOuterCorner(cells, range.row, lastCol, 1, radii[1]);
addOuterCorner(cells, lastRow, lastCol, 2, radii[2]);
addOuterCorner(cells, lastRow, range.col, 3, radii[3]);
for (var key in cells) {
if (cells.hasOwnProperty(key)) {
var parts = key.split(":");
setCellBorderRadius(parseInt(parts[0], 10), parseInt(parts[1], 10), cells[key].join(" "), targetSheet);
}
}
}
function addOuterCorner(cells, row, col, cornerIndex, radius) {
var key = row + ":" + col;
if (!cells[key]) {
cells[key] = ["0", "0", "0", "0"];
}
cells[key][cornerIndex] = String(radius);
}
function parseRadiusValue(value) {
var parts = String(value).split(/\s+/).filter(function (item) {
return item !== "";
});
if (parts.length === 0) {
parts = ["0"];
}
if (parts.length === 1) {
return [parts[0], parts[0], parts[0], parts[0]];
}
if (parts.length === 2) {
return [parts[0], parts[1], parts[0], parts[1]];
}
if (parts.length === 3) {
return [parts[0], parts[1], parts[2], parts[1]];
}
return [parts[0], parts[1], parts[2], parts[3]];
}
function forEachCell(range, callback) {
var rowEnd = range.row + range.rowCount;
var colEnd = range.col + range.colCount;
for (var row = range.row; row < rowEnd; row++) {
for (var col = range.col; col < colEnd; col++) {
callback(row, col);
}
}
}
function setCellBorderRadius(row, col, value, targetSheet) {
targetSheet = targetSheet || sheet;
var style = targetSheet.getStyle(row, col, SheetArea) || new spreadNS.Style();
style.borderRadius = value;
targetSheet.setStyle(row, col, style, SheetArea);
}
function getRadiusValue() {
return [
normalizeCornerValue("topLeft"),
normalizeCornerValue("topRight"),
normalizeCornerValue("bottomRight"),
normalizeCornerValue("bottomLeft")
].join(" ");
}
function normalizeCornerValue(id) {
var value = parseInt(getElement(id).value, 10);
if (isNaN(value) || value < 0) {
value = 0;
}
return value;
}
function getSelectedRadiusOption() {
var checked = document.querySelector("input[name='radiusOption']:checked");
return checked ? checked.value : "outer";
}
function getElement(id) {
return document.getElementById(id);
}
<!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/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div class="sample-tutorial">
<div id="ss" class="sample-spreadsheets"></div>
<div id="optionsContainer" class="options-container">
<div class="panel-section">
<div class="panel-section-title">
<span>Style</span>
</div>
<div class="panel-section-description">Synced with the selected area. Changes apply instantly.</div>
<div class="panel-card">
<div class="form-group-inline back-color-field">
<label class="form-label" for="backgroundColor">Back Color</label>
<div class="back-color-controls">
<label class="no-fill-option"><input type="checkbox" id="backgroundNoFill"> No Fill</label>
<input type="color" id="backgroundColor" value="#f1f6ea" aria-label="Back color">
</div>
</div>
<div class="form-group-inline border-field">
<label class="form-label" for="borderStyle">Border</label>
<div class="border-controls">
<select id="borderStyle" class="form-select">
<option value="none">None</option>
<option value="thin">Thin</option>
<option value="medium" selected>Medium</option>
<option value="dashed">Dashed</option>
<option value="dotted">Dotted</option>
<option value="thick">Thick</option>
<option value="double">Double</option>
<option value="hair">Hair</option>
<option value="mediumDashed">Medium Dashed</option>
<option value="dashDot">Dash Dot</option>
<option value="mediumDashDot">Medium Dash Dot</option>
<option value="dashDotDot">Dash Dot Dot</option>
<option value="mediumDashDotDot">Medium Dash Dot Dot</option>
<option value="slantedDashDot">Slanted Dash Dot</option>
</select>
<input type="color" id="borderColor" value="#6f8f45" aria-label="Border color">
</div>
</div>
<div class="field-hint">Border is applied to the selected range outline by default.</div>
</div>
</div>
<div class="panel-section">
<div class="panel-section-title">
<span>Radius</span>
</div>
<div class="panel-section-description">Set radius values, then click Apply Radius.</div>
<div class="panel-card">
<div class="form-group">
<label class="form-label">Apply To</label>
<div class="radio-group">
<label><input type="radio" name="radiusOption" value="outer" checked> Outer</label>
<label><input type="radio" name="radiusOption" value="all"> All</label>
<label><input type="radio" name="radiusOption" value="none"> None</label>
</div>
</div>
<div class="corner-grid">
<label class="form-label" for="topLeft">Top Left <input class="form-input" type="number" id="topLeft" min="0" max="40" value="14"></label>
<label class="form-label" for="topRight">Top Right <input class="form-input" type="number" id="topRight" min="0" max="40" value="14"></label>
<label class="form-label" for="bottomLeft">Bottom Left <input class="form-input" type="number" id="bottomLeft" min="0" max="40" value="14"></label>
<label class="form-label" for="bottomRight">Bottom Right <input class="form-input" type="number" id="bottomRight" min="0" max="40" value="14"></label>
</div>
<div class="panel-actions">
<button id="applyRadius" class="btn btn-primary" type="button">Apply Radius</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
html,
body {
position: absolute;
inset: 0;
margin: 0;
}
.sample-tutorial {
position: relative;
display: flex;
height: 100%;
overflow: hidden;
background: #ffffff;
}
.sample-spreadsheets {
flex: 1 1 auto;
min-width: 0;
width: auto;
height: 100%;
overflow: hidden;
}
.options-container {
flex: 0 0 400px;
width: 400px;
height: 100%;
box-sizing: border-box;
background: #ffffff;
overflow-y: auto;
overflow-x: hidden;
border-left: 1px solid #eeeeee;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 13px;
}
.panel-section {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
.panel-section:last-child {
border-bottom: none;
}
.panel-section-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
font-weight: 600;
font-size: 13px;
color: #555;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.panel-section-description {
margin-bottom: 10px;
color: #64748b;
font-size: 12px;
line-height: 1.35;
}
.panel-card {
background: #fafafa;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 14px;
}
.form-group {
margin-bottom: 12px;
}
.form-group:last-child {
margin-bottom: 0;
}
.form-group-inline {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.form-group-inline:last-child {
margin-bottom: 0;
}
.form-group-inline .form-label {
width: 135px;
min-width: 135px;
margin-bottom: 0;
padding: 0 4px;
box-sizing: border-box;
}
.form-group-inline .form-select {
flex: 1;
margin-left: 8px;
}
.form-label {
display: block;
font-size: 12px;
font-weight: 500;
color: #666;
margin-bottom: 4px;
}
.form-select,
.form-input {
width: 100%;
height: 32px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid #d0d0d0;
border-radius: 6px;
background: #fff;
color: #333;
font-size: 13px;
outline: none;
transition: border-color 0.15s, box-shadow 0.15s;
}
.form-select {
cursor: pointer;
}
.form-select:focus,
.form-input:focus {
border-color: #4a7a00;
box-shadow: 0 0 0 2px rgba(74, 122, 0, 0.15);
}
.back-color-field .form-label {
margin-bottom: 0;
}
.back-color-controls {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
flex: 1;
margin-left: 8px;
}
.no-fill-option {
display: flex;
align-items: center;
gap: 5px;
color: #333;
font-size: 13px;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
}
.back-color-controls input[type="color"] {
width: 44px;
height: 30px;
padding: 2px;
border: 1px solid #d0d0d0;
border-radius: 6px;
background: #ffffff;
cursor: pointer;
}
.back-color-controls input[type="color"]:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.border-field .form-label {
margin-bottom: 0;
}
.border-controls {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
margin-left: 8px;
}
.border-controls .form-select {
flex: 1;
margin-left: 0;
}
.border-controls input[type="color"] {
width: 44px;
height: 30px;
padding: 2px;
border: 1px solid #d0d0d0;
border-radius: 6px;
background: #ffffff;
cursor: pointer;
}
.border-controls input[type="color"]:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.field-hint {
margin: -6px 0 12px 143px;
color: #64748b;
font-size: 12px;
line-height: 1.35;
}
.radio-group {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px;
}
.radio-group label {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
min-height: 32px;
margin: 0;
border: 1px solid #d0d0d0;
border-radius: 6px;
background: #fff;
color: #333;
font-size: 13px;
font-weight: 500;
cursor: pointer;
}
.options-container input[type="radio"],
.options-container input[type="checkbox"] {
accent-color: #4a7a00;
}
.corner-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.corner-grid label {
font-size: 12px;
font-weight: 500;
}
.panel-actions {
margin-top: 14px;
display: flex;
gap: 8px;
}
.options-container .btn {
height: 34px;
padding: 0 16px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
}
.options-container .btn-primary {
background: #4a7a00;
color: #fff;
flex: 1;
}
.options-container .btn-primary:hover {
background: #3d6b00;
}
#applyRadius {
width: 100%;
}