Custom Designer Theme

This demo shows how to customize the theme of the Designer.

The SpreadJS Designer provides a set of tokens that allow users to customize the theme’s colors, border radius, and shadow. Before customizing the Theme, please select a modern theme as the base: Light: gc.spread.sheets.designer.light.x.x.x.min.css Dark: gc.spread.sheets.designer.dark.x.x.x.min.css SpreadJS Designer offers a setTheme API, making it easy to customize the theme, for example: SpreadJS Designer also support customizing the theme by overriding CSS variables, for example: Here are all the tokens currently supported: API Tokens CSS variables Tokens colorForeground--sjs-color-foreground colorForegroundDisabled--sjs-color-foreground-disabled colorBackground--sjs-color-background colorBackgroundHover--sjs-color-background-hover colorBackgroundSelected--sjs-color-background-selected colorBackgroundDisabled--sjs-color-background-disabled colorBackground2--sjs-color-background-2 colorBackground2Hover--sjs-color-background-2-hover colorBackground2Selected--sjs-color-background-2-selected colorBrandForeground--sjs-color-brand-foreground colorBrandBackground--sjs-color-brand-background colorBrandBackgroundHover--sjs-color-brand-background-hover colorBrandBackgroundSelected--sjs-color-brand-background-selected colorStroke--sjs-color-stroke colorStrokeHover--sjs-color-stroke-hover colorStrokeSelected--sjs-color-stroke-selected colorStrokeDisabled--sjs-color-stroke-disabled borderRadiusM--sjs-border-radius-m borderRadiusL--sjs-border-radius-l borderRadiusXL--sjs-border-radius-xl shadow4--sjs-shadow-4 shadow8--sjs-shadow-8
let spreadNS = GC.Spread.Sheets; let config = GC.Spread.Sheets.Designer.DefaultConfig; let designer, spread; window.onload = function () { initDesignerConfig(); }; const THEME_SETTING_PANEL_CMD = 'theme-setting-panel-command'; const THEME_SETTING_PANEL_TEMPLATE = 'theme-setting-panel-template'; const i18n = { designerPresetThemes: "Designer Preset Themes", runtimePresetThemes: "Runtime Preset Themes", loading: "Loading CSS File...", gradientVariable: "Gradient Token", colorForeground: "Foreground Color", colorForegroundDisabled: "Foreground Disabled Color", colorBackground: "Background Color", colorBackgroundHover: "Background Hover Color", colorBackgroundSelected: "Background Selected Color", colorBackgroundDisabled: "Background Disabled Color", colorBackground2: "Page Background Color", colorBackground2Hover: "Page Background Hover Color", colorBackground2Selected: "Page Background Selected Color", colorBrandBackground: "Brand Background Color", colorBrandForeground: "Brand Foreground Color", colorBrandBackgroundHover: "Brand Background Hover Color", colorBrandBackgroundSelected: "Brand Background Selected Color", colorStroke: "Stroke Color", colorStrokeHover: "Stroke Hover Color", colorStrokeSelected: "Stroke Selected Color", } function initDesignerConfig() { const colorSettings = [{ name: "colorForeground", gradient: [{ name: "colorForegroundDisabled", offset: 2 }] }, { name: "colorBackground", gradient: [{ name: "colorBackgroundHover", offset: 1 }, { name: "colorBackgroundSelected", offset: 2 }, { name: "colorBackgroundDisabled", offset: 3 }] }, { name: "colorBackground2", gradient: [{ name: "colorBackground2Hover", offset: 1 }, { name: "colorBackground2Selected", offset: 2 }] }, { name: "colorBrandBackground", gradient: [{ name: "colorBrandForeground" }, { name: "colorBrandBackgroundHover", offset: 1 }, { name: "colorBrandBackgroundSelected", offset: 2 }] }, { name: "colorStroke", gradient: [{ name: "colorStrokeHover", offset: 1 }, { name: "colorStrokeSelected", offset: 2 }] }] let designerTheme = 'light'; let runtimeTheme = 'excel2013white'; const themeSettingCMD = { commandName: THEME_SETTING_PANEL_CMD, execute: async (designer, propertyName, value) => { if (propertyName === 'designerTheme') { GC.Spread.Sheets.Designer.setTheme(null); await changeCSSScript(getDesignerCSSHref(designerTheme), getDesignerCSSHref(value)); designerTheme = value; if (value === 'dark') { await changeCSSScript(getRuntimeCSSHref(runtimeTheme), getRuntimeCSSHref('excel2016black')); designer.getWorkbook().refresh(); runtimeTheme = 'excel2016black'; } } else if (propertyName === 'runtimeTheme') { await changeCSSScript(getRuntimeCSSHref(runtimeTheme), getRuntimeCSSHref(value)); designer.getWorkbook().refresh(); runtimeTheme = value; } else { let customTheme = { [propertyName]: value }; // Automatically generate gradient colors if (colorSettings.find(item => item.name === propertyName)) { colorSettings.find(item => item.name === propertyName).gradient.forEach(item => { if (item.offset !== undefined) { customTheme[item.name] = ColorTool.generate(value, {theme: value === '#000000' ? 'dark' : 'default'})[5 + item.offset]; } }); } // Auto foreground color if (propertyName === 'colorBackground') { customTheme["colorForeground"] = calcForegroundColor(value); } GC.Spread.Sheets.Designer.setTheme(customTheme); } }, getState: (designer) => { return { designerTheme, runtimeTheme, ...GC.Spread.Sheets.Designer.getTheme() }; } } const generateColorEditor = (colorSetting) => { return { type: "LabelContainer", text: i18n[colorSetting.name], margin: "5px 0 0 0", padding: "10px 10px", children: [{ type: "FlexContainer", direction: "horizontal", className: "custom-theme-demo", children: [{ type: "TextBlock", text: i18n[colorSetting.name], margin: "0 0 0 10px" },{ type: "ColorComboEditor", margin: "0 0 0 30px", bindingPath: colorSetting.name }] }, { type: "CollapsePanel", text: i18n.gradientVariable, active: false, children: colorSetting.gradient.map(item => ({ type: "FlexContainer", direction: "horizontal", className: "custom-theme-demo", children: [{ type: "TextBlock", margin: "0 0 0 5px", text: i18n[item.name], },{ type: "ColorComboEditor", margin: "0 0 0 10px", bindingPath: item.name }] })) }] } } const themeSettingTemplate = { templateName: THEME_SETTING_PANEL_TEMPLATE, content: [{ type: "Column", margin: "10px", children: [{ type: "ColumnSet", children: [{ type: "Column", width: "stretch", children: [{ type: "LabelContainer", text: i18n.designerPresetThemes, padding: "5px 5px", children: [{ type: "List", bindingPath: "designerTheme", items: [{ text: "Light", value: "light" },{ text: "Dark", value: "dark" },] },] }] },{ type: "Column", width: "stretch", margin: "0 0 0 5px", children: [{ type: "LabelContainer", text: i18n.runtimePresetThemes, padding: "5px 5px", children: [{ type: "List", bindingPath: "runtimeTheme", items: [{ text: "Excel2013 White", value: "excel2013white" },{ text: "Excel2016 Black", value: "excel2016black" },] },] }] },] }, ...colorSettings.map(generateColorEditor)] }] } GC.Spread.Sheets.Designer.registerTemplate(THEME_SETTING_PANEL_TEMPLATE, themeSettingTemplate); const themeSettingPanel = { command: THEME_SETTING_PANEL_CMD, uiTemplate: THEME_SETTING_PANEL_TEMPLATE, position: "right", width: "400px", classList: ['theme-setting-demo'] } config.sidePanels.push(themeSettingPanel); config.commandMap = { [THEME_SETTING_PANEL_CMD]: themeSettingCMD } new spreadNS.Designer.Designer(document.getElementById('ribbonHost'), config); } function getDesignerCSSHref(themeName) { return 'gc.spread.sheets.designer' + (themeName === 'classic' ? '' : ('.' + themeName)); } function getRuntimeCSSHref(themeName) { return 'gc.spread.sheets.' + themeName; } function showLoadingMask() { let mask = document.createElement('div'); mask.id = 'loading-mask'; mask.innerText = i18n.loading; let target = document.querySelector('.theme-setting-demo'); if (getComputedStyle(target).position === "static") { target.style.position = "relative"; } mask.setAttribute('data-mask', '1'); target.appendChild(mask); } function hideLoadingMask() { let mask = document.getElementById('loading-mask'); if (mask) mask.remove(); } function withLoadingMask(promise) { showLoadingMask(); return promise.finally(hideLoadingMask); } function changeCSSScript (current, target) { let currentLink = document.querySelector(`link[href*="${current}"]`); let href = currentLink.href; return withLoadingMask(addLink(href.replace(current, target)).then(() => { currentLink.remove(); })); } function calcForegroundColor (backgroundColor) { const [r, g, b] = (s => s.startsWith('#') ? s.slice(1).match(/\w\w/g).map(x=>parseInt(x,16)) : s.match(/\d+/g).map(Number))(backgroundColor); const grayLevel = (0.299 * r + 0.587 * g + 0.114 * b) / 255; return grayLevel > 0.5 ? "#000000" : "#ffffff"; } function addLink (href) { return new Promise((resolve, reject) => { const link = document.createElement('link'); link.type = "text/css"; link.rel = "stylesheet"; link.href = href; link.onload = function () { resolve(); }; const header = document.head; header.insertBefore(link, header.firstElementChild); }) }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture"/> <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"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.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$/en/purejs/node_modules/@mescius/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-print/dist/gc.spread.sheets.print.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-barcode/dist/gc.spread.sheets.barcode.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-reportsheet-addon/dist/gc.spread.report.reportsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-datacharts-addon/dist/gc.spread.sheets.datacharts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-formula-panel/dist/gc.spread.sheets.formulapanel.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-io/dist/gc.spread.sheets.io.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer-resources-en/dist/gc.spread.sheets.designer.resource.en.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer/dist/gc.spread.sheets.designer.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/designer/license.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <script src="color-tool.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="container"> <div class="spreadSheet"> <div id="ribbonHost"></div> <div id="ss"></div> </div> </div> </body> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .container { height: 100%; } .spreadSheet { height: 100%; } #ribbonHost { height: 100%; } .description { margin: 10px; width: 40%; } .gc-designer-label-container { background-color: var(--sjs-color-background); } .custom-theme-demo .gc-combo-editor-container { width: 120px; margin-right: 10px; } .custom-theme-demo { justify-content: space-between } #loading-mask { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--sjs-color-background, #fff); color: var(--sjs,color-foreground, #000); display: flex; align-items: center; justify-content: center; font-size: 22px; z-index: 99; opacity: 0.8; }