Navigation

SpreadJS supports navigation actions defined in GC.Spread.Sheets.Commands. These actions allow you to navigate the Spread instance in script code without needing to have the user do it.

This can be useful when you have business logic that may depend on navigating in the Spread instance. moveToNextCell: Moves to next cell (default action of Tab). moveToPreviousCell: Moves to previous cell (default action of Shift + Tab). selectNextControl: Selects spread.nextControl specified element or auto detected one if not set. selectPreviousControl: Selects spread.previousControl specified element or auto detected one if not set. moveToNextCellThenControl: Moves to next cell if the active cell is the last visible cell, then selects next control. moveToPreviousCellThenControl: Moves to previous cell if the active cell is the first visible cell, then selects previous control. Set the selected control using the nextControl and previousControl methods. For example: Spread allows creating custom commands. For example: Use the register method to set navigation keys and the corresponding actions. For example: The arguments of register in order are: name: The name of the command. execute: The execution function of the command. key: The unicode for the key. ctrl: True if the action uses the Ctrl key; otherwise, false. shift: True if the action uses the Shift key; otherwise, false. alt: True if the action uses the Alt key; otherwise, false. meta: True if the action uses the Command key on the Macintosh or the Windows key on Microsoft Windows; otherwise, false. The commitInput command can be used to commit an editing value without moving the active cell. The default Enter behavior uses commitInputNavigationDown, which commits the editing value and moves the selection down. To keep Enter navigation when no cell is being edited, bind navigationDownForEnter(reuse the navigationDown by register with a new name binding the Enter key) before commitInput: Use the hideSelection option to hide the selection when Spread loses the focus. For example:
window.onload = function () { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 2 }); initSpread(spread); }; function initSpread(spread) { var commitInputSheetName = "Commit Input"; var sheet = spread.getSheet(0); var commitInputSheet = spread.getSheet(1); commitInputSheet.name(commitInputSheetName); spread.suspendPaint(); initNavigationSheet(sheet); initCommitInputSheet(commitInputSheet); spread.resumePaint(); var commandManager = spread.commandManager(); _getElementById("preButton").addEventListener('click', _alertHandle); _getElementById("nextButton").addEventListener('click', _alertHandle); function _alertHandle() { alert(this.value); } _getElementById("tabAction").addEventListener('change', _moveHandle); _getElementById("shiftTabAction").addEventListener('change', _moveHandle); function _moveHandle() { var id = this.id, isShift = id.indexOf("shift") === 0, value = parseInt(this.value, 10), actions = GC.Spread.Sheets.Commands, action; switch (value) { case 0: action = isShift ? actions.moveToPreviousCell : actions.moveToNextCell; break; case 1: action = isShift ? actions.selectPreviousControl : actions.selectNextControl; break; case 2: action = isShift ? actions.moveToPreviousCellThenControl : actions.moveToNextCellThenControl; break; } if (action) { commandManager.register("customCommand" + new Date().valueOf(), action, GC.Spread.Commands.Key.tab, false, isShift, false, false); } } _getElementById("nextControl").addEventListener('change', _handle); _getElementById("preControl").addEventListener('change', _handle); function _handle() { var id = this.id, isPre = id.indexOf("pre") === 0, value = this.value, control = value ? document.getElementById(value) : undefined; if (isPre) { spread.previousControl(control); } else { spread.nextControl(control); } } _getElementById("hideSelection").addEventListener('click', function () { spread.options.hideSelection = this.checked; }); _getElementById("commitInputOnly").addEventListener('change', function () { spread.setActiveSheet(commitInputSheetName); setEnterShortcut(this.checked); updateCommitInputDescription(this.checked); activateCommitInputEditor(); }); spread.bind(GC.Spread.Sheets.Events.ActiveSheetChanged, updateOptionsPanel); function initNavigationSheet(sheet) { sheet.setRowCount(20); sheet.setColumnCount(10); sheet.addSpan(0, 0, 1, 7); sheet.setValue(0, 0, "press the \'Tab\' or \'Shift + Tab\' key, the selction will move in accordance."); for (var i = 1; i < 20; i += 3) { for (var j = 1; j < 20; j += 3) { sheet.setValue(i, j, "Test"); } } } function initCommitInputSheet(sheet) { sheet.setRowCount(25); sheet.setColumnCount(12); sheet.options.allowCellOverflow = true; sheet.setValue(0, 0, "press the 'Enter' key, observe selection moving."); loadSaleDataAnalysisTable(sheet, 1, 0, true); } function customSelectLeft(workbook, options) { var sheet = workbook.getSheetFromName(options.sheetName); var activeRowIndex = sheet.getActiveRowIndex(); var activeColIndex = sheet.getActiveColumnIndex(); var selNeedAdjust = getNeedAdjustSelection(sheet.getSelections(), activeRowIndex, activeColIndex); var findFirstNotNullColIndex = function (sheet, fixRow, offset, stop) { while (offset > stop) { if (sheet.getValue(fixRow, offset) !== null) { break; } offset--; } return offset; } var rangeChangeSmall = selNeedAdjust.col === activeColIndex && selNeedAdjust.colCount > 1 ? true : false; var stopSearchIndex = rangeChangeSmall ? activeColIndex : 0; var startSearchIndex = rangeChangeSmall ? selNeedAdjust.col + selNeedAdjust.colCount - 1 - 1 : selNeedAdjust.col - 1; var findResult = findFirstNotNullColIndex(sheet, activeRowIndex, startSearchIndex, stopSearchIndex); if (selNeedAdjust !== null && findResult >= 0) { selNeedAdjust.colCount = rangeChangeSmall ? findResult - selNeedAdjust.col + 1 : selNeedAdjust.col - findResult + selNeedAdjust.colCount; selNeedAdjust.col = rangeChangeSmall ? selNeedAdjust.col : findResult; sheet.repaint(); } } function customSelectRight(workbook, options) { var sheet = workbook.getSheetFromName(options.sheetName); var activeRowIndex = sheet.getActiveRowIndex(); var activeColIndex = sheet.getActiveColumnIndex(); var sheetColCount = sheet.getColumnCount(); var selNeedAdjust = getNeedAdjustSelection(sheet.getSelections(), activeRowIndex, activeColIndex); var findNextNotNullColIndex = function (sheet, fixRow, offset, stop) { while (offset < stop) { if (sheet.getValue(fixRow, offset) !== null) { break; } offset++; } return offset; } var rangeChangeSmall = selNeedAdjust.col + selNeedAdjust.colCount - 1 === activeColIndex && selNeedAdjust.colCount > 1 ? true : false; var stopSearchIndex = rangeChangeSmall ? activeColIndex : sheetColCount; var startSearchIndex = rangeChangeSmall ? selNeedAdjust.col + 1 : selNeedAdjust.col + selNeedAdjust.colCount; var findResult = findNextNotNullColIndex(sheet, activeRowIndex, startSearchIndex, stopSearchIndex); if (selNeedAdjust !== null && findResult <= sheetColCount) { selNeedAdjust.colCount = rangeChangeSmall ? selNeedAdjust.colCount + selNeedAdjust.col - findResult : findResult - selNeedAdjust.col + 1; selNeedAdjust.col = rangeChangeSmall ? findResult : selNeedAdjust.col; sheet.repaint(); } } function customSelectUp(workbook, options) { var sheet = workbook.getSheetFromName(options.sheetName); var activeRowIndex = sheet.getActiveRowIndex(); var activeColIndex = sheet.getActiveColumnIndex(); var selNeedAdjust = getNeedAdjustSelection(sheet.getSelections(), activeRowIndex, activeColIndex); var findFirstNotNullRowIndex = function (sheet, fixCol, offset, stop) { while (offset > stop) { if (sheet.getValue(offset, fixCol) !== null) { break; } offset--; } return offset; } var rangeChangeSmall = selNeedAdjust.row === activeRowIndex && selNeedAdjust.rowCount > 1 ? true : false; var stopSearchIndex = rangeChangeSmall ? activeRowIndex : 0; var startSearchIndex = rangeChangeSmall ? selNeedAdjust.row + selNeedAdjust.rowCount - 1 - 1 : selNeedAdjust.row - 1; var findResult = findFirstNotNullRowIndex(sheet, activeColIndex, startSearchIndex, stopSearchIndex); if (selNeedAdjust !== null && findResult >= 0) { selNeedAdjust.rowCount = rangeChangeSmall ? findResult - selNeedAdjust.row + 1 : selNeedAdjust.row - findResult + selNeedAdjust.rowCount; selNeedAdjust.row = rangeChangeSmall ? selNeedAdjust.row : findResult; sheet.repaint(); } } function customSelectDown(workbook, options) { var sheet = workbook.getSheetFromName(options.sheetName); var activeRowIndex = sheet.getActiveRowIndex(); var activeColIndex = sheet.getActiveColumnIndex(); var sheetRowCount = sheet.getRowCount(); var selNeedAdjust = getNeedAdjustSelection(sheet.getSelections(), activeRowIndex, activeColIndex); var findNextNotNullRowIndex = function (sheet, fixCol, offset, stop) { while (offset < stop) { if (sheet.getValue(offset, fixCol) !== null) { break; } offset++; } return offset; } var rangeChangeSmall = selNeedAdjust.row + selNeedAdjust.rowCount - 1 === activeRowIndex && selNeedAdjust.rowCount > 1 ? true : false; var stopSearchIndex = rangeChangeSmall ? activeRowIndex : sheetRowCount; var startSearchIndex = rangeChangeSmall ? selNeedAdjust.row + 1 : selNeedAdjust.row + selNeedAdjust.rowCount; var findResult = findNextNotNullRowIndex(sheet, activeColIndex, startSearchIndex, stopSearchIndex); if (selNeedAdjust !== null && findResult <= sheetRowCount) { selNeedAdjust.rowCount = rangeChangeSmall ? selNeedAdjust.rowCount + selNeedAdjust.row - findResult : findResult - selNeedAdjust.row + 1; selNeedAdjust.row = rangeChangeSmall ? findResult : selNeedAdjust.row; sheet.repaint(); } } function getNeedAdjustSelection(selections, rowIndex, colIndex) { var sel = null; for (var i = 0; i < selections.length; i++) { if (selections[i].contains(rowIndex, colIndex)) { sel = selections[i]; } } return sel; } commandManager.register("customSelectLeft", customSelectLeft); commandManager.register("customSelectRight", customSelectRight); commandManager.register("customSelectDown", customSelectDown); commandManager.register("customSelectUp", customSelectUp); commandManager.setShortcutKey(null, GC.Spread.Commands.Key.left, true, true, false, false); commandManager.setShortcutKey("customSelectLeft", GC.Spread.Commands.Key.left, true, true, false, false); commandManager.setShortcutKey(null, GC.Spread.Commands.Key.right, true, true, false, false); commandManager.setShortcutKey("customSelectRight", GC.Spread.Commands.Key.right, true, true, false, false); commandManager.setShortcutKey(null, GC.Spread.Commands.Key.down, true, true, false, false); commandManager.setShortcutKey("customSelectDown", GC.Spread.Commands.Key.down, true, true, false, false); commandManager.setShortcutKey(null, GC.Spread.Commands.Key.up, true, true, false, false); commandManager.setShortcutKey("customSelectUp", GC.Spread.Commands.Key.up, true, true, false, false); // reuse the existed navigationDown by a new name to bind a Enter key commandManager.register('navigationDownForEnter', GC.Spread.Sheets.Commands.navigationDown); // one command could not map to two shortcut. setEnterShortcut(false); updateCommitInputDescription(true); spread.setActiveSheetIndex(0); updateOptionsPanel(); function updateOptionsPanel() { var isCommitInputSheet = spread.getActiveSheet().name() === commitInputSheetName; _getElementById("navigationOptions").style.display = isCommitInputSheet ? "none" : "block"; _getElementById("commitInputOptions").style.display = isCommitInputSheet ? "block" : "none"; if (isCommitInputSheet) { setEnterShortcut(_getElementById("commitInputOnly").checked); setTimeout(activateCommitInputEditor, 0); } else { setEnterShortcut(false); } } function updateCommitInputDescription(useCommitInput) { _getElementById("commitInputNavigationDownDescription").style.display = useCommitInput ? "none" : "block"; _getElementById("commitInputDescription").style.display = useCommitInput ? "block" : "none"; } function setEnterShortcut(useCommitInput) { commandManager.setShortcutKey(null, GC.Spread.Commands.Key.enter, false, false, false, false); if (useCommitInput) { commandManager.setShortcutKey("navigationDownForEnter", GC.Spread.Commands.Key.enter, false, false, false, false); commandManager.setShortcutKey("commitInput", GC.Spread.Commands.Key.enter, false, false, false, false); } else { commandManager.setShortcutKey("commitInputNavigationDown", GC.Spread.Commands.Key.enter, false, false, false, false); } } function activateCommitInputEditor() { var sheet = spread.getSheetFromName(commitInputSheetName); if (spread.getActiveSheet() !== sheet) { return; } sheet.setActiveCell(5, 4); sheet.startEdit(); } }; function loadSaleDataAnalysisTable(sheet, startRow, startCol, haveTitle) { var spread = sheet.parent; if (!spread) { return; } spread.suspendPaint(); if (startRow === undefined) { startRow = 0; } if (startCol === undefined) { startCol = 0; } if (sheet.getRowCount(GC.Spread.Sheets.SheetArea.viewport) - startRow < 19 || sheet.getColumnCount(GC.Spread.Sheets.SheetArea.viewport) - startCol < 10) { return; } spread.options.referenceStyle = GC.Spread.Sheets.ReferenceStyle.r1c1; if (haveTitle) { sheet.addSpan(startRow + 0, startCol + 1, 1, 10); sheet.setRowHeight(startRow + 0, 40); sheet.setValue(startRow + 0, startCol + 1, "Sale Data Analysis"); sheet.getCell(startRow + 0, startCol + 1).font("bold 30px arial"); sheet.getCell(startRow + 0, startCol + 1).vAlign(GC.Spread.Sheets.VerticalAlign.center); } sheet.addSpan(startRow + 1, startCol + 1, 1, 3); sheet.setValue(startRow + 1, startCol + 1, "Store"); sheet.addSpan(startRow + 1, startCol + 4, 1, 7); sheet.setValue(startRow + 1, startCol + 4, "Goods"); sheet.addSpan(startRow + 2, startCol + 1, 1, 2); sheet.setValue(startRow + 2, startCol + 1, "Area"); sheet.addSpan(startRow + 2, startCol + 3, 2, 1); sheet.setValue(startRow + 2, startCol + 3, "ID"); sheet.addSpan(startRow + 2, startCol + 4, 1, 2); sheet.setValue(startRow + 2, startCol + 4, "Fruits"); sheet.addSpan(startRow + 2, startCol + 6, 1, 2); sheet.setValue(startRow + 2, startCol + 6, "Vegetables"); sheet.addSpan(startRow + 2, startCol + 8, 1, 2); sheet.setValue(startRow + 2, startCol + 8, "Foods"); sheet.addSpan(startRow + 2, startCol + 10, 2, 1); sheet.setValue(startRow + 2, startCol + 10, "Total"); sheet.setValue(startRow + 3, startCol + 1, "State"); sheet.setValue(startRow + 3, startCol + 2, "City"); sheet.setValue(startRow + 3, startCol + 4, "Grape"); sheet.setValue(startRow + 3, startCol + 5, "Apple"); sheet.setValue(startRow + 3, startCol + 6, "Potato"); sheet.setValue(startRow + 3, startCol + 7, "Tomato"); sheet.setValue(startRow + 3, startCol + 8, "Sandwich"); sheet.setValue(startRow + 3, startCol + 9, "Hamburger"); sheet.addSpan(startRow + 4, startCol + 1, 7, 1); sheet.addSpan(startRow + 4, startCol + 2, 3, 1); sheet.addSpan(startRow + 7, startCol + 2, 3, 1); sheet.addSpan(startRow + 10, startCol + 2, 1, 2); sheet.setValue(startRow + 10, startCol + 2, "Sub Total:"); sheet.addSpan(startRow + 11, startCol + 1, 7, 1); sheet.addSpan(startRow + 11, startCol + 2, 3, 1); sheet.addSpan(startRow + 14, startCol + 2, 3, 1); sheet.addSpan(startRow + 17, startCol + 2, 1, 2); sheet.setValue(startRow + 17, startCol + 2, "Sub Total:"); sheet.addSpan(startRow + 18, startCol + 1, 1, 3); sheet.setValue(startRow + 18, startCol + 1, "Total:"); sheet.setValue(startRow + 4, startCol + 1, "NC"); sheet.setValue(startRow + 4, startCol + 2, "Raleigh"); sheet.setValue(startRow + 7, startCol + 2, "Charlotte"); sheet.setValue(startRow + 4, startCol + 3, "001"); sheet.setValue(startRow + 5, startCol + 3, "002"); sheet.setValue(startRow + 6, startCol + 3, "003"); sheet.setValue(startRow + 7, startCol + 3, "004"); sheet.setValue(startRow + 8, startCol + 3, "005"); sheet.setValue(startRow + 9, startCol + 3, "006"); sheet.setValue(startRow + 11, startCol + 1, "PA"); sheet.setValue(startRow + 11, startCol + 2, "Philadelphia"); sheet.setValue(startRow + 14, startCol + 2, "Pittsburgh"); sheet.setValue(startRow + 11, startCol + 3, "007"); sheet.setValue(startRow + 12, startCol + 3, "008"); sheet.setValue(startRow + 13, startCol + 3, "009"); sheet.setValue(startRow + 14, startCol + 3, "010"); sheet.setValue(startRow + 15, startCol + 3, "011"); sheet.setValue(startRow + 16, startCol + 3, "012"); for (var i = 4; i < 10; i++) { sheet.setFormula(startRow + 10, startCol + i, "=SUM(R[-6]C:R[-1]C"); sheet.setFormula(startRow + 17, startCol + i, "=SUM(R[-6]C:R[-1]C"); sheet.setFormula(startRow + 18, startCol + i, "=R[-8]C + R[-1]C"); } sheet.setFormula(startRow + 18, startCol + 10, "=R[-8]C + R[-1]C"); for (var i = startRow; i < 14 + startRow; i++) { sheet.setFormula(4 + i, startCol + 10, "=SUM(RC[-6]:RC[-1])"); } sheet.getRange(startRow + 1, startCol + 1, 3, 10).backColor("#D9D9FF"); sheet.getRange(startRow + 4, startCol + 1, 15, 3).backColor("#D9FFD9"); sheet.getRange(startRow + 1, startCol + 1, 3, 10).hAlign(GC.Spread.Sheets.HorizontalAlign.center); sheet.getRange(startRow + 1, startCol + 1, 18, 10).setBorder(new GC.Spread.Sheets.LineBorder("Black", GC.Spread.Sheets.LineStyle.thin), {all: true}); sheet.getRange(startRow + 4, startCol + 4, 3, 6).setBorder(new GC.Spread.Sheets.LineBorder("Green", GC.Spread.Sheets.LineStyle.dotted), {innerHorizontal: true}); sheet.getRange(startRow + 7, startCol + 4, 3, 6).setBorder(new GC.Spread.Sheets.LineBorder("Green", GC.Spread.Sheets.LineStyle.dotted), {innerHorizontal: true}); sheet.getRange(startRow + 11, startCol + 4, 3, 6).setBorder(new GC.Spread.Sheets.LineBorder("Green", GC.Spread.Sheets.LineStyle.dotted), {innerHorizontal: true}); sheet.getRange(startRow + 14, startCol + 4, 3, 6).setBorder(new GC.Spread.Sheets.LineBorder("Green", GC.Spread.Sheets.LineStyle.dotted), {innerHorizontal: true}); fillSampleData(sheet, new GC.Spread.Sheets.Range(startRow + 4, startCol + 4, 6, 6)); fillSampleData(sheet, new GC.Spread.Sheets.Range(startRow + 11, startCol + 4, 6, 6)); function fillSampleData(sheet, range) { for (var i = 0; i < range.rowCount; i++) { for (var j = 0; j < range.colCount; j++) { sheet.setValue(range.row + i, range.col + j, Math.ceil(Math.random() * 300)); } } }; spread.options.referenceStyle = GC.Spread.Sheets.ReferenceStyle.a1; spread.resumePaint(); } function _getElementById(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> <aside id="navigationOptions" class="options-container" aria-label="Navigation action settings"> <div class="panel-header"> <h1>Navigation Actions</h1> <p class="panel-description">Change how Tab and Shift+Tab move between cells and page controls.</p> </div> <section class="control-section"> <h2>Candidate Controls</h2> <div class="field-row"> <input type="text" id="preEdit" placeholder="Previous Edit" title="Candidate previous control" /> <input type="button" id="preButton" title="Candidate previous control too" value="Previous Button" /> </div> <div class="field-row"> <input type="text" id="nextEdit" placeholder="Next Edit" title="Candidate next control" /> <input type="button" id="nextButton" title="Candidate next control too" value="Next Button" /> </div> </section> <section class="control-section"> <h2>Tab Behavior</h2> <div class="field"> <label for="tabAction">Tab for:</label> <select id="tabAction"> <option value="0" selected="selected">Next cell</option> <option value="1">Next control</option> <option value="2">Next cell then control</option> </select> <p class="hint">Controls which navigation action happens when the user presses Tab.</p> </div> <div class="field"> <label for="shiftTabAction">Shift + Tab for:</label> <select id="shiftTabAction"> <option value="0" selected="selected">Previous cell</option> <option value="1">Previous control</option> <option value="2">Previous cell then control</option> </select> <p class="hint">Controls which navigation action happens when the user presses Shift+Tab.</p> </div> </section> <section class="control-section"> <h2>Control Targets</h2> <div class="field"> <label for="nextControl">Next control:</label> <select id="nextControl"> <option value="" selected="selected">Not set (auto detect)</option> <option value="nextEdit">Next Edit</option> <option value="nextButton">Next Button</option> </select> <p class="hint">Defines which control is the next control in relation to the Spread instance.</p> </div> <div class="field"> <label for="preControl">Previous control:</label> <select id="preControl"> <option value="" selected="selected">Not set (auto detect)</option> <option value="preEdit">Previous Edit</option> <option value="preButton">Previous Button</option> </select> <p class="hint">Defines which control is the previous control in relation to the Spread instance.</p> </div> </section> <section class="control-section"> <h2>Selection</h2> <label class="check-field" for="hideSelection"> <input type="checkbox" id="hideSelection" /> <span> <strong>Hide Selection</strong> <small>Hides the selection in the Spread instance.</small> </span> </label> </section> </aside> <aside id="commitInputOptions" class="options-container" aria-label="Commit input settings" style="display: none;"> <div class="panel-header"> <h1>Commit Input</h1> <p class="panel-description" id="commitInputNavigationDownDescription">Commit And Navigate Down uses commitInputNavigationDown. Press Enter while editing to commit the value and move the selection down.</p> <p class="panel-description" id="commitInputDescription" style="display: none;">Commit uses navigationDown and commitInput together. Press Enter while editing to commit the value and keep the active cell in place; press Enter outside edit mode to move down.</p> </div> <section class="control-section"> <h2>Enter Behavior</h2> <label class="check-field" for="commitInputOnly"> <input type="checkbox" id="commitInputOnly" checked /> <span> <strong>Commit only on Enter</strong> <small>Binds Enter to navigationDown followed by commitInput.</small> </span> </label> </section> </aside> </div> </body> </html>
html, body { height: 100%; margin: 0; } body { position: absolute; inset: 0; color: #1f2933; background: #f7f8fa; } .sample-tutorial { display: flex; height: 100%; overflow: hidden; } .sample-spreadsheets { flex: 1 1 auto; min-width: 0; height: 100%; overflow: hidden; } .options-container { flex: 0 0 320px; width: 320px; height: 100%; padding: 24px 20px; box-sizing: border-box; overflow: auto; background: #ffffff; border-left: 1px solid #e5e7eb; } .panel-header { padding-bottom: 20px; border-bottom: 1px solid #eef0f3; } .panel-header h1 { margin: 0; font-size: 20px; font-weight: 650; line-height: 1.25; } .panel-description { margin: 10px 0 0; color: #6b7280; font-size: 13px; line-height: 1.6; } .control-section { padding: 20px 0; border-bottom: 1px solid #eef0f3; } .control-section:last-child { border-bottom: 0; } .control-section h2 { margin: 0 0 14px; color: #374151; font-size: 13px; font-weight: 700; line-height: 1.3; } .field { display: grid; gap: 7px; margin-bottom: 14px; } .field:last-child { margin-bottom: 0; } .field-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; margin-bottom: 12px; } .field-row:last-child { margin-bottom: 0; } label { color: #374151; font-size: 13px; font-weight: 600; line-height: 1.35; } select, input[type="text"], input[type="button"] { width: 100%; height: 34px; padding: 0 10px; box-sizing: border-box; color: #111827; font: inherit; background: #ffffff; border: 1px solid #d8dde5; border-radius: 6px; outline: none; transition: border-color .15s ease, box-shadow .15s ease; } select:focus, input[type="text"]:focus, input[type="button"]:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, .12); } input[type="button"] { color: #1f2933; font-weight: 600; cursor: pointer; } input[type="button"]:hover { background: #f8fafc; } .hint { margin: 0; color: #7a8492; font-size: 12px; line-height: 1.5; } .check-field { display: flex; gap: 10px; align-items: flex-start; padding: 10px 0; cursor: pointer; } .check-field input { width: 16px; height: 16px; margin: 2px 0 0; accent-color: #2563eb; flex: 0 0 auto; } .check-field span { display: grid; gap: 3px; } .check-field strong { color: #1f2933; font-size: 13px; font-weight: 650; line-height: 1.35; } .check-field small { color: #7a8492; font-size: 12px; font-weight: 400; line-height: 1.45; } @media (max-width: 760px) { .sample-tutorial { flex-direction: column; } .sample-spreadsheets { min-height: 55%; } .options-container { flex: 0 0 auto; width: 100%; height: 45%; border-top: 1px solid #e5e7eb; border-left: 0; } }