UNPKG

@odoo/o-spreadsheet

Version:
1,307 lines (1,259 loc) 352 kB
<!-- This file is generated by o-spreadsheet build tools. Do not edit it. @see https://github.com/odoo/o-spreadsheet @version 19.4.1 @date 2026-07-01T05:05:20.289Z @hash e359501 --> <odoo> <t t-name="o-spreadsheet-ValidationMessages"> <t t-if="this.props.messages"> <t t-foreach="this.props.messages" t-as="msg" t-key="msg_index"> <div t-att-class="this.divClasses" class="d-flex flex-column p-3 m-1 o-validation"> <div class="d-flex align-items-center"> <div class="flex-shrink-0"> <t t-if="this.props.msgType === 'info'" t-call="o-spreadsheet-Icon.CIRCLE_INFO"/> <t t-else="" t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/> </div> <div class="d-flex flex-column overflow-hidden"> <span class="ps-2 text-truncate" t-out="msg"/> </div> </div> </div> </t> </t> <t t-if="this.props.slots and this.props.slots['info-text']"> <div t-att-class="this.divClasses" class="d-flex flex-column p-3 m-1 o-validation"> <div class="d-flex align-items-center"> <div class="flex-shrink-0"> <t t-if="this.props.msgType === 'info'" t-call="o-spreadsheet-Icon.CIRCLE_INFO"/> <t t-else="" t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/> </div> <div class="d-flex flex-column overflow-hidden"> <span class="ps-2" t-call-slot="info-text"/> </div> </div> </div> </t> </t> <t t-name="o-spreadsheet-TopBar"> <div class="o-spreadsheet-topbar d-flex flex-column user-select-none bg-white" t-on-click="this.props.onClick"> <div t-if="!this.env.isSmall" class="o-topbar-top d-flex justify-content-between border-bottom"> <!-- Menus --> <div class="o-topbar-topleft d-flex" t-ref="this.topBarTopRef"> <t t-foreach="this.menus" t-as="menu" t-key="menu_index"> <div t-if="menu.children.length !== 0" class="o-topbar-menu o-hoverable-button text-nowrap rounded" t-att-class="{'active': this.state.menuState.parentMenu and this.state.menuState.parentMenu.id === menu.id}" t-on-click="(ev) => this.toggleContextMenu(menu, ev)" t-on-mouseover="(ev) => this.onMenuMouseOver(menu, ev)" t-att-data-id="menu.id"> <t t-out="this.getMenuName(menu)"/> </div> </t> </div> <div class="o-topbar-topright d-flex justify-content-end align-items-center"> <div t-foreach="this.topbarComponents" t-as="comp" t-key="comp.id" class="px-1"> <t t-component="comp.component"/> </div> </div> </div> <!-- Toolbar and Cell Content --> <div class="d-flex o-topbar-responsive" t-att-class="{'o-topbar-responsive': !this.env.model.getters.isReadonly()}" t-ref="this.toolBarContainerRef"> <div class="o-topbar-toolbar d-flex flex-grow-1" t-att-class="{'flex-shrink-0': this.env.model.getters.isReadonly()}"> <!-- Toolbar --> <div t-if="this.env.model.getters.isReadonly()" class="o-readonly-toolbar d-flex flex-grow-1 align-items-center text-muted"> <span> <i class="fa fa-eye"/> Readonly Access </span> </div> <div t-else="" class="o-toolbar-tools d-flex flex-grow-1 align-items-center" t-ref="this.toolbarRef"> <div class="d-flex h-100 o-named-range-selector-container align-items-center ms-2 me-1" t-ref="this.namedRangesRef" t-on-click.stop=""> <NamedRangeSelector/> <div class="o-topbar-divider border-end"/> </div> <div class="d-flex tool-container h-100" t-foreach="this.toolsCategories" t-as="category" t-key="category" t-att-id="category"> <t t-foreach="this.toolbarMenuRegistry.getEntries(category)" t-as="toolbarAction" t-key="toolbarAction_index"> <t t-component="toolbarAction.component" t-props="toolbarAction.props"/> </t> <div t-if="this.showDivider(category_index)" class="o-topbar-divider border-end"/> </div> <div t-ref="this.moreToolsContainerRef" class="d-flex align-items-center flex-grow-1 me-auto more-tools-container"> <span class="o-toolbar-button o-menu-item-button o-hoverable-button me-2 px-1 py-2 more-tools" t-ref="this.moreToolsButtonRef" t-on-click.stop="this.toggleMoreTools"> <t t-call="o-spreadsheet-Icon.SHORT_THIN_DRAG_HANDLE"/> </span> </div> </div> </div> <TopBarComposer t-if="!this.env.isSmall"/> </div> <div t-if="this.fingerprints.isEnabled" class="topbar-banner irregularity-map d-flex align-items-center justify-content-between border-top"> <div t-on-click="() => this.fingerprints.disable()" role="button" title="This tool analyzes spreadsheet formulas for patterns and highlights inconsistencies. Irregularities may indicate potential errors in formula structures, references, or arguments. (Click to turn off)" class="h-100 d-flex align-items-center text-info px-3"> <t t-call="o-spreadsheet-Icon.IRREGULARITY_MAP"/> Irregularity map </div> <div class="ps-3 h-100 flex-fill d-flex justify-content-between rounded-0 alert alert-info ps-0 py-0 my-0"> <span class="d-flex align-items-center"> This tool analyzes formulas for patterns and highlights inconsistencies. Irregularities may indicate potential errors in formula structures, references or arguments. </span> <div class="ps-3 btn o-button-link flex-shrink-0" t-on-click="() => this.fingerprints.disable()"> Turn off </div> </div> </div> <div t-if="!this.env.model.getters.isAutomaticEvaluationEnabled()" class="topbar-banner manual-evaluation d-flex align-items-center justify-content-between border-top"> <div class="h-100 d-flex align-items-center text-warning px-3"> <i class="fa fa-pause-circle me-1"/> Manual calculation </div> <div class="ps-3 h-100 flex-fill d-flex justify-content-between rounded-0 alert alert-warning ps-0 py-0 my-0"> <span class="d-flex align-items-center"> Automatic evaluation is disabled. Press F9 to recalculate. </span> <div class="ps-3 btn o-button-link flex-shrink-0" t-on-click="() => this.env.model.dispatch('SET_AUTOMATIC_EVALUATION', { enabled: true })"> Re-enable </div> </div> </div> </div> <MenuPopover t-if="this.state.menuState.isOpen" t-key="this.state.menuState.parentMenu.id" anchorRect="this.state.menuState.anchorRect" menuItems="this.state.menuState.menuItems" onClose="() => this.closeMenus()" onMenuClicked="() => this.props.onClick()" popoverPositioning="'bottom-left'" menuId="this.state.menuState.parentMenu.id" autoSelectFirstItem="this.state.menuState.autoSelectFirstItem" onKeyboardNavigation.bind="this.onKeyboardNavigation" /> <Popover t-if="this.state.toolsPopoverState.isOpen" t-props="this.toolsPopoverProps"> <div class="d-flex px-2 py-1 flex-wrap align-items-center"> <t t-foreach="this.state.invisibleToolsCategories" t-as="category" t-key="category" t-att-id="category"> <t t-foreach="this.toolbarMenuRegistry.getEntries(category)" t-as="toolbarAction" t-key="toolbarAction_index"> <t t-component="toolbarAction.component" t-props="toolbarAction.props"/> </t> <div t-if="category_index &lt; this.state.invisibleToolsCategories.length-1" class="o-topbar-divider border-end" /> </t> </div> </Popover> </t> <div t-name="o-spreadsheet-TopBarZoom" t-on-click.stop=""> <NumberEditor currentValue="this.currentFontSize" onValueChange.bind="this.setZoom" class="this.props.class" onToggle.bind="this.toggle" onFocusInput.bind="this.onFocusInput" valueIcon="'%'" min="25" max="300" valueList="this.valueList" title.translate="Zoom" /> </div> <div t-name="o-spreadsheet-NumberFormatsTool" t-ref="this.buttonRef" t-on-click.stop=""> <ActionButton action="this.formatNumberMenuItemSpec" hasTriangleDownIcon="true" onClick.bind="this.toggleMenu" class="this.props.class" /> <MenuPopover t-if="this.isActive" anchorRect="this.state.anchorRect" menuItems="this.state.menuItems" onClose="() => {}" popoverPositioning="'bottom-left'" /> </div> <t t-name="o-spreadsheet-TopBarFontSizeEditor"> <FontSizeEditor currentFontSize="this.currentFontSize" onFontSizeChanged.bind="this.setFontSize" class="this.class" onToggle.bind="this.onToggle" onFocusInput.bind="this.onFocusInput" /> </t> <div t-name="o-spreadsheet-DropdownAction" class="o-dropdown" t-ref="this.actionRef" t-on-click.stop=""> <ActionButton action="this.props.parentAction" hasTriangleDownIcon="true" onClick.bind="this.toggleDropdown" class="this.props.class" /> <Popover t-if="this.isActive" t-props="this.popoverProps"> <div class="o-dropdown-content p-1" t-if="this.isActive" t-on-click.stop=""> <div class="o-dropdown-line"> <t t-foreach="this.props.childActions" t-as="action" t-key="action_index"> <ActionButton action="action" class="this.props.childClass"/> </t> </div> </div> </Popover> </div> <t t-name="o-spreadsheet-ColorEditor"> <div class="d-flex align-items-center" t-att-class="{'o-disabled': this.env.model.getters.isCurrentSheetLocked() }"> <ColorPickerWidget currentColor="this.currentColor" toggleColorPicker.bind="this.onClick" showColorPicker="this.isMenuOpen" onColorPicked="(color) => this.setColor(color)" title="this.props.title" icon="this.props.icon" class="this.props.class" /> </div> </t> <t t-name="o-spreadsheet-TextInput"> <div class="position-relative"> <input type="text" t-ref="this.genericInputRef" t-att-class="this.inputClass" t-att-id="this.props.id" t-att-placeholder="this.props.placeholder" t-on-change="this.save" t-on-blur="this.onBlur" t-on-pointerdown="this.onMouseDown" t-on-pointerup="this.onMouseUp" t-on-focus="this.onFocus" t-on-input="this.onInput" t-on-keydown="this.onKeyDown" t-att-maxlength="this.props.maxLength" /> <span t-if="this.props.errorMessage" class="os-input-error-icon text-danger position-absolute d-flex align-items-center" t-att-title="this.props.errorMessage"> <t t-call="o-spreadsheet-Icon.ERROR"/> </span> </div> </t> <t t-name="o-spreadsheet-TableStylesPopover"> <Popover t-if="this.props.popoverProps" t-props="this.props.popoverProps"> <div class="o-table-style-popover d-flex flex-column py-3" t-ref="this.tableStyleListRef" t-on-contextmenu.prevent=""> <div class="d-flex o-notebook ps-4 mb-3"> <div t-foreach="Object.keys(this.categories)" t-as="category" t-key="category" class="o-notebook-tab d-flex align-items-center border" t-att-class="{ 'selected': this.state.selectedCategory === category }" t-on-click="() => this.state.selectedCategory = category" t-att-data-id="category" t-out="this.categories[category_value]" /> </div> <div class="d-flex flex-wrap px-4" t-att-class="this.props.type === 'pivot' ? 'o-pivot-previews' : 'o-table-previews'"> <t t-foreach="this.displayedStyles" t-as="styleId" t-key="styleId"> <TableStylePreview styleId="styleId" selected="styleId === this.props.selectedStyleId" tableConfig="this.props.tableConfig" tableStyle="this.props.tableStyles[styleId]" onClick="() => this.props.onStylePicked(styleId)" type="this.props.type" /> </t> <div t-if="this.state.selectedCategory === 'custom'" class="o-new-table-style o-table-style-list-item o-table-style-popover-preview d-flex justify-content-center align-items-center" t-on-click="this.newTableStyle"> + </div> </div> </div> </Popover> </t> <t t-name="o-spreadsheet-TableStylePreview"> <div class="o-table-style-list-item position-relative" t-att-class="{ 'selected': this.props.selected }" t-att-data-id="this.props.styleId" t-att-title="this.styleName" t-on-click="this.props.onClick" t-on-contextmenu.prevent="(ev) => this.onContextMenu(ev)"> <div class="o-table-preview"> <canvas t-ref="this.canvasRef" class="w-100 h-100 os-theme-dependant"/> </div> <div class="o-table-style-edit-button position-absolute d-none bg-white border" t-if="this.isStyleEditable" t-on-click="this.editTableStyle" title="Edit custom table style"> <t t-call="o-spreadsheet-Icon.EDIT"/> </div> </div> <MenuPopover t-if="this.menu.isOpen" menuItems="this.menu.menuItems" anchorRect="this.menu.anchorRect" onClose.bind="this.closeMenu" /> </t> <t t-name="o-spreadsheet-TableStylePicker"> <div class="o-table-style-picker d-flex flew-row justify-content-between ps-1 border rounded"> <div class="d-flex flex-row overflow-hidden" t-att-class="this.props.type === 'pivot' ? 'o-pivot-previews' : 'o-table-previews ps-2'"> <t t-foreach="this.getDisplayedTableStyles()" t-as="styleId" t-key="styleId"> <TableStylePreview selected="styleId === this.props.tableConfig.styleId" tableConfig="this.props.tableConfig" tableStyle="this.props.tableStyles[styleId]" styleId="styleId" onClick="() => this.onStylePicked(styleId)" type="this.props.type" /> </t> </div> <div class="o-table-style-picker-arrow d-flex align-items-center px-1 border-start" t-on-click.stop="this.onArrowButtonClick"> <t t-call="o-spreadsheet-Icon.CARET_DOWN"/> </div> </div> <TableStylesPopover tableConfig="this.props.tableConfig" selectedStyleId="this.props.tableConfig.styleId" onStylePicked.bind="this.onStylePicked" popoverProps="this.state.popoverProps" closePopover.bind="this.closePopover" tableStyles="this.props.tableStyles" type="this.props.type" /> </t> <t t-name="o-spreadsheet-TableResizer"> <div class="o-table-resizer position-absolute" t-att-style="this.containerStyle" t-on-pointerdown="this.onMouseDown" /> </t> <t t-name="o-spreadsheet-TableDropdownButton"> <div class="o-table-widget d-flex align-item-center" t-att-class="this.class"> <ActionButton action="this.action" hasTriangleDownIcon="true" t-on-click="this.onClick" class="'opacity-100'" /> </div> <TableStylesPopover tableConfig="this.tableConfig" onStylePicked.bind="this.onStylePicked" popoverProps="this.state.popoverProps" closePopover.bind="this.closePopover" tableStyles="this.tableStyles" type="'table'" /> </t> <t t-name="o-spreadsheet-StandaloneGridCanvas"> <canvas t-ref="this.canvasRef"/> </t> <t t-name="o-spreadsheet-SpreadsheetPrint"> <div class="o-spreadsheet-print d-flex flex-column h-100 w-100"> <t t-set="pages" t-value="this.printStore.printPages"/> <div class="o-print-header flex-shrink-0 d-flex align-items-center d-print-none"> <h3 class="m-0 p-3 ps-4 text-white">Print Preview</h3> <span class="text-white"> <t t-out="pages.length"/> pages </span> <button class="o-button ms-auto me-2 flex-grow-0" t-on-click="this.props.onExitPrintMode"> Cancel </button> <button class="o-button primary flex-grow-0 me-4" t-on-click="this.onPrint">Next</button> </div> <div class="o-print-container d-flex flex-grow-1 overflow-hidden"> <div class="o-print-preview w-100 overflow-auto d-flex flex-column"> <div class="mx-auto"> <div t-if="pages.length === 0" class="o-print-page o-empty-print-page m-4 shadow d-flex flex-column align-items-center justify-content-center flex-shrink-0" t-att-style="this.pageStyle"> <h4 class="fst-italic text-muted">No content to print</h4> </div> <div t-else="" t-foreach="pages" t-as="page" t-key="page_index" class="o-print-page m-4 shadow d-flex justify-content-center flex-shrink-0" t-att-style="this.pageStyle"> <StandaloneGridCanvas sheetId="page.sheetId" zone="page.zone" renderingCtx="page.renderingCtx" /> </div> </div> </div> <div class="o-sidePanel flex-shrink-0 bg-white border-top border-start d-print-none"> <div class="o-sidePanelBody pt-4"> <Section t-if="!this.env.model.getters.isDashboard()"> <div class="o-section-title">Print content</div> <Select selectedValue="this.printStore.printSelection" values="this.printStore.printSelectionOptions" class="'o-print-selection'" onChange.bind="this.onPrintSelectionChange" /> </Section> <Section> <div class="o-section-title">Scale</div> <Select selectedValue="this.printStore.printScale" values="this.printStore.printScaleOptions" class="'o-print-scale'" onChange.bind="this.onPrintScaleChange" /> </Section> <Section> <div class="o-section-title">Paper</div> <Select selectedValue="this.printStore.pageLayout" values="this.printStore.layoutOptions" class="'o-print-layout'" onChange.bind="this.onLayoutChange" /> </Section> <Section> <div class="o-section-title">Orientation</div> <BadgeSelection choices="this.printStore.orientationChoices" onChange.bind="this.changeOrientation" selectedValue="this.printStore.orientation" /> </Section> <Section t-if="!this.env.model.getters.isDashboard()"> <div class="o-section-title">Formatting</div> <Checkbox name="'showGridLines'" value="!this.printStore.hideGridLines" onChange.bind="this.setGridLinesVisibility" className="'mt-2'" label.translate="Show gridlines" /> </Section> </div> </div> </div> </div> </t> <t t-name="o-spreadsheet-Spreadsheet"> <div class="o-spreadsheet h-100 w-100" t-att-class="this.getSpreadSheetClasses()" t-ref="this.spreadsheetRef" t-att-style="this.getStyle()"> <t t-if="this.state.printModeEnabled"> <SpreadsheetPrint onExitPrintMode.bind="this.exitPrintMode"/> </t> <t t-elif="this.env.model.getters.isDashboard()"> <SpreadsheetDashboard getGridSize.bind="this.getGridSize"/> <FullScreenFigure/> </t> <t t-else=""> <div class="o-spreadsheet-topbar-wrapper o-two-columns"> <TopBar onClick="() => this.focusGrid()" dropdownMaxHeight="this.gridHeight"/> </div> <div class="o-grid-container" t-att-class="{'o-two-columns': !this.sidePanel.isMainPanelOpen}" t-att-style="this.gridContainerStyle" t-on-click="this.focusGrid"> <div class="o-top-left"/> <div class="o-column-groups o-zoomable"> <HeaderGroupContainer layers="this.colLayers" dimension="'COL'"/> </div> <div class="o-row-groups o-zoomable"> <HeaderGroupContainer layers="this.rowLayers" dimension="'ROW'"/> </div> <div class="o-group-grid overflow-hidden"> <Grid exposeFocus="(focus) => this._focusGrid = focus" getGridSize.bind="this.getGridSize" /> </div> </div> <SidePanels/> <div class="o-spreadsheet-bottombar-wrapper o-two-columns overflow-hidden"> <SmallBottomBar t-if="this.env.isSmall" onClick="() => this.focusGrid()"/> <BottomBar t-else="" onClick="() => this.focusGrid()"/> </div> </t> </div> </t> <t t-name="o-spreadsheet-SmallBottomBar"> <div class="o-spreadsheet-small-bottom-bar o-two-columns d-flex flex-column overflow-hidden"> <t t-if="this.menuState.isOpen"> <RibbonMenu onClose="() => this.menuState.isOpen=false"/> </t> <t t-else=""> <div class="o-small-composer px-2 py-2 position-relative bg-white border"> <div class="w-100" t-ref="this.composerRef"> <Composer t-props="this.composerProps"/> <span t-if="this.showFxIcon" class="position-absolute top-50 translate-middle-y ps-2 pe-none"> <t t-call="o-spreadsheet-Icon.FX_SVG"/> </span> </div> <span class="align-items-center d-flex justify-content-center o-selection-button" title="confirm edition" t-if="this.focus !== 'inactive'" t-on-click="() => this.composerStore.stopEdition()"> <span class="d-flex"> <t t-call="o-spreadsheet-Icon.CHECK"/> </span> </span> </div> <div class="d-flex flex-row mb-1" t-if="this.focus !== 'inactive'"> <div t-foreach="this.symbols" t-as="symbol" t-key="symbol_index" class="o-spreadsheet-editor-symbol w-100 d-flex justify-content-center align-items-center mx-1" t-out="symbol" tabindex="-1" t-att-title="symbol" t-on-click="() => this.insertSymbol(symbol)" composerFocusableElement="true" /> </div> <div class="d-flex flex-fill align-items-center bottom-bar-menu border-top border-bottom"> <Ripple> <div class="py-1 px-1 mx-2 ribbon-toggler" t-on-click="this.toggleRibbon"> <i class="o-icon fa fa-cog"/> </div> </Ripple> <BottomBar onClick="this.props.onClick"/> </div> </t> </div> </t> <div t-name="o-spreadsheet-RibbonMenu"> <div class="o-ribbon-menu d-flex flex-column" t-ref="this.menuRef"> <div class="o-ribbon-title d-flex py-2 fw-bold"> <div class="o-previous-button px-3 py-1 mx-2 rounded" t-on-click="this.onClickBack" t-att-title="this.backTitle"> <i class="fa fa-angle-left"/> </div> <span class="d-flex align-items-center" t-out="this.state.title"/> </div> <div class="o-ribbon-menu-wrapper overflow-auto" t-ref="this.containerRef" t-on-scroll="this.updateShadows"> <Menu t-props="this.menuProps"/> </div> </div> </div> <t t-name="o-spreadsheet-TableStyleEditorPanel"> <div class="o-table-style-editor-panel"> <Section title.translate="Style name"> <input type="text" class="o-input" t-custom-model="this.state.styleName"/> </Section> <Section class="'pt-1'" title.translate="Style color"> <RoundColorPicker currentColor="this.state.primaryColor" onColorPicked.bind="this.onColorPicked" disableNoColor="true" /> </Section> <Section class="'pt-1'" title.translate="Style template"> <div class="d-flex flex-wrap"> <t t-foreach="this.tableTemplates" t-as="templateName" t-key="templateName"> <TableStylePreview selected="templateName === this.state.selectedTemplateName" tableConfig="this.previewTableConfig" tableStyle="this.computeTableStyle(templateName)" onClick="() => this.onTemplatePicked(templateName)" type="'table'" /> </t> </div> </Section> <Section> <div class="o-sidePanelButtons"> <button t-if="this.props.styleId" t-on-click="this.onDelete" class="o-delete o-button-danger o-button"> Delete </button> <button t-on-click="this.onCancel" class="o-cancel o-button">Discard</button> <button t-on-click="this.onConfirm" class="o-confirm o-button primary">Confirm</button> </div> </Section> </div> </t> <t t-name="o-spreadsheet-TablePanel"> <div class="o-table-panel"> <Section title.translate="Style options"> <div class="d-flex flex-row"> <div class="w-50"> <div class="d-flex align-items-center"> <Checkbox label="this.getCheckboxLabel('headerRow')" name="'headerRow'" value="this.tableConfig.numberOfHeaders > 0" onChange.bind="this.updateHasHeaders" /> <NumberInput t-if="this.tableConfig.numberOfHeaders > 0" value="this.tableConfig.numberOfHeaders" class="'o-table-n-of-headers ms-2'" min="0" onChange.bind="this.onChangeNumberOfHeaders" /> </div> <Checkbox label="this.getCheckboxLabel('totalRow')" name="'totalRow'" value="this.tableConfig.totalRow" onChange="(val) => this.updateTableConfig('totalRow', val)" /> <Checkbox label="this.getCheckboxLabel('bandedRows')" name="'bandedRows'" value="this.tableConfig.bandedRows" onChange="(val) => this.updateTableConfig('bandedRows', val)" /> <Checkbox label="this.getCheckboxLabel('hasFilters')" name="'hasFilters'" value="this.tableConfig.hasFilters" title="this.hasFilterCheckboxTooltip" disabled="!this.canHaveFilters" onChange.bind="this.updateHasFilters" /> </div> <div> <Checkbox label="this.getCheckboxLabel('firstColumn')" name="'firstColumn'" value="this.tableConfig.firstColumn" onChange="(val) => this.updateTableConfig('firstColumn', val)" /> <Checkbox label="this.getCheckboxLabel('lastColumn')" name="'lastColumn'" value="this.tableConfig.lastColumn" onChange="(val) => this.updateTableConfig('lastColumn', val)" /> <Checkbox label="this.getCheckboxLabel('bandedColumns')" name="'bandedColumns'" value="this.tableConfig.bandedColumns" onChange="(val) => this.updateTableConfig('bandedColumns', val)" /> </div> </div> </Section> <Section> <TableStylePicker tableConfig="this.props.table.config" onStylePicked.bind="this.onStylePicked" tableStyles="this.env.model.getters.getTableStyles()" type="'table'" /> </Section> <Section title.translate="Data range"> <SelectionInput t-key="this.props.table.type" ranges="[this.state.tableXc]" hasSingleRange="true" isInvalid="this.state.tableZoneErrors.length !== 0" onSelectionChanged="(ranges) => this.onRangeChanged(ranges)" onSelectionConfirmed.bind="this.onRangeConfirmed" /> </Section> <Section class="'pt-0'"> <Checkbox label="this.getCheckboxLabel('automaticAutofill')" name="'automaticAutofill'" value="this.tableConfig.automaticAutofill and this.props.table.type !== 'dynamic'" onChange="(val) => this.updateTableConfig('automaticAutofill', val)" className="'mb-1'" disabled="this.props.table.type === 'dynamic'" /> <div class="d-flex flex-row align-items-center"> <Checkbox label="this.getCheckboxLabel('isDynamic')" name="'isDynamic'" value="this.props.table.type === 'dynamic'" onChange.bind="this.updateTableIsDynamic" disabled="!this.canBeDynamic" /> <div class="o-info-icon d-flex flex-row align-items-center text-muted ms-1" t-att-title="this.dynamicTableTooltip"> <t t-call="o-spreadsheet-Icon.CIRCLE_INFO"/> </div> </div> </Section> <Section> <div class="o-sidePanelButtons"> <button t-on-click="this.deleteTable" class="o-table-delete o-button o-button-danger"> Delete table </button> </div> </Section> <Section t-if="this.errorMessages.length"> <ValidationMessages messages="this.errorMessages" msgType="'error'"/> </Section> </div> </t> <t t-name="o-spreadsheet-SplitIntoColumnsPanel"> <div class="o-split-to-cols-panel"> <Section title.translate="Separator"> <Select class="'mb-3'" values="this.separators" selectedValue="this.state.separatorValue" onChange.bind="this.onSeparatorChange" /> <input class="o-input mb-3" type="text" t-if="this.state.separatorValue === 'custom'" t-att-value="this.state.customSeparator" t-on-input="this.updateCustomSeparator" placeholder="Add any characters or symbol" /> <t t-set="addColumnsLabel">Add new columns to avoid overwriting cells</t> <Checkbox value="this.state.addNewColumns" label="addColumnsLabel" onChange.bind="this.updateAddNewColumnsCheckbox" /> </Section> <Section> <div class="o-sidePanelButtons"> <button class="o-button primary" t-att-class="{'o-disabled': this.isConfirmDisabled}" t-on-click="this.confirm"> Confirm </button> </div> </Section> <Section t-if="this.errorMessages.length || this.warningMessages.length" class="'pb-0 pt-2'"> <ValidationMessages messages="this.errorMessages" msgType="'error'"/> <ValidationMessages messages="this.warningMessages" msgType="'warning'"/> </Section> </div> </t> <t t-name="o-spreadsheet-SidePanels" t-if="this.sidePanelStore.isMainPanelOpen"> <div class="o-sidePanels d-flex overflow-hidden"> <t t-foreach="this.panelList" t-as="panel" t-key="panel.key"> <div t-att-style="panel.style"> <SidePanel t-key="panel.key" t-props="panel.props"/> </div> </t> </div> </t> <t t-name="o-spreadsheet-SidePanel"> <t t-if="this.props.isCollapsed" t-call="o-spreadsheet-SidePanelCollapsed"/> <t t-else="" t-call="o-spreadsheet-SidePanelExtended"/> </t> <t t-name="o-spreadsheet-SidePanelExtended"> <div class="o-sidePanel h-100 bg-white border-top border-start"> <div class="o-sidePanelHeader d-flex align-items-center justify-content-between border-bottom"> <div t-if="this.props.onToggleCollapsePanel" class="o-collapse-panel o-sidePanelAction rounded" t-on-click="this.props.onToggleCollapsePanel"> <i class="fa fa-angle-double-right"/> </div> <div class="o-sidePanelTitle o-fw-bold ms-2" t-out="this.getTitle()"/> <div t-if="this.props.isPinned" class="o-pin-panel o-sidePanelAction ms-auto rounded active" t-on-click="this.props.onTogglePinPanel"> <i class="fa fa-thumb-tack"/> </div> <div class="o-sidePanelClose o-sidePanelAction rounded" t-on-click="this.props.onCloseSidePanel"> ✕ </div> </div> <div class="o-sidePanelBody-container d-flex flex-grow-1 "> <div class="o-sidePanel-handle-container"> <div class="o-sidePanel-handle" t-on-pointerdown="this.props.onStartHandleDrag" t-on-dblclick="this.props.onResetPanelSize"> <t t-call="o-spreadsheet-Icon.THIN_DRAG_HANDLE"/> </div> </div> <div class="o-sidePanelBody"> <t t-component="this.props.panelContent.Body" t-props="this.props.panelProps" onCloseSidePanel="this.props.onCloseSidePanel" /> </div> <div class="o-sidePanelFooter" t-if="this.props.panelContent?.Footer"> <t t-component="this.props.panelContent.Footer" t-props="this.props.panelProps"/> </div> </div> </div> </t> <t t-name="o-spreadsheet-SidePanelCollapsed"> <div class="o-sidePanel collapsed w-100 h-100 bg-white" t-on-click="this.props.onToggleCollapsePanel"> <div class="d-flex flex-column align-items-center"> <div t-if="this.props.onToggleCollapsePanel" class="o-collapse-panel o-sidePanelAction rounded mb-1"> <i class="fa fa-angle-double-left"/> </div> <div t-if="this.props.isPinned" class="o-pin-panel o-sidePanelAction ms-auto rounded active" t-on-click="this.props.onTogglePinPanel"> <i class="fa fa-thumb-tack"/> </div> <div class="o-sidePanelClose o-sidePanelAction rounded mb-1" t-on-click.stop="this.props.onCloseSidePanel"> ✕ </div> <div class="o-sidePanelTitle o-fw-bold" t-out="this.getTitle()"/> </div> </div> </t> <t t-name="o-spreadsheet-SettingsPanel"> <div class="o-settings-panel"> <Section title.translate="Locale"> <Select selectedValue="this.currentLocale.code" values="this.selectOptions" onChange="(value) => this.onLocaleChange(value)" /> <div class="o-locale-preview mt-4 p-3 rounded border"> <div> <span class="o-fw-bold me-1">Number:</span> <span t-out="this.numberFormatPreview"/> </div> <div> <span class="o-fw-bold me-1">Date:</span> <span t-out="this.dateFormatPreview"/> </div> <div> <span class="o-fw-bold me-1">Date time:</span> <span t-out="this.dateTimeFormatPreview"/> </div> <div> <span class="o-fw-bold me-1">First day of week:</span> <span t-out="this.firstDayOfWeek"/> </div> </div> </Section> <Section class="'pt-0'"> <t t-set="message">Those settings affect all users.</t> <ValidationMessages messages="[message]" msgType="'info'"/> </Section> </div> </t> <t t-name="o-spreadsheet-RemoveDuplicatesPanel"> <div class="o-remove-duplicates"> <Section> <ValidationMessages messages="[this.selectionStatisticalInformation]" msgType="'info'"/> </Section> <Section class="'pt-0'"> <t t-set="dataHasHeaderLabel">Data has header row</t> <Checkbox name="'dataHasHeader'" value="this.state.hasHeader" label="dataHasHeaderLabel" onChange.bind="this.toggleHasHeader" /> </Section> <Section class="'pt-0'" title.translate="Columns to analyze"> <div class="o-checkbox-selection overflow-auto"> <t t-set="selectAllLabel">Select all</t> <Checkbox value="this.isEveryColumnSelected" label="selectAllLabel" onChange.bind="this.toggleAllColumns" /> <t t-foreach="Object.keys(this.state.columns)" t-as="colIndex" t-key="colIndex"> <Checkbox value="this.state.columns[colIndex]" label="this.getColLabel(colIndex)" onChange="() => this.toggleColumn(colIndex)" /> </t> </div> </Section> <Section> <div class="o-sidePanelButtons"> <button class="o-button primary" t-att-class="{'o-disabled': !this.canConfirm}" t-on-click="this.onRemoveDuplicates"> Remove duplicates </button> </div> </Section> <Section t-if="this.errorMessages.length"> <ValidationMessages messages="this.errorMessages" msgType="'error'"/> </Section> </div> </t> <t t-name="o-spreadsheet-PivotTitleSection"> <Section> <t t-set-slot="title"> <div class="d-flex flex-row justify-content-between align-items-center"> Name <CogWheelMenu items="this.cogWheelMenuItems"/> </div> </t> <TextInput class="'os-pivot-title'" value="this.name" onChange.bind="this.onNameChanged" selectContentOnFocus="true" /> </Section> </t> <t t-name="o-spreadsheet-PivotSidePanel"> <div class="o-pivot-panel d-flex flex-column h-100"> <div class="o-panel d-flex"> <div class="o-sidePanel-tab o-panel-configuration d-flew flex-grow-1 text-center" t-att-class="this.state.panel !== 'configuration' ? 'inactive' : ''" t-on-click="this.switchPanel.bind(this, 'configuration')"> <i class="fa fa-sliders me-1"/> Configuration </div> <div class="o-sidePanel-tab o-panel-design d-flew flex-grow-1 text-center" t-att-class="this.state.panel !== 'design' ? 'inactive' : ''" t-on-click="this.switchPanel.bind(this, 'design')"> <i class="fa fa-paint-brush me-1"/> Design </div> </div> <div class="o-panel-content overflow-hidden h-100"> <div class="h-100" t-att-class="this.state.panel !== 'configuration' ? 'd-none' : ''"> <t t-component="this.sidePanelEditor" pivotId="this.props.pivotId" onCloseSidePanel="this.props.onCloseSidePanel" /> </div> <div class="h-100" t-att-class="this.state.panel !== 'design' ? 'd-none' : ''"> <PivotDesignPanel pivotId="this.props.pivotId"/> </div> </div> </div> </t> <t t-name="o-spreadsheet-PivotSpreadsheetSidePanel"> <t t-set="isReadonly" t-value="this.env.model.getters.isReadonly()"/> <div class="d-flex flex-column h-100 justify-content-between overflow-hidden"> <div class="h-100 position-relative overflow-x-hidden overflow-y-auto" t-ref="this.pivotSidePanelRef"> <div t-att="isReadonly ? ['inert', 1] : []" t-att-class="{ 'pe-none opacity-50': isReadonly }"> <PivotTitleSection pivotId="this.props.pivotId" flipAxis.bind="this.flipAxis"/> <Section title.translate="Range"> <SelectionInput ranges="this.ranges" required="true" isInvalid="this.shouldDisplayInvalidRangeError" hasSingleRange="true" onSelectionChanged="(ranges) => this.onSelectionChanged(ranges)" onSelectionConfirmed="() => this.onSelectionConfirmed()" /> <span class="text-danger sp_range_error_message" t-if="this.shouldDisplayInvalidRangeError" t-out="this.pivot.invalidRangeMessage" /> </Section> <PivotLayoutConfigurator t-if="!this.pivot.isInvalidRange" unusedGroupableFields="this.store.unusedGroupableFields" measureFields="this.store.measureFields" unusedGranularities="this.store.unusedGranularities" dateGranularities="this.store.dateGranularities" datetimeGranularities="this.store.datetimeGranularities" definition="this.definition" onDimensionsUpdated.bind="this.onDimensionsUpdated" onFiltersUpdated.bind="this.onFiltersUpdated" getScrollableContainerEl.bind="this.getScrollableContainerEl" pivotId="this.props.pivotId" /> <t t-if="!this.pivot.isInvalidRange"> <Section class="'pt-0'"> <div class="o-fw-bold pt-0 pb-1 d-flex flex-row justify-content-between align-items-center o-section-title"> Domain <AddDimensionButton onFieldPicked.bind="this.addFilter" fields="this.store.unusedFilterFields" /> </div> <t t-foreach="this.definition.filters" t-as="filter" t-key="filter_index"> <PivotFilterEditor pivotId="this.props.pivotId" definition="this.definition" filter="filter" onFiltersUpdated.bind="this.onFiltersUpdated" /> </t> </Section> </t> </div> </div> <PivotDeferUpdate t-if="!isReadonly" deferUpdate="this.store.updatesAreDeferred" toggleDeferUpdate="(value) => this.store.deferUpdates(value)" isDirty="this.store.isDirty" discard="this.store.discardPendingUpdate" apply="this.store.applyUpdate" /> </div> </t> <t t-name="o-spreadsheet-PivotDesignPanel"> <div class="h-100 overflow-y-auto"> <Section class="'o-pivot-design'" title.translate="Display options"> <div> <div class="container-fluid p-0 pt-2"> <div class="row mb-2 align-items-center"> <div class="col-6">Max rows:</div> <div class="col-6 d-flex align-items-center"> <NumberInput value="this.pivotStyle.numberOfRows ?? ''" class="'o-pivot-n-of-rows'" placeholder.translate="e.g. 10" onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfRows')" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Max columns:</div> <div class="col-6 d-flex align-items-center"> <NumberInput value="this.pivotStyle.numberOfColumns ?? ''" class="'o-pivot-n-of-columns'" placeholder.translate="e.g. 5" onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfColumns')" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Show totals:</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'displayTotals'" value="this.pivotStyle.displayTotals ?? this.defaultStyle.displayTotals" onChange="(val) => this.updatePivotStyleProperty('displayTotals', val)" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Show column titles:</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'displayColumnHeaders'" value="this.pivotStyle.displayColumnHeaders ?? this.defaultStyle.displayColumnHeaders" onChange="(val) => this.updatePivotStyleProperty('displayColumnHeaders', val)" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Show measure titles:</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'displayMeasuresRow'" value="this.pivotStyle.displayMeasuresRow ?? this.defaultStyle.displayMeasuresRow" onChange="(val) => this.updatePivotStyleProperty('displayMeasuresRow', val)" /> </div> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Use tabular form:</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'tabularForm'" value="this.pivotStyle.tabularForm ?? this.defaultStyle.tabularForm" onChange="(val) => this.updatePivotStyleProperty('tabularForm', val)" /> </div> </div> </div> </Section> <Section class="'o-pivot-table-style'" title.translate="Pivot table style"> <div class="row mb-2 align-items-center pt-2"> <div class="col-6">Banded rows:</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'bandedRows'" value="this.pivotStyle.bandedRows ?? this.defaultStyle.bandedRows" onChange="(val) => this.updatePivotStyleProperty('bandedRows', val)" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Banded columns</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'bandedColumns'" value="this.pivotStyle.bandedColumns ?? this.defaultStyle.bandedColumns" onChange="(val) => this.updatePivotStyleProperty('bandedColumns', val)" /> </div> </div> <div class="row mb-2 align-items-center"> <div class="col-6">Filter button</div> <div class="col-6 d-flex align-items-center"> <Checkbox name="'hasFilters'" value="this.pivotStyle.hasFilters ?? this.defaultStyle.hasFilters" onChange="(val) => this.updatePivotStyleProperty('hasFilters', val)" /> </div> </div> <div class="mt-4"> <TableStylePicker tableConfig="this.tableConfig" onStylePicked.bind="this.onStylePicked" tableStyles="this.tableStyles" type="'pivot'" /> </div> </Section> </div> </t> <t t-name="o-spreadsheet-PivotMeasureDisplayPanel"> <Section title.translate="Show measure as:"> <Select class="'o-pivot-measure-display-type'" values="this.measureDisplayTypeOptions" selectedValue="this.store.measureDisplay.type" onChange="(value) => this.store.updateMeasureDisplayType(value)" /> <div class="o-pivot-measure-display-description mt-3 ps-3 border-start" t-out="this.measureDisplayDescription[this.store.measureDisplay.type]" /> </Section> <Section t-if="this.store.doesDisplayNeedsField" title.translate="Base field:"> <div class="o-pivot-measure-display-field w-100 py-1 px-3 border"> <t t-if="this.store.fields.length"> <RadioSelection choices="this.fieldChoices" selectedValue="this.store.measureDisplay.fieldNameWithGranularity" name="'baseField'" onChange.bind="(val) => this.store.updateMeasureDisplayField(val)" direction="'vertical'" /> </t> <t t-else=""> <div class="text-muted text-center my-3">No active dimension in the pivot</div> </t> </div> </Section> <t t-set="values" t-value="this.store.values"/> <Section t-if="this.store.doesDisplayNeedsValue and values.length" title.translate="Base item:"> <div class="o-pivot-measure-display-value w-100 py-1 px-3 border"> <RadioSelection choices="values" selectedValue="this.store.measureDisplay.value" name="'baseValue'" onChange.bind="(val) => this.store.updateMeasureDisplayValue(val)" direction="'vertical'" /> </div> </Section> <Section> <div class="o-sidePanelButtons"> <button t-on-click="this.onCancel" class="o-pivot-measure-cancel o-button">Discard</button> <button t-on-click="this.onSave" class="o-pivot-measure-save o-button pr