<template> <v-container class="h-100 j-excel-container pa-0 ma-0" fluid> <v-toolbar height="32" dense class="elevation-0 xc-toolbar xc-border-bottom" style="z-index: 7"> <div class="d-flex xc-border align-center search-box"> <v-menu bottom offset-y> <template v-slot:activator="{on}"> <div v-on="on"> <v-icon class="pa-1 pr-0 ml-2" small color="grey" >mdi-magnify </v-icon> <v-icon color="grey" class="pl-0 pa-1" small>mdi-menu-down </v-icon> </div> </template> <v-list dense> <v-list-item v-for="col in availableColumns" :key="col.cn" @click="searchField = col.cn"> <span class="caption">{{ col._cn }}</span> </v-list-item> </v-list> </v-menu> <v-divider vertical></v-divider> <v-text-field autocomplete="off" v-model="searchQueryVal" style="min-width: 300px" flat dense solo hide-details @keyup.enter="searchQuery = searchQueryVal" @blur="searchQuery = searchQueryVal" :placeholder="searchField ? `Search '${searchField}' column` : 'Search all columns'" class="elevation-0 pa-0 flex-grow-1 caption search-field"> </v-text-field> </div> <span v-if="relationType && false" class="caption grey--text">{{ refTable }}({{ relationPrimaryValue }}) -> {{ relationType === 'hm' ? ' Has Many ' : ' Belongs To ' }} -> {{ table }}</span> <v-spacer></v-spacer> <lock-menu v-if="_isUIAllowed('view-type')" v-model="viewStatus.type"></lock-menu> <x-btn tooltip="Reload view data" outlined small text @click="loadTableData"> <v-icon small class="mr-1" color="grey darken-3">mdi-reload</v-icon> </x-btn> <x-btn tooltip="Add new row" v-if="relationType !== 'bt'" :disabled="isLocked" outlined small text @click="insertNewRow(true,true)"> <v-icon small class="mr-1" color="grey darken-3">mdi-plus</v-icon> </x-btn> <x-btn small text outlined tooltip="Save new rows" :disabled="!edited || isLocked" @click="save"> <v-icon small class="mr-1" color="grey darken-3">save</v-icon> Save </x-btn> <fields v-model="showFields" :field-list="fieldList" :meta="meta" :is-locked="isLocked" :fieldsOrder.sync="fieldsOrder" :sqlUi="sqlUi" :showSystemFields.sync="showSystemFields"></fields> <sort-list :is-locked="isLocked" :field-list="fieldList" v-model="sortList" ></sort-list> <column-filter :is-locked="isLocked" :field-list="fieldList" v-model="filters" dense> </column-filter> <v-tooltip bottom> <template v-slot:activator="{on}"> <v-btn :disabled="isLocked" v-on="on" small @click="deleteTable('showDialog')" outlined text> <x-icon small color="red grey">mdi-delete-outline</x-icon> </v-btn> </template> <span class="">Delete table</span> </v-tooltip> <!-- Cell height --> <!-- <v-menu> <template v-slot:activator="{ on, attrs }"> <v-icon v-bind="attrs" v-on="on" small class="mx-2" color="grey darken-3" > mdi-arrow-collapse-vertical </v-icon> </template> <v-list dense class="caption"> <v-list-item v-for="h in cellHeights" dense @click.stop="cellHeight = h.size" :key="h.size"> <v-list-item-icon class="mr-1"> <v-icon small :color="cellHeight === h.size && 'primary'">{{ h.icon }}</v-icon> </v-list-item-icon> <v-list-item-title :class="{'primary--text' : cellHeight === h.size}" style="text-transform: capitalize"> {{ h.size }} </v-list-item-title> </v-list-item> </v-list> </v-menu>--> <x-btn tooltip="Toggle navigation drawer" outlined small text @click="toggleDrawer = !toggleDrawer" :btnClass="{ 'primary lighten-5' : !toggleDrawer}" > <v-icon small class="mx-2" color="grey darken-3" >{{ toggleDrawer ? 'mdi-door-closed' : 'mdi-door-open' }} </v-icon> </x-btn> <!-- <v-spacer></v-spacer>--> <!-- <v-text-field outlined dense hide-details class="elevation-0" append-icon="mdi-magnify"></v-text-field>--> </v-toolbar> <div :class="`cell-height-${cellHeight}`" style=" height:calc(100% - 32px);overflow:auto;transition: width 500ms " class="d-flex" > <div class="flex-grow-1 h-100" style="overflow-y: auto"> <div ref="table" style="height : calc(100% - 36px); overflow: auto;width:100%"> <v-skeleton-loader v-if="!dataLoaded && (loadingData || loadingMeta)" type="table"></v-skeleton-loader> <template v-else-if="selectedView && (selectedView.type === 'table' || selectedView.show_as === 'grid' )"> <xc-grid-view ref="ncgridview" :relationType="relationType" :columns-width.sync="columnsWidth" :is-locked="isLocked" :table="table" :availableColumns="availableColumns" :showFields="showFields" :sqlUi="sqlUi" :isEditable="isEditable" :nodes="nodes" :primaryValueColumn="primaryValueColumn" :belongsTo="belongsTo" :hasMany="hasMany" :data="data" :visibleColLength="visibleColLength" :meta="meta" :isVirtual="selectedView.type === 'vtable'" @onNewColCreation="onNewColCreation" @onCellValueChange="onCellValueChange" @insertNewRow="insertNewRow" @showRowContextMenu="showRowContextMenu" @addNewRelationTab="addNewRelationTab" @expandRow="expandRow" @onRelationDelete="loadMeta" ></xc-grid-view> </template> <template v-else-if="selectedView && selectedView.show_as === 'gallery' "> <gallery-view :nodes="nodes" :table="table" :show-fields="showFields" :available-columns="availableColumns" :meta="meta" :data="data" :primaryValueColumn="primaryValueColumn" @expandForm="({rowIndex,rowMeta}) => expandRow(rowIndex,rowMeta)" ></gallery-view> </template> <template v-else-if="selectedView && selectedView.show_as === 'kanban' "> <kanban-view :nodes="nodes" :table="table" :show-fields="showFields" :available-columns="availableColumns" :meta="meta" :data="data" :primaryValueColumn="primaryValueColumn" @expandForm="({rowIndex,rowMeta}) => expandRow(rowIndex,rowMeta)" ></kanban-view> </template> <template v-else-if="selectedView && selectedView.show_as === 'calendar' "> <calendar-view :nodes="nodes" :table="table" :show-fields="showFields" :available-columns="availableColumns" :meta="meta" :data="data" :primaryValueColumn="primaryValueColumn" @expandForm="({rowIndex,rowMeta}) => expandRow(rowIndex,rowMeta)" ></calendar-view> </template> </div> <v-pagination v-if="data" style="max-width: 100%" v-model="page" :length="Math.ceil(count / size)" :total-visible="8" @input="loadTableData" color="primary lighten-2" ></v-pagination> <!-- <div v-else class="d-flex justify-center py-4">--> <!-- <v-alert type="info" dense class="ma-1 flex-shrink-1">Table is empty</v-alert>--> <!-- </div>--> </div> <spreadsheet-nav-drawer :currentApiUrl="currentApiUrl" :toggleDrawer="toggleDrawer" :nodes="nodes" :table="table" :meta="meta" :selectedViewId.sync="selectedViewId" :selectedView.sync="selectedView" :primaryValueColumn="primaryValueColumn" :concatenatedXWhere="concatenatedXWhere" :sort="sort" :filters.sync="filters" :sortList.sync="sortList" :showFields.sync="showFields" @mapFieldsAndShowFields="mapFieldsAndShowFields" @loadTableData="loadTableData" ref="drawer" :load="loadViews" :hide-views="!relation" :showAdvanceOptions.sync="showAdvanceOptions" :fieldsOrder.sync="fieldsOrder" :viewStatus.sync="viewStatus" :columnsWidth.sync="columnsWidth" @showAdditionalFeatOverlay="showAdditionalFeatOverlay($event)" > <v-tooltip bottom> <template v-slot:activator="{on}"> <v-list-item v-on="on" @click="showAdditionalFeatOverlay('webhooks')"> <v-icon x-small class="mr-2">mdi-hook</v-icon> <span class="caption"> Automations</span> </v-list-item> </template> Create Automations or API Webhooks </v-tooltip> <v-tooltip bottom> <template v-slot:activator="{on}"> <v-list-item v-on="on" @click="showAdditionalFeatOverlay('acl')"> <v-icon x-small class="mr-2">mdi-shield-edit-outline</v-icon> <span class="caption"> API ACL</span> </v-list-item> </template> Create / Edit API Webhooks </v-tooltip> <v-list-item v-if="showAdvanceOptions" @click="showAdditionalFeatOverlay('validators')"> <v-icon x-small class="mr-2">mdi-sticker-check-outline</v-icon> <span class="caption"> API Validators</span> </v-list-item> <v-divider v-if="showAdvanceOptions" class="advance-menu-divider"></v-divider> <v-list-item v-if="showAdvanceOptions" @click="showAdditionalFeatOverlay('columns')"> <v-icon x-small class="mr-2">mdi-view-column</v-icon> <span class="caption font-weight-light">SQL Columns</span> </v-list-item> <v-list-item v-if="showAdvanceOptions" @click="showAdditionalFeatOverlay('indexes')"> <v-icon x-small class="mr-2">mdi-blur</v-icon> <span class="caption font-weight-light">SQL Indexes</span> </v-list-item> <v-list-item v-if="showAdvanceOptions" @click="showAdditionalFeatOverlay('triggers')"> <v-icon x-small class="mr-2">mdi-shield-edit-outline</v-icon> <span class="caption font-weight-light">SQL Triggers</span> </v-list-item> </spreadsheet-nav-drawer> </div> <v-menu v-if="rowContextMenu" value="rowContextMenu" :position-x="rowContextMenu.x" :position-y="rowContextMenu.y" absolute offset-y > <v-list dense> <template v-if="isEditable && !isLocked"> <v-list-item v-if="relationType !== 'bt'" @click="insertNewRow(false)" > <span class="caption">Insert New Row</span> </v-list-item> <v-list-item @click="deleteRow"> <span class="caption">Delete Row</span> </v-list-item> <v-list-item @click="deleteSelectedRows"> <span class="caption">Delete Selected Rows</span> </v-list-item> </template> <template v-if="meta.hasMany && meta.hasMany.length"> <v-divider v-if="isEditable && !isLocked"></v-divider> <span class="ml-3 grey--text " style="font-size: 9px">Has Many</span> <v-list-item v-for="(hm,i) in meta.hasMany" @click="addNewRelationTabCtxMenu(hm,'hm')" :key="i"> <span class="caption text-capitalize">{{ hm._tn }}</span> </v-list-item> </template> <template v-if="meta.belongsTo && meta.belongsTo.length"> <v-divider></v-divider> <span class="ml-3 grey--text " style="font-size: 9px">Belongs To</span> <v-list-item v-for="(bt,i) in belongsTo" @click="addNewRelationTabCtxMenu(bt,'bt')" :key="i"> <span class="caption text-capitalize">{{ bt._rtn }}</span> </v-list-item> </template> </v-list> </v-menu> <v-dialog :overlay-opacity="0.8" v-if="data" width="1000px" max-width="100%" class=" mx-auto" v-model="showExpandModal"> <expanded-form :db-alias="nodes.dbAlias" :has-many="hasMany" :belongs-to="belongsTo" v-if="selectedExpandRowIndex != null" @cancel="showExpandModal = false;" @input="showExpandModal = false; (data[selectedExpandRowIndex] && data[selectedExpandRowIndex].rowMeta && delete data[selectedExpandRowIndex].rowMeta.new)" :table="table" v-model="data[selectedExpandRowIndex].row" :oldRow.sync="data[selectedExpandRowIndex].oldRow" :is-new="data[selectedExpandRowIndex].rowMeta.new" :selected-row-meta="selectedExpandRowMeta" :meta="meta" :sql-ui="sqlUi" :primary-value-column="primaryValueColumn" :api="api" @commented="reloadComments" ></expanded-form> </v-dialog> <additional-features :deleteTable="deleteTable" :nodes="nodes" v-model="showAddFeatOverlay" :type="featureType" :table="table" ></additional-features> </v-container> </template> <script> import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory"; import Table from "@/components/project/table"; import {SqlUI} from "@/helpers/SqlUiFactory"; import NewColumn from "@/components/project/spreadsheet/editColumn/editColumn"; import {mapActions} from "vuex"; import AdditionalFeatures from "@/components/project/spreadsheet/overlay/additinalFeatures"; import ColumnFilter from "~/components/project/spreadsheet/components/columnFilterMenu"; import CreateViewDialog from "@/components/project/spreadsheet/dialog/createViewDialog"; import debounce from 'debounce'; import GalleryView from "@/components/project/spreadsheet/views/galleryView"; import CalendarView from "@/components/project/spreadsheet/views/calendarView"; import KanbanView from "@/components/project/spreadsheet/views/kanbanView"; import XcGridView from "@/components/project/spreadsheet/views/xcGridView"; import SortList from "@/components/project/spreadsheet/components/sortListMenu"; import Fields from "@/components/project/spreadsheet/components/fieldsMenu"; import SpreadsheetNavDrawer from "@/components/project/spreadsheet/components/spreadsheetNavDrawer"; import spreadsheet from "@/components/project/spreadsheet/mixins/spreadsheet"; import LockMenu from "@/components/project/spreadsheet/components/lockMenu"; import ExpandedForm from "@/components/project/spreadsheet/components/expandedForm"; export default { mixins: [spreadsheet], name: "rows-xc-data-table", components: { ExpandedForm, LockMenu, SpreadsheetNavDrawer, Fields, SortList, XcGridView, KanbanView, CalendarView, GalleryView, CreateViewDialog, ColumnFilter, AdditionalFeatures, NewColumn, Table, }, props: { env: String, nodes: Object, addNewRelationTab: Function, relationType: String, relation: Object, relationIdValue: [String, Number], refTable: String, relationPrimaryValue: [String, Number], deleteTable: Function, showTabs: [Boolean, Number] }, data: () => ({ dataLoaded: false, searchQueryVal: '', columnsWidth: null, viewStatus: { type: null }, fieldsOrder: [], showSystemFields: false, showAdvanceOptions: false, loadViews: false, selectedView: {}, overShieldIcon: false, progress: false, createViewType: '', addNewColModal: false, showAddFeatOverlay: false, featureType: null, addNewColMenu: false, newColumn: {}, shareLink: null, loadingMeta: true, loadingData: true, toggleDrawer: false, selectedViewId: 0, searchField: null, searchQuery: '', showExpandModal: false, selectedExpandRowIndex: null, selectedExpandRowMeta: null, navDrawer: true, selected: { row: null, col: null }, editEnabled: { row: null, col: null }, page: 1, count: 0, size: 25, xWhere: '', sort: '', cellHeight: 'small', spreadsheet: null, options: { allowToolbar: true, columnSorting: false, }, filteredData: [], cellHeights: [{ size: 'small', icon: 'mdi-view-headline' }, { size: 'medium', icon: 'mdi-view-sequential' }, { size: 'large', icon: 'mdi-view-stream' }, { size: 'xlarge', icon: 'mdi-card' }], rowContextMenu: null }), async mounted() { try { await this.createTableIfNewTable(); await this.loadMeta(); if (this.relationType === 'hm') { this.filters.push({ field: this.meta.columns.find(c => c.cn === this.relation.cn)._cn, op: 'is equal', value: this.relationIdValue, readOnly: true }) } else if (this.relationType === 'bt') { this.filters.push({ // field: this.relation.rcn, field: this.meta.columns.find(c => c.cn === this.relation.rcn)._cn, op: 'is equal', value: this.relationIdValue, readOnly: true }) } else { // await this.$refs.drawer.loadViews(); await this.loadTableData(); } this.mapFieldsAndShowFields(); } catch (e) { console.log(e) } this.searchField = this.primaryValueColumn; this.dataLoaded = true; // await this.loadViews(); }, methods: { ...mapActions({ loadTablesFromChildTreeNode: "project/loadTablesFromChildTreeNode" }), reloadComments() { if (this.$refs.ncgridview) { this.$refs.ncgridview.xcAuditModelCommentsCount(); } }, syncDataDebounce: debounce(async function (self) { await self.syncData() }, 500), async syncData() { if (this.relation) return; try { const query_params = { filters: this.filters, sortList: this.sortList, showFields: this.showFields, fieldsOrder: this.fieldsOrder, viewStatus: this.viewStatus, columnsWidth: this.columnsWidth, }; this.$set(this.selectedView, 'query_params', JSON.stringify(query_params)); if (!this._isUIAllowed('xcVirtualTableUpdate')) return; await this.sqlOp({dbAlias: this.nodes.dbAlias}, 'xcVirtualTableUpdate', { id: this.selectedViewId, query_params }); } catch (e) { // this.$toast.error(e.message).goAway(3000); } }, showAdditionalFeatOverlay(feat) { this.showAddFeatOverlay = true; this.featureType = feat; }, async createTableIfNewTable() { if (this.nodes.newTable) { const columns = this.sqlUi.getNewTableColumns().filter(col => this.nodes.newTable.columns.includes(col.cn)); await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [ { env: this.nodes.env, dbAlias: this.nodes.dbAlias }, "tableCreate", { tn: this.nodes.tn, _tn: this.nodes._tn, columns }]); await this.loadTablesFromChildTreeNode({ _nodes: { ...this.nodes } }); delete this.nodes.newTable; } this.loadViews = true; }, comingSoon() { this.$toast.info('Coming soon!').goAway(3000) }, addNewRelationTabCtxMenu(obj, type) { const rowObj = this.rowContextMenu.row; this.addNewRelationTab( obj, this.table, this.meta._tn || this.table, type === 'hm' ? obj.tn : obj.rtn, type === 'hm' ? obj._tn : obj._rtn, // todo: column name alias rowObj[type === 'hm' ? obj.rcn : obj._cn], type, rowObj, rowObj[this.primaryValueColumn] ) }, changed(col, row) { this.$set(this.data[row].rowMeta, 'changed', this.data[row].rowMeta.changed || {}); if (this.data[row].rowMeta) { this.$set(this.data[row].rowMeta.changed, this.availableColumns[col].cn, true); } }, async save() { for (let row = 0; row < this.rowLength; row++) { const {row: rowObj, rowMeta} = this.data[row]; if (rowMeta.new) { try { const pks =this.availableColumns.filter((col) => { return col.pk; }); if (this.availableColumns.every((col) => { return !col.ai; }) && pks.length && pks.every(col => !rowObj[col._cn])) { return this.$toast.info('Primary column is empty please provide some value').goAway(3000); } const insertObj = this.availableColumns.reduce((o, col) => { if (!col.ai && (rowObj && rowObj[col._cn]) !== null) { o[col._cn] = rowObj && rowObj[col._cn]; } return o; }, {}); const insertedData = await this.api.insert(insertObj); this.data.splice(row, 1, { row: insertedData, rowMeta: {}, oldRow: {...insertedData} }); this.$toast.success(`${insertedData[this.primaryValueColumn] ? `${insertedData[this.primaryValueColumn]}'s r` : 'R'}ow saved successfully.`, { position: 'bottom-center' }).goAway(3000); } catch (e) { if (e.response && e.response.data && e.response.data.msg) { this.$toast.error(e.response.data.msg).goAway(3000) } else { this.$toast.error(`Failed to save row : ${e.message}`).goAway(3000) } } } } }, async onCellValueChange(col, row, column) { if (!this.data[row]) return; const {row: rowObj, rowMeta, oldRow} = this.data[row]; if (rowMeta.new) { } else { try { if (!this.api) return; const id = this.meta.columns.filter((c) => c.pk).map(c => rowObj[c._cn]).join(','); await this.api.update(id, { [column._cn]: rowObj[column._cn] }, {[column._cn]: oldRow[column._cn]}) this.$set(oldRow, column._cn, rowObj[column._cn]) this.$toast.success(`${rowObj[this.primaryValueColumn] ? `${rowObj[this.primaryValueColumn]}'s c` : 'C'}olumn '${column.cn}' updated successfully.`, { position: 'bottom-center' }).goAway(3000); } catch (e) { if (e.response && e.response.data && e.response.data.msg) { this.$toast.error(e.response.data.msg).goAway(3000) } else { this.$toast.error(`Failed to update row : ${e.message}`).goAway(3000) } } } }, async deleteRow() { try { const rowObj = this.rowContextMenu.row; if (!this.rowContextMenu.rowMeta.new) { const id = this.meta && this.meta.columns && this.meta.columns.filter((c) => c.pk).map(c => rowObj[c._cn]).join(','); await this.api.delete(id); } this.data.splice(this.rowContextMenu.index, 1); this.$toast.success(`Deleted row successfully`).goAway(3000) } catch (e) { this.$toast.error(`Failed to delete row : ${e.message}`).goAway(3000) } }, async deleteSelectedRows() { let row = this.rowLength while (row--) { try { const {row: rowObj, rowMeta} = this.data[row]; if (!rowMeta.selected) { continue; } if (!rowMeta.new) { const id = this.meta.columns.filter((c) => c.pk).map(c => rowObj[c._cn]).join(','); await this.api.delete(id); } this.data.splice(row, 1); } catch (e) { return this.$toast.error(`Failed to delete row : ${e.message}`).goAway(3000) } this.$toast.success(`Deleted selected rows successfully`).goAway(3000) } }, async insertNewRow(atEnd = false, expand = false) { const focusRow = atEnd ? this.rowLength : this.rowContextMenu.index + 1; const focusCol = this.availableColumns.findIndex(c => !c.ai); this.data.splice(focusRow, 0, { row: this.relationType === 'hm' ? { ...this.fieldList.reduce((o, f) => ({...o, [f]: null}), {}), [this.relation.cn]: this.relationIdValue } : this.fieldList.reduce((o, f) => ({...o, [f]: null}), {}), rowMeta: { new: true }, oldRow: {} }); this.selected = {row: focusRow, col: focusCol} this.editEnabled = {row: focusRow, col: focusCol} if (expand) { const {rowMeta} = this.data[this.data.length - 1]; this.expandRow(this.data.length - 1, rowMeta) } }, async handleKeyDown({metaKey, key, altKey, shiftKey, ctrlKey}) { switch ([ this._isMac ? metaKey : ctrlKey , key].join('_')) { case 'true_s' : this.edited && await this.save(); break; case 'true_l' : await this.loadTableData(); break; case 'true_n' : this.insertNewRow(true); break; } }, async loadMeta() { this.loadingMeta = true; const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ env: this.nodes.env, dbAlias: this.nodes.dbAlias }, 'tableXcModelGet', { tn: this.table }]); this.meta = JSON.parse(tableMeta.meta); this.loadingMeta = false; }, loadTableDataDeb: debounce(async function (self) { await self.loadTableDataFn() }, 100), loadTableData() { this.loadTableDataDeb(this) }, async loadTableDataFn() { this.loadingData = true; try { if (this.api) { const {list, count} = await this.api.paginatedList(this.queryParams); this.count = count; this.data = list.map(row => ({ row, oldRow: {...row}, rowMeta: {} })); } } catch (e) { console.log(e); } this.loadingData = false; }, showRowContextMenu(e, row, rowMeta, index) { e.preventDefault(); this.rowContextMenu = false; this.$nextTick(() => { this.rowContextMenu = { x: e.clientX, y: e.clientY, row, index, rowMeta } }); }, expandRow(row, rowMeta) { this.showExpandModal = true; this.selectedExpandRowIndex = row; this.selectedExpandRowMeta = rowMeta; }, async onNewColCreation() { await this.loadMeta(); this.$nextTick(async () => { await this.loadTableData(); this.mapFieldsAndShowFields(); }); } }, computed: { currentApiUrl() { return this.api && `${this.api.apiUrl}?` + Object.entries(this.queryParams).filter(p => p[1]).map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&') }, isEditable() { return this._isUIAllowed('xcDatatableEditable') }, sqlUi() { return SqlUI.create(this.nodes.dbConnection); }, api() { return this.meta && this.meta._tn ? ApiFactory.create(this.$store.getters['project/GtrProjectType'], this.meta && this.meta._tn, this.meta && this.meta.columns, this) : null; } }, } </script> <style scoped> /deep/ .v-input__control .v-input__slot .v-input--selection-controls__input { transform: scale(.85); margin-right: 0; } /deep/ .xc-toolbar .v-input__slot, .navigation .v-input__slot { box-shadow: none !important; } /deep/ .navigation .v-input__slot input::placeholder { font-size: .8rem; } /deep/ .v-btn { text-transform: capitalize; } .row-expand-icon, .row-checkbox { opacity: 0; } /deep/ .row-checkbox .v-input__control { height: 24px !important; } .cell-height-medium td, .cell-height-medium tr { min-height: 35px !important; /*height: 35px !important;*/ /*max-height: 35px !important;*/ } .cell-height-large td, .cell-height-large tr { min-height: 40px !important; /*height: 40px !important;*/ /*max-height: 40px !important;*/ } .cell-height-xlarge td, .cell-height-xlarge tr { min-height: 50px !important; /*height: 50px !important;*/ /*max-height: 50px !important;*/ } /deep/ .xc-border.search-box { overflow: visible; border-radius: 4px; } /deep/ .xc-border.search-box .v-input { transition: .4s border-color; } /deep/ .xc-border.search-box .v-input--is-focused { border: 1px solid var(--v-primary-base) !important; margin: -1px; } /deep/ .search-field.v-text-field.v-text-field--solo.v-input--dense > .v-input__control { min-height: auto; } .views-navigation-drawer { transition: .4s max-width, .4s min-width; } .new-column-header { text-align: center; min-width: 70px; } /deep/ .v-input__control label { font-size: inherit; } </style> <!-- /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR <oof1lab@gmail.com> * @author Pranav C Balan <pranavxc@gmail.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ -->