ファイルメニューのカスタマイズ

このデモでは、ファイル メニューにカスタム ページを追加する方法を示します。

このサンプルでは、​​以下の内容を示します。 ファイルメニューにカスタムページを追加する方法。 カスタムコンポーネントを定義し、ファイルメニューで使用する方法。
const resources = { tips: 'リボンの「ファイル」タブをクリックしてファイルメニューを表示し、「テンプレート」を選択してカスタムページをご確認ください。', loading: '読み込み中...' } window.onload = async function () { // ファイルメニューのパネルをカスタマイズして、「テンプレート」ページを追加します。 let fileMenuPanelCommand = GC.Spread.Sheets.Designer.getCommand(GC.Spread.Sheets.Designer.CommandNames.FileMenuPanel); let oldExecuteFn = fileMenuPanelCommand.execute; let oldStatusFn = fileMenuPanelCommand.getState; fileMenuPanelCommand.execute = async function (context, propertyName, newValue) { oldExecuteFn.apply(this, arguments); if (propertyName === "customTemplates") { console.log(newValue); showLoadingOverlay(); let json = await loadTemplateData(newValue); let spread = context.getWorkbook(); context.setData("FileMenu_show", false); await spread.fromJSON(json); hideLoadingOverlay(); context.getWorkbook().focus(true); } } fileMenuPanelCommand.getState = function () { let result = oldStatusFn.apply(this, arguments); result.customTemplates = custom_templates; return result; } let config = GC.Spread.Sheets.Designer.DefaultConfig; config.commandMap = { fileMenuPanel: fileMenuPanelCommand } let designer = new GC.Spread.Sheets.Designer.Designer("dss", config); let spread = designer.getWorkbook(); spread.getActiveSheet().setValue(1, 1, resources.tips); } function loadTemplateData(templateID) { let dataPath = custom_templates.find(t => t.id === templateID).dataPath; return fetch(dataPath).then(function (response) { return response.text(); }).then((str) => getDataFromJS(str)); } function showLoadingOverlay() { let overlay = document.createElement('div'); overlay.className = 'loading-overlay'; overlay.id = 'loading-overlay'; overlay.innerHTML = ` <div class="loading-spinner"></div> <div class="loading-text">${resources.loading}</div> `; document.body.appendChild(overlay); } function hideLoadingOverlay() { let overlay = document.getElementById('loading-overlay'); if (overlay) { document.body.removeChild(overlay); } }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture" content="ja-jp" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.css"> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-print/dist/gc.spread.sheets.print.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-barcode/dist/gc.spread.sheets.barcode.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-formula-panel/dist/gc.spread.sheets.formulapanel.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-io/dist/gc.spread.sheets.io.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-resources-ja/dist/gc.spread.sheets.resources.ja.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-designer-resources-ja/dist/gc.spread.sheets.designer.resource.ja.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ja/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> <link href="./styles.css" rel="stylesheet" type="text/css" /> <script src="./component.js"></script> <script src="./template.js"></script> <script src="./data/templateData.js"></script> </head> <body style="margin:0;padding:0"> <div id="dss" style="width:100vw;border:1px solid darkgray"></div> </body> </html>
function FileTemplateSelector() { GC.Spread.Sheets.Designer.AtomicComponentBase.call(this, ...arguments); this.selectedTemplate = null; }; FileTemplateSelector.prototype = new GC.Spread.Sheets.Designer.AtomicComponentBase(); FileTemplateSelector.prototype.getTemplate = function () { return ` <div class="template-selector"> <div class="templates-grid"> </div> </div>`; } FileTemplateSelector.prototype._generateTemplateItem = function (template) { let div = document.createElement("div"); div.setAttribute("key", template.id); div.className = "template-card"; div.innerHTML = ` <div class="template-preview"> <div class="template-image-placeholder"> <div class="preview-content"> <div class="simple-preview" style="background: url(${template.image}) no-repeat center center; background-size: cover;"> <div class="preview-icon"></div> </div> </div> </div> </div> <div class="template-info"> <h3 class="template-title">${template.title}</h3> </div> `; return div; } FileTemplateSelector.prototype.handleTemplateClick = function (e) { const templateCard = e.target.closest(".template-card"); if (templateCard) { this.selectedTemplate = templateCard.getAttribute("key"); this.raiseValueChanged() } } FileTemplateSelector.prototype.onMounted = function (host) { host.querySelector(".templates-grid").addEventListener("click", this.handleTemplateClick.bind(this)); } FileTemplateSelector.prototype.onRaiseValueChanged = function () { this.onRaiseValueChanged(); } FileTemplateSelector.prototype.onValueChanged = function (prevValue, nextValue, host) { let templates = nextValue; if (templates && Array.isArray(templates) && templates.length > 0) { host.querySelector(".templates-grid").innerHTML = ""; templates.forEach(template => { let templateItem = this._generateTemplateItem(template); host.querySelector(".templates-grid").appendChild(templateItem); }) } } FileTemplateSelector.prototype.updateValue = function (host) { // 選択したテンプレートを返します。 return this.selectedTemplate; } FileTemplateSelector.prototype.onDestroy = function (host) { host.querySelector(".templates-grid").removeEventListener("click", this.handleTemplateClick.bind(this)); } // カスタムコンポーネントをデザイナに登録します。 GC.Spread.Sheets.Designer.Designer.RegisterComponent("FileTemplateSelector", FileTemplateSelector);
const templateResource = { template: 'Template', templates: 'テンプレート' } var fileMenuTemplate = GC.Spread.Sheets.Designer.getTemplate( GC.Spread.Sheets.Designer.TemplateNames.FileMenuPanelTemplate ); addCustomItem(fileMenuTemplate); GC.Spread.Sheets.Designer.registerTemplate( GC.Spread.Sheets.Designer.TemplateNames.FileMenuPanelTemplate, fileMenuTemplate ); function addCustomItem(template) { // 「テンプレート」をメニューに追加します。 template.content[0].children[0].children[0].children[0].children[1].items.push({ text: "テンプレート", value: "Template" }); // テンプレートのページをコンテナに追加します。 var listDisplayContainer = template.content[0].children[0].children[1]; listDisplayContainer.children.push({ type: "Container", visibleWhen: "activeCategory_main=Template", children: [ { type: 'TextBlock', margin: '50px 0 15px 50px', style: 'font-size:36px;', text: templateResource.templates }, { type: "FileTemplateSelector", bindingPath:"customTemplates" } ], attributes:[ {key: 'data-activeCategory_main', value: templateResource.template} ] }); }
body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } #dss { height: 100%; } .template-selector { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; min-width: 0; overflow-x: hidden; } .templates-grid { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 20px; justify-content: flex-start; align-items: flex-start; width: 100%; } @media (max-width: 600px) { .templates-grid { justify-content: center; } } .template-card { border: 1px solid #d1d1d1; border-radius: 4px; background: white; cursor: pointer; transition: all 0.2s ease; overflow: hidden; height: 180px; width: 200px; flex: 0 0 auto; display: flex; flex-direction: column; } .template-card:hover { border-color: #0078d4; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transform: translateY(-2px); } .template-preview { flex: 1; background: #f8f9fa; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; } .template-image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } .preview-content { width: 90%; height: 90%; border-radius: 2px; position: relative; background: white; border: 1px solid #ddd; display: flex; align-items: center; justify-content: center; } .simple-preview { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; } .preview-icon { font-size: 32px; opacity: 0.6; } .template-info { padding: 12px; background: white; border-top: 1px solid #e0e0e0; } .template-title { font-size: 14px; font-weight: 400; margin: 0; color: #333; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } @media (max-width: 768px) { .templates-grid { gap: 12px; justify-content: center; } .template-card { width: 160px; height: 150px; } .template-title { font-size: 12px; } .template-info { padding: 8px; } } @media (max-width: 480px) { .template-selector { padding: 10px; } .templates-grid { gap: 8px; justify-content: space-around; } .template-card { width: 140px; height: 130px; } .template-title { font-size: 11px; } } @media (min-width: 1400px) { .template-card { width: 220px; height: 200px; } .preview-icon { font-size: 40px; } } .loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.3); display: flex; justify-content: center; align-items: center; z-index: 9999; } .loading-spinner { width: 50px; height: 50px; border: 4px solid #f3f3f3; border-top: 4px solid #0078d4; border-radius: 50%; animation: spin 1s linear infinite; } .loading-text { color: white; margin-left: 16px; font-size: 16px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }