自定义 Designer 主题

本示例演示如何自定义 Designer 的主题。

SpreadJS Designer 提供了一组令牌,允许用户自定义主题的颜色、圆角和阴影。 在自定义主题之前,请先选择一个现代主题作为基础: 浅色主题:gc.spread.sheets.designer.light.x.x.x.min.css 深色主题:gc.spread.sheets.designer.dark.x.x.x.min.css SpreadJS Designer 提供了 setTheme API,方便自定义主题,例如: SpreadJS Designer 还支持通过覆盖 CSS 变量自定义主题,例如: 以下是当前支持的所有令牌: API 令牌 CSS 变量令牌 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: "设计器预设主题", runtimePresetThemes: "运行时预设主题", loading: "正在加载 CSS 文件...", gradientVariable: "Gradient Token", colorForeground: "前景色", colorForegroundDisabled: "禁用状态前景色", colorBackground: "背景色", colorBackgroundHover: "悬停状态背景色", colorBackgroundSelected: "选中状态背景色", colorBackgroundDisabled: "禁用状态背景色", colorBackground2: "页面背景色", colorBackground2Hover: "页面悬停状态背景色", colorBackground2Selected: "页面选中状态背景色", colorBrandBackground: "品牌背景色", colorBrandForeground: "品牌前景色", colorBrandBackgroundHover: "品牌悬停状态背景色", colorBrandBackgroundSelected: "品牌选中状态背景色", colorStroke: "描边色", colorStrokeHover: "悬停状态描边色", colorStrokeSelected: "选中状态描边色", } 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" content="zh-cn" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.css"> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-print/dist/gc.spread.sheets.print.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-barcode/dist/gc.spread.sheets.barcode.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-reportsheet-addon/dist/gc.spread.report.reportsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-datacharts-addon/dist/gc.spread.sheets.datacharts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-formula-panel/dist/gc.spread.sheets.formulapanel.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-io/dist/gc.spread.sheets.io.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-designer-resources-cn/dist/gc.spread.sheets.designer.resource.cn.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/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; }