1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00
This commit is contained in:
lana-k
2025-03-30 15:57:47 +02:00
parent 6f7961e1b4
commit df16383d49
64 changed files with 316 additions and 279 deletions

View File

@@ -13,7 +13,8 @@ module.exports = {
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'vue/no-mutating-props': 'warn', 'vue/no-mutating-props': 'warn',
'vue/no-reserved-component-names': 'warn', 'vue/no-reserved-component-names': 'warn',
'vue/no-v-model-argument': 'off' 'vue/no-v-model-argument': 'off',
'vue/require-default-prop': 'off'
}, },
overrides: [ overrides: [
{ {

View File

@@ -11,9 +11,6 @@ import { ModalsContainer } from 'vue-final-modal'
export default { export default {
components: { ModalsContainer }, components: { ModalsContainer },
created() {
this.$store.commit('setInquiries', storedInquiries.getStoredInquiries())
},
computed: { computed: {
inquiries() { inquiries() {
return this.$store.state.inquiries return this.$store.state.inquiries
@@ -26,6 +23,9 @@ export default {
storedInquiries.updateStorage(this.inquiries) storedInquiries.updateStorage(this.inquiries)
} }
} }
},
created() {
this.$store.commit('setInquiries', storedInquiries.getStoredInquiries())
} }
} }
</script> </script>

View File

@@ -8,18 +8,18 @@
> >
<div class="value"> <div class="value">
<input <input
:class="{ filled: filled }"
ref="delimiterInput" ref="delimiterInput"
v-model="inputValue"
:class="{ filled: filled }"
type="text" type="text"
maxlength="1" maxlength="1"
v-model="inputValue"
@click.stop
:disabled="disabled" :disabled="disabled"
@click.stop
/> />
<div class="name">{{ getSymbolName(modelValue) }}</div> <div class="name">{{ getSymbolName(modelValue) }}</div>
</div> </div>
<div class="controls" @click.stop> <div class="controls" @click.stop>
<clear-icon @click="clear" :disabled="disabled" /> <clear-icon :disabled="disabled" @click="clear" />
<drop-down-chevron <drop-down-chevron
:disabled="disabled" :disabled="disabled"
@click="!disabled && (showOptions = !showOptions)" @click="!disabled && (showOptions = !showOptions)"
@@ -30,8 +30,8 @@
<div <div
v-for="(option, index) in options" v-for="(option, index) in options"
:key="index" :key="index"
@click="chooseOption(option)"
class="option" class="option"
@click="chooseOption(option)"
> >
<pre>{{ option }}</pre> <pre>{{ option }}</pre>
<div>{{ getSymbolName(option) }}</div> <div>{{ getSymbolName(option) }}</div>
@@ -47,9 +47,13 @@ import ClearIcon from '@/components/svg/clear'
export default { export default {
name: 'DelimiterSelector', name: 'DelimiterSelector',
props: ['modelValue', 'width', 'disabled'],
emits: ['update:modelValue'],
components: { DropDownChevron, ClearIcon }, components: { DropDownChevron, ClearIcon },
props: {
modelValue: String,
width: String,
disabled: Boolean
},
emits: ['update:modelValue'],
data() { data() {
return { return {
showOptions: false, showOptions: false,
@@ -84,7 +88,7 @@ export default {
this.inputValue = option this.inputValue = option
this.showOptions = false this.showOptions = false
}, },
onContainerClick(event) { onContainerClick() {
this.$refs.delimiterInput.focus() this.$refs.delimiterInput.focus()
}, },

View File

@@ -4,20 +4,20 @@
class="dialog" class="dialog"
content-class="import-modal" content-class="import-modal"
scrollable scrollable
:clickToClose="false" :click-to-close="false"
> >
<div class="dialog-header"> <div class="dialog-header">
{{ typeName }} import {{ typeName }} import
<close-icon @click="cancelImport" :disabled="disableDialog" /> <close-icon :disabled="disableDialog" @click="cancelImport" />
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<text-field <text-field
label="Table name" id="csv-json-table-name"
v-model="tableName" v-model="tableName"
label="Table name"
width="484px" width="484px"
:disabled="disableDialog" :disabled="disableDialog"
:error-msg="tableNameError" :error-msg="tableNameError"
id="csv-json-table-name"
/> />
<div v-if="!isJson && !isNdJson" class="chars"> <div v-if="!isJson && !isNdJson" class="chars">
<delimiter-selector <delimiter-selector
@@ -28,27 +28,27 @@
@input="preview" @input="preview"
/> />
<text-field <text-field
id="quote-char"
v-model="quoteChar"
label="Quote char" label="Quote char"
hint="The character used to quote fields." hint="The character used to quote fields."
v-model="quoteChar"
width="93px" width="93px"
:disabled="disableDialog" :disabled="disableDialog"
class="char-input" class="char-input"
id="quote-char"
@input="preview" @input="preview"
/> />
<text-field <text-field
id="escape-char"
v-model="escapeChar"
label="Escape char" label="Escape char"
hint=' hint='
The character used to escape the quote character within a field The character used to escape the quote character within a field
(e.g. "column with ""quotes"" in text"). (e.g. "column with ""quotes"" in text").
' '
max-hint-width="242px" max-hint-width="242px"
v-model="escapeChar"
width="93px" width="93px"
:disabled="disableDialog" :disabled="disableDialog"
class="char-input" class="char-input"
id="escape-char"
@input="preview" @input="preview"
/> />
</div> </div>
@@ -70,28 +70,28 @@
</div> </div>
<div class="dialog-buttons-container"> <div class="dialog-buttons-container">
<button <button
id="import-cancel"
class="secondary" class="secondary"
:disabled="disableDialog" :disabled="disableDialog"
@click="cancelImport" @click="cancelImport"
id="import-cancel"
> >
Cancel Cancel
</button> </button>
<button <button
v-show="!importCompleted" v-show="!importCompleted"
id="import-start"
class="primary" class="primary"
:disabled="disableDialog || disableImport" :disabled="disableDialog || disableImport"
@click="loadToDb(file)" @click="loadToDb(file)"
id="import-start"
> >
Import Import
</button> </button>
<button <button
v-show="importCompleted" v-show="importCompleted"
id="import-finish"
class="primary" class="primary"
:disabled="disableDialog" :disabled="disableDialog"
@click="finish" @click="finish"
id="import-finish"
> >
Finish Finish
</button> </button>

View File

@@ -63,6 +63,10 @@ import events from '@/lib/utils/events'
export default { export default {
name: 'DbUploader', name: 'DbUploader',
components: {
ChangeDbIcon,
CsvJsonImport
},
props: { props: {
type: { type: {
type: String, type: String,
@@ -79,10 +83,6 @@ export default {
} }
}, },
emits: [], emits: [],
components: {
ChangeDbIcon,
CsvJsonImport
},
data() { data() {
return { return {
state: '', state: '',

View File

@@ -12,9 +12,9 @@
</div> </div>
<span <span
v-if="tooltip" v-if="tooltip"
ref="tooltip"
class="icon-tooltip" class="icon-tooltip"
:style="tooltipStyle" :style="tooltipStyle"
ref="tooltip"
> >
{{ tooltip }} {{ tooltip }}
</span> </span>
@@ -27,10 +27,16 @@ import LoadingIndicator from '@/components/LoadingIndicator'
export default { export default {
name: 'SideBarButton', name: 'SideBarButton',
props: ['active', 'disabled', 'tooltip', 'tooltipPosition', 'loading'],
emits: ['click'],
components: { LoadingIndicator }, components: { LoadingIndicator },
mixins: [tooltipMixin], mixins: [tooltipMixin],
props: {
active: Boolean,
disabled: Boolean,
tooltip: String,
tooltipPosition: String,
loading: Boolean
},
emits: ['click'],
methods: { methods: {
onClick() { onClick() {
this.hideTooltip() this.hideTooltip()

View File

@@ -2,13 +2,13 @@
<modal <modal
:modal-id="name" :modal-id="name"
class="dialog" class="dialog"
:clickToClose="false" :click-to-close="false"
:contentTransition="{ name: 'loading-dialog' }" :content-transition="{ name: 'loading-dialog' }"
:overlayTransition="{ name: 'loading-dialog' }" :overlay-transition="{ name: 'loading-dialog' }"
> >
<div class="dialog-header"> <div class="dialog-header">
{{ title }} {{ title }}
<close-icon @click="$emit('cancel')" :disabled="loading" /> <close-icon :disabled="loading" @click="$emit('cancel')" />
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div v-if="loading" class="loading-dialog-body"> <div v-if="loading" class="loading-dialog-body">
@@ -49,7 +49,8 @@ import LoadingIndicator from '@/components/LoadingIndicator'
import CloseIcon from '@/components/svg/close' import CloseIcon from '@/components/svg/close'
export default { export default {
name: 'loadingDialog', name: 'LoadingDialog',
components: { LoadingIndicator, CloseIcon },
props: { props: {
loadingMsg: String, loadingMsg: String,
successMsg: String, successMsg: String,
@@ -66,7 +67,6 @@ export default {
} }
} }
}, },
components: { LoadingIndicator, CloseIcon },
methods: { methods: {
cancel() { cancel() {
this.$emit('cancel') this.$emit('cancel')

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="logs-container" ref="logsContainer"> <div ref="logsContainer" class="logs-container">
<div v-for="(msg, index) in messages" :key="index" class="msg"> <div v-for="(msg, index) in messages" :key="index" class="msg">
<img v-if="msg.type === 'error'" src="~@/assets/images/error.svg" /> <img v-if="msg.type === 'error'" src="~@/assets/images/error.svg" />
<img <img
@@ -21,10 +21,10 @@
import LoadingIndicator from '@/components/LoadingIndicator' import LoadingIndicator from '@/components/LoadingIndicator'
export default { export default {
name: 'logs', name: 'Logs',
props: ['messages'],
emits: [],
components: { LoadingIndicator }, components: { LoadingIndicator },
props: { messages: Array },
emits: [],
watch: { watch: {
'messages.length': 'scrollToBottom' 'messages.length': 'scrollToBottom'
}, },

View File

@@ -8,13 +8,13 @@
]" ]"
> >
<div <div
class="movable-splitter"
ref="movableSplitter" ref="movableSplitter"
class="movable-splitter"
:style="movableSplitterStyle" :style="movableSplitterStyle"
/> />
<div <div
class="splitpanes-pane"
ref="left" ref="left"
class="splitpanes-pane"
:size="paneBefore.size" :size="paneBefore.size"
max-size="30" max-size="30"
:style="styles.before" :style="styles.before"
@@ -64,7 +64,7 @@
</div> </div>
</div> </div>
<!-- splitter end --> <!-- splitter end -->
<div class="splitpanes-pane" ref="right" :style="styles.after"> <div ref="right" class="splitpanes-pane" :style="styles.after">
<slot name="right-pane" /> <slot name="right-pane" />
</div> </div>
</div> </div>
@@ -158,6 +158,9 @@ export default {
} }
} }
}, },
mounted() {
this.container = this.$refs.container
},
methods: { methods: {
bindEvents() { bindEvents() {
@@ -242,9 +245,6 @@ export default {
this.paneAfter.size = this.beforeMinimising.after this.paneAfter.size = this.beforeMinimising.after
} }
} }
},
mounted() {
this.container = this.$refs.container
} }
} }
</script> </script>

View File

@@ -1,5 +1,6 @@
<template> <template>
<paginate <paginate
v-model="page"
:page-count="pageCount" :page-count="pageCount"
:page-range="5" :page-range="5"
:margin-pages="1" :margin-pages="1"
@@ -13,7 +14,6 @@
next-link-class="paginator-next" next-link-class="paginator-next"
prev-link-class="paginator-prev" prev-link-class="paginator-prev"
disabled-class="paginator-disabled" disabled-class="paginator-disabled"
v-model="page"
/> />
</template> </template>
@@ -23,7 +23,10 @@ import Paginate from 'vuejs-paginate-next'
export default { export default {
name: 'Pager', name: 'Pager',
components: { Paginate }, components: { Paginate },
props: ['pageCount', 'modelValue'], props: {
pageCount: Number,
modelValue: Number
},
emits: ['update:modelValue'], emits: ['update:modelValue'],
data() { data() {
return { return {

View File

@@ -1,13 +1,13 @@
<template> <template>
<div> <div>
<div class="rounded-bg"> <div class="rounded-bg">
<div class="header-container" ref="header-container"> <div ref="header-container" class="header-container">
<div> <div>
<div <div
v-for="(th, index) in header" v-for="(th, index) in header"
:key="index"
class="fixed-header" class="fixed-header"
:style="{ width: `${th.width}px` }" :style="{ width: `${th.width}px` }"
:key="index"
:title="th.name" :title="th.name"
> >
{{ th.name }} {{ th.name }}
@@ -15,8 +15,8 @@
</div> </div>
</div> </div>
<div <div
class="table-container"
ref="table-container" ref="table-container"
class="table-container"
@scroll="onScrollTable" @scroll="onScrollTable"
> >
<table <table
@@ -36,11 +36,11 @@
<tr v-for="rowIndex in currentPageData.count" :key="rowIndex"> <tr v-for="rowIndex in currentPageData.count" :key="rowIndex">
<td <td
v-for="(col, colIndex) in columns" v-for="(col, colIndex) in columns"
:key="colIndex"
:data-col="colIndex" :data-col="colIndex"
:data-row="pageSize * (currentPage - 1) + rowIndex - 1" :data-row="pageSize * (currentPage - 1) + rowIndex - 1"
:data-isNull="isNull(getCellValue(col, rowIndex))" :data-isNull="isNull(getCellValue(col, rowIndex))"
:data-isBlob="isBlob(getCellValue(col, rowIndex))" :data-isBlob="isBlob(getCellValue(col, rowIndex))"
:key="colIndex"
:aria-selected="false" :aria-selected="false"
@click="onCellClick" @click="onCellClick"
> >
@@ -61,8 +61,8 @@
</div> </div>
<pager <pager
v-show="pageCount > 1" v-show="pageCount > 1"
:page-count="pageCount"
v-model="currentPage" v-model="currentPage"
:page-count="pageCount"
/> />
</div> </div>
</div> </div>
@@ -125,6 +125,15 @@ export default {
} }
} }
}, },
watch: {
currentPageData() {
this.calculateHeadersWidth()
this.selectCell(null)
},
dataSet() {
this.currentPage = 1
}
},
mounted() { mounted() {
this.resizeObserver = new ResizeObserver(this.calculateHeadersWidth) this.resizeObserver = new ResizeObserver(this.calculateHeadersWidth)
this.resizeObserver.observe(this.$refs.table) this.resizeObserver.observe(this.$refs.table)
@@ -140,6 +149,9 @@ export default {
} }
} }
}, },
beforeUnmount() {
this.resizeObserver.unobserve(this.$refs.table)
},
methods: { methods: {
isBlob(value) { isBlob(value) {
return value && ArrayBuffer.isView(value) return value && ArrayBuffer.isView(value)
@@ -252,18 +264,6 @@ export default {
this.selectCell(newCell) this.selectCell(newCell)
} }
} }
},
beforeUnmount() {
this.resizeObserver.unobserve(this.$refs.table)
},
watch: {
currentPageData() {
this.calculateHeadersWidth()
this.selectCell(null)
},
dataSet() {
this.currentPage = 1
}
} }
} }
</script> </script>

View File

@@ -6,8 +6,8 @@
> >
{{ label }} {{ label }}
<hint-icon <hint-icon
class="hint"
v-if="hint" v-if="hint"
class="hint"
:hint="hint" :hint="hint"
:max-width="maxHintWidth || '149px'" :max-width="maxHintWidth || '149px'"
/> />
@@ -28,18 +28,19 @@
<script> <script>
import HintIcon from '@/components/svg/hint' import HintIcon from '@/components/svg/hint'
export default { export default {
name: 'textField', name: 'TextField',
props: [ components: { HintIcon },
'placeholder', props: {
'label', placeholder: String,
'errorMsg', label: String,
'modelValue', errorMsg: String,
'width', modelValue: String,
'hint', width: String,
'maxHintWidth', hint: String,
'disabled' maxHintWidth: String,
], disabled: Boolean
components: { HintIcon } },
emits: ['update:modelValue']
} }
</script> </script>

View File

@@ -33,7 +33,7 @@
</clipPath> </clipPath>
</defs> </defs>
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
Add new table from CSV, JSON or NDJSON Add new table from CSV, JSON or NDJSON
</span> </span>
</span> </span>
@@ -45,7 +45,7 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'AddTableIcon', name: 'AddTableIcon',
mixins: [tooltipMixin], mixins: [tooltipMixin],
props: ['tooltip'], props: { tooltip: String },
emits: ['click'], emits: ['click'],
methods: { methods: {
onClick() { onClick() {

View File

@@ -21,7 +21,7 @@
fill="#A2B1C6" fill="#A2B1C6"
/> />
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
Load another database, CSV, JSON or NDJSON Load another database, CSV, JSON or NDJSON
</span> </span>
</div> </div>
@@ -31,7 +31,7 @@
import tooltipMixin from '@/tooltipMixin' import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'changeDbIcon', name: 'ChangeDbIcon',
mixins: [tooltipMixin], mixins: [tooltipMixin],
emits: ['click'], emits: ['click'],
methods: { methods: {

View File

@@ -23,7 +23,7 @@
<script> <script>
export default { export default {
name: 'ClearIcon', name: 'ClearIcon',
props: ['disabled'] props: { disabled: Boolean }
} }
</script> </script>

View File

@@ -1,12 +1,12 @@
<template> <template>
<svg <svg
@click.stop="$emit('click')"
:class="['icon', { disabled: disabled }]" :class="['icon', { disabled: disabled }]"
:width="size" :width="size"
:height="size" :height="size"
viewBox="0 0 14 14" viewBox="0 0 14 14"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@click.stop="$emit('click')"
> >
<path <path
d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14

View File

@@ -17,7 +17,7 @@
<script> <script>
export default { export default {
name: 'DropDownChevron', name: 'DropDownChevron',
props: ['disabled'] props: { disabled: Boolean }
} }
</script> </script>

View File

@@ -17,7 +17,7 @@
fill="#A2B1C6" fill="#A2B1C6"
/> />
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
{{ tooltip }} {{ tooltip }}
</span> </span>
</span> </span>
@@ -29,7 +29,10 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'ExportIcon', name: 'ExportIcon',
mixins: [tooltipMixin], mixins: [tooltipMixin],
props: ['tooltip', 'tooltipPosition'], props: {
tooltip: String,
tooltipPosition: String
},
emits: ['click'], emits: ['click'],
methods: { methods: {
onClick() { onClick() {

View File

@@ -34,9 +34,9 @@
/> />
</svg> </svg>
<span <span
ref="tooltip"
class="icon-tooltip" class="icon-tooltip"
:style="{ ...tooltipStyle, maxWidth: maxWidth }" :style="{ ...tooltipStyle, maxWidth: maxWidth }"
ref="tooltip"
> >
{{ hint }} {{ hint }}
</span> </span>
@@ -48,9 +48,12 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'HintIcon', name: 'HintIcon',
props: ['hint', 'maxWidth'],
emits: ['click'],
mixins: [tooltipMixin], mixins: [tooltipMixin],
props: {
hint: String,
maxWidth: String
},
emits: ['click'],
methods: { methods: {
onClick() { onClick() {
this.hideTooltip() this.hideTooltip()

View File

@@ -18,7 +18,7 @@
<script> <script>
export default { export default {
name: 'treeChevron', name: 'TreeChevron',
props: { props: {
expanded: { expanded: {
type: Boolean, type: Boolean,

View File

@@ -1,8 +1,8 @@
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import Workspace from '@/views/Main/Workspace' import Workspace from '@/views/MainView/Workspace'
import Inquiries from '@/views/Main/Inquiries' import Inquiries from '@/views/MainView/Inquiries'
import Welcome from '@/views/Welcome' import Welcome from '@/views/Welcome'
import Main from '@/views/Main' import MainView from '@/views/MainView'
import LoadView from '@/views/LoadView' import LoadView from '@/views/LoadView'
import store from '@/store' import store from '@/store'
import database from '@/lib/database' import database from '@/lib/database'
@@ -15,8 +15,8 @@ const routes = [
}, },
{ {
path: '/', path: '/',
name: 'Main', name: 'MainView',
component: Main, component: MainView,
children: [ children: [
{ {
path: '/workspace', path: '/workspace',

View File

@@ -15,7 +15,7 @@
{{ item.name }} {{ item.name }}
<div class="divider" /> <div class="divider" />
<div class="options"> <div class="options">
<div v-for="(opt, index) in item.info" :key="index"> <div v-for="(opt, optIndex) in item.info" :key="optIndex">
{{ opt }} {{ opt }}
</div> </div>
</div> </div>

View File

@@ -1,22 +1,22 @@
<template> <template>
<div id="my-inquiries-container"> <div id="my-inquiries-container">
<div id="start-guide" v-if="allInquiries.length === 0"> <div v-if="allInquiries.length === 0" id="start-guide">
You don't have saved inquiries so far. You don't have saved inquiries so far.
<span class="link" @click="emitCreateTabEvent">Create</span> <span class="link" @click="emitCreateTabEvent">Create</span>
the one from scratch or the one from scratch or
<span @click="importInquiries" class="link">import</span> from a file. <span class="link" @click="importInquiries">import</span> from a file.
</div> </div>
<div <div
id="loading-predefined-status"
v-if="$store.state.loadingPredefinedInquiries" v-if="$store.state.loadingPredefinedInquiries"
id="loading-predefined-status"
> >
<loading-indicator /> <loading-indicator />
Loading predefined inquiries... Loading predefined inquiries...
</div> </div>
<div <div
v-show="allInquiries.length > 0"
id="my-inquiries-content" id="my-inquiries-content"
ref="my-inquiries-content" ref="my-inquiries-content"
v-show="allInquiries.length > 0"
> >
<div id="my-inquiries-toolbar"> <div id="my-inquiries-toolbar">
<div id="toolbar-buttons"> <div id="toolbar-buttons">
@@ -28,17 +28,17 @@
Import Import
</button> </button>
<button <button
v-show="selectedInquiriesCount > 0"
id="toolbar-btns-export" id="toolbar-btns-export"
class="toolbar" class="toolbar"
v-show="selectedInquiriesCount > 0"
@click="exportSelectedInquiries()" @click="exportSelectedInquiries()"
> >
Export Export
</button> </button>
<button <button
v-show="selectedNotPredefinedCount > 0"
id="toolbar-btns-delete" id="toolbar-btns-delete"
class="toolbar" class="toolbar"
v-show="selectedNotPredefinedCount > 0"
@click="showDeleteDialog(selectedInquiriesIds)" @click="showDeleteDialog(selectedInquiriesIds)"
> >
Delete Delete
@@ -46,9 +46,9 @@
</div> </div>
<div id="toolbar-search"> <div id="toolbar-search">
<text-field <text-field
v-model="filter"
placeholder="Search inquiry by name" placeholder="Search inquiry by name"
width="300px" width="300px"
v-model="filter"
/> />
</div> </div>
</div> </div>
@@ -60,7 +60,7 @@
<div v-show="showedInquiries.length > 0" class="rounded-bg"> <div v-show="showedInquiries.length > 0" class="rounded-bg">
<div class="header-container"> <div class="header-container">
<div> <div>
<div class="fixed-header" ref="name-th"> <div ref="name-th" class="fixed-header">
<check-box <check-box
ref="mainCheckBox" ref="mainCheckBox"
theme="light" theme="light"
@@ -99,9 +99,9 @@
> >
Predefined Predefined
<span <span
ref="tooltip"
class="icon-tooltip" class="icon-tooltip"
:style="tooltipStyle" :style="tooltipStyle"
ref="tooltip"
> >
Predefined inquiries come from the server. These Predefined inquiries come from the server. These
inquiries cant be deleted or renamed. inquiries cant be deleted or renamed.
@@ -121,9 +121,9 @@
/> />
<copy-icon @click="duplicateInquiry(index)" /> <copy-icon @click="duplicateInquiry(index)" />
<export-icon <export-icon
@click="exportToFile([inquiry], `${inquiry.name}.json`)"
tooltip="Export inquiry to file" tooltip="Export inquiry to file"
tooltip-position="top-left" tooltip-position="top-left"
@click="exportToFile([inquiry], `${inquiry.name}.json`)"
/> />
<delete-icon <delete-icon
v-if="!inquiry.isPredefined" v-if="!inquiry.isPredefined"
@@ -147,9 +147,9 @@
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<text-field <text-field
v-model="newName"
label="New inquiry name" label="New inquiry name"
:error-msg="errorMsg" :error-msg="errorMsg"
v-model="newName"
width="100%" width="100%"
/> />
</div> </div>

View File

@@ -20,7 +20,7 @@
fill="#A2B1C6" fill="#A2B1C6"
/> />
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
Duplicate inquiry Duplicate inquiry
</span> </span>
</span> </span>
@@ -31,8 +31,8 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'CopyIcon', name: 'CopyIcon',
emits: ['click'],
mixins: [tooltipMixin], mixins: [tooltipMixin],
emits: ['click'],
methods: { methods: {
onClick() { onClick() {
this.hideTooltip() this.hideTooltip()

View File

@@ -18,7 +18,7 @@
fill="#A2B1C6" fill="#A2B1C6"
/> />
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
Delete inquiry Delete inquiry
</span> </span>
</span> </span>
@@ -29,8 +29,8 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'DeleteIcon', name: 'DeleteIcon',
emits: ['click'],
mixins: [tooltipMixin], mixins: [tooltipMixin],
emits: ['click'],
methods: { methods: {
onClick() { onClick() {
this.hideTooltip() this.hideTooltip()

View File

@@ -18,7 +18,7 @@
fill="#A2B1C6" fill="#A2B1C6"
/> />
</svg> </svg>
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip"> <span ref="tooltip" class="icon-tooltip" :style="tooltipStyle">
Rename inquiry Rename inquiry
</span> </span>
</span> </span>
@@ -29,8 +29,8 @@ import tooltipMixin from '@/tooltipMixin'
export default { export default {
name: 'RenameIcon', name: 'RenameIcon',
emits: ['click'],
mixins: [tooltipMixin], mixins: [tooltipMixin],
emits: ['click'],
methods: { methods: {
onClick() { onClick() {
this.hideTooltip() this.hideTooltip()

View File

@@ -10,8 +10,8 @@
</div> </div>
<div id="nav-buttons"> <div id="nav-buttons">
<button <button
id="save-btn"
v-show="currentInquiry && $route.path === '/workspace'" v-show="currentInquiry && $route.path === '/workspace'"
id="save-btn"
class="primary" class="primary"
:disabled="isSaved" :disabled="isSaved"
@click="checkInquiryBeforeSave" @click="checkInquiryBeforeSave"
@@ -37,9 +37,9 @@
modifications will be saved as a new inquiry. Enter the name for it. modifications will be saved as a new inquiry. Enter the name for it.
</div> </div>
<text-field <text-field
v-model="name"
label="Inquiry name" label="Inquiry name"
:error-msg="errorMsg" :error-msg="errorMsg"
v-model="name"
width="100%" width="100%"
/> />
</div> </div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div @click="colVisible = !colVisible" class="table-name"> <div class="table-name" @click="colVisible = !colVisible">
<tree-chevron :expanded="colVisible" /> <tree-chevron :expanded="colVisible" />
{{ name }} {{ name }}
</div> </div>
@@ -19,7 +19,10 @@ import TreeChevron from '@/components/svg/treeChevron'
export default { export default {
name: 'TableDescription', name: 'TableDescription',
components: { TreeChevron }, components: { TreeChevron },
props: ['name', 'columns'], props: {
name: String,
columns: Array
},
data() { data() {
return { return {
colVisible: false colVisible: false

View File

@@ -1,10 +1,10 @@
<template> <template>
<div id="schema-container"> <div id="schema-container">
<div id="schema-filter"> <div id="schema-filter">
<text-field placeholder="Search table" width="100%" v-model="filter" /> <text-field v-model="filter" placeholder="Search table" width="100%" />
</div> </div>
<div id="db"> <div id="db">
<div @click="schemaVisible = !schemaVisible" class="db-name"> <div class="db-name" @click="schemaVisible = !schemaVisible">
<tree-chevron v-show="schema.length > 0" :expanded="schemaVisible" /> <tree-chevron v-show="schema.length > 0" :expanded="schemaVisible" />
{{ dbName }} {{ dbName }}
</div> </div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="chart-container" ref="chartContainer"> <div ref="chartContainer" class="chart-container">
<div class="warning chart-warning" v-show="!dataSources && visible"> <div v-show="!dataSources && visible" class="warning chart-warning">
There is no data to build a chart. Run your SQL query and make sure the There is no data to build a chart. Run your SQL query and make sure the
result is not empty. result is not empty.
</div> </div>
@@ -10,17 +10,17 @@
> >
<PlotlyEditor <PlotlyEditor
v-show="visible" v-show="visible"
ref="plotlyEditor"
:data="state.data" :data="state.data"
:layout="state.layout" :layout="state.layout"
:frames="state.frames" :frames="state.frames"
:config="config" :config="config"
:dataSources="dataSources" :data-sources="dataSources"
:dataSourceOptions="dataSourceOptions" :data-source-options="dataSourceOptions"
:plotly="plotly" :plotly="plotly"
:useResizeHandler="useResizeHandler" :use-resize-handler="useResizeHandler"
:debug="true" :debug="true"
:advancedTraceTypeSelector="true" :advanced-trace-type-selector="true"
ref="plotlyEditor"
@update="update" @update="update"
@render="onRender" @render="onRender"
/> />
@@ -40,17 +40,17 @@ import events from '@/lib/utils/events'
export default { export default {
name: 'Chart', name: 'Chart',
props: [
'dataSources',
'initOptions',
'importToPngEnabled',
'importToSvgEnabled',
'forPivot'
],
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
components: { components: {
PlotlyEditor: applyPureReactInVue(ReactPlotlyEditorWithPlotRef) PlotlyEditor: applyPureReactInVue(ReactPlotlyEditorWithPlotRef)
}, },
props: {
dataSources: Object,
initOptions: Object,
importToPngEnabled: Boolean,
importToSvgEnabled: Boolean,
forPivot: Boolean
},
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
data() { data() {
return { return {
plotly, plotly,
@@ -74,6 +74,16 @@ export default {
return chartHelper.getOptionsFromDataSources(this.dataSources) return chartHelper.getOptionsFromDataSources(this.dataSources)
} }
}, },
watch: {
dataSources() {
// we need to update state.data in order to update the graph
// https://github.com/plotly/react-chart-editor/issues/948
if (this.dataSources) {
dereference.default(this.state.data, this.dataSources)
this.updatePlotly()
}
}
},
created() { created() {
// https://github.com/plotly/plotly.js/issues/4555 // https://github.com/plotly/plotly.js/issues/4555
plotly.setPlotConfig({ plotly.setPlotConfig({
@@ -113,21 +123,11 @@ export default {
beforeUnmount() { beforeUnmount() {
this.resizeObserver.unobserve(this.$refs.chartContainer) this.resizeObserver.unobserve(this.$refs.chartContainer)
}, },
watch: {
dataSources() {
// we need to update state.data in order to update the graph
// https://github.com/plotly/react-chart-editor/issues/948
if (this.dataSources) {
dereference.default(this.state.data, this.dataSources)
this.updatePlotly()
}
}
},
methods: { methods: {
async handleResize() { async handleResize() {
this.updatePlotly() this.updatePlotly()
}, },
onRender(data, layout, frames) { onRender() {
// TODO: check changes and enable Save button if needed // TODO: check changes and enable Save button if needed
}, },
update(data, layout, frames) { update(data, layout, frames) {

View File

@@ -14,11 +14,14 @@ import SortIcon from '@/components/svg/sort'
export default { export default {
name: 'PivotSortBtn', name: 'PivotSortBtn',
props: ['direction', 'modelValue'],
emits: ['update:modelValue'],
components: { components: {
SortIcon SortIcon
}, },
props: {
direction: String,
modelValue: String
},
emits: ['update:modelValue'],
methods: { methods: {
changeSorting() { changeSorting() {
if (this.modelValue === 'key_a_to_z') { if (this.modelValue === 'key_a_to_z') {

View File

@@ -4,12 +4,12 @@
<div class="row"> <div class="row">
<label>Columns</label> <label>Columns</label>
<multiselect <multiselect
class="sqliteviz-select cols"
v-model="cols" v-model="cols"
class="sqliteviz-select cols"
:options="colsToSelect" :options="colsToSelect"
:disabled="colsToSelect.length === 0" :disabled="colsToSelect.length === 0"
:multiple="true" :multiple="true"
:hideSelected="true" :hide-selected="true"
:close-on-select="true" :close-on-select="true"
:show-labels="false" :show-labels="false"
:max="colsToSelect.length" :max="colsToSelect.length"
@@ -26,18 +26,18 @@
<span class="no-results">No Results</span> <span class="no-results">No Results</span>
</template> </template>
</multiselect> </multiselect>
<pivot-sort-btn class="sort-btn" direction="col" v-model="colOrder" /> <pivot-sort-btn v-model="colOrder" class="sort-btn" direction="col" />
</div> </div>
<div class="row"> <div class="row">
<label>Rows</label> <label>Rows</label>
<multiselect <multiselect
class="sqliteviz-select rows"
v-model="rows" v-model="rows"
class="sqliteviz-select rows"
:options="rowsToSelect" :options="rowsToSelect"
:disabled="rowsToSelect.length === 0" :disabled="rowsToSelect.length === 0"
:multiple="true" :multiple="true"
:hideSelected="true" :hide-selected="true"
:close-on-select="true" :close-on-select="true"
:show-labels="false" :show-labels="false"
:max="rowsToSelect.length" :max="rowsToSelect.length"
@@ -55,20 +55,20 @@
<span class="no-results">No Results</span> <span class="no-results">No Results</span>
</template> </template>
</multiselect> </multiselect>
<pivot-sort-btn class="sort-btn" direction="row" v-model="rowOrder" /> <pivot-sort-btn v-model="rowOrder" class="sort-btn" direction="row" />
</div> </div>
<div class="row aggregator"> <div class="row aggregator">
<label>Aggregator</label> <label>Aggregator</label>
<multiselect <multiselect
class="sqliteviz-select short aggregator"
v-model="aggregator" v-model="aggregator"
class="sqliteviz-select short aggregator"
:options="aggregators" :options="aggregators"
label="name" label="name"
track-by="name" track-by="name"
:close-on-select="true" :close-on-select="true"
:show-labels="false" :show-labels="false"
:hideSelected="true" :hide-selected="true"
:option-height="29" :option-height="29"
open-direction="bottom" open-direction="bottom"
placeholder="Choose a function" placeholder="Choose a function"
@@ -79,28 +79,28 @@
</multiselect> </multiselect>
<multiselect <multiselect
class="sqliteviz-select aggr-arg"
v-show="valCount > 0" v-show="valCount > 0"
v-model="val1" v-model="val1"
class="sqliteviz-select aggr-arg"
:options="keyNames" :options="keyNames"
:disabled="keyNames.length === 0" :disabled="keyNames.length === 0"
:close-on-select="true" :close-on-select="true"
:show-labels="false" :show-labels="false"
:hideSelected="true" :hide-selected="true"
:option-height="29" :option-height="29"
open-direction="bottom" open-direction="bottom"
placeholder="Choose an argument" placeholder="Choose an argument"
/> />
<multiselect <multiselect
class="sqliteviz-select aggr-arg"
v-show="valCount > 1" v-show="valCount > 1"
v-model="val2" v-model="val2"
class="sqliteviz-select aggr-arg"
:options="keyNames" :options="keyNames"
:disabled="keyNames.length === 0" :disabled="keyNames.length === 0"
:close-on-select="true" :close-on-select="true"
:show-labels="false" :show-labels="false"
:hideSelected="true" :hide-selected="true"
:option-height="29" :option-height="29"
open-direction="bottom" open-direction="bottom"
placeholder="Choose a second argument" placeholder="Choose a second argument"
@@ -110,15 +110,15 @@
<div class="row"> <div class="row">
<label>View</label> <label>View</label>
<multiselect <multiselect
class="sqliteviz-select short renderer"
v-model="renderer" v-model="renderer"
class="sqliteviz-select short renderer"
:options="renderers" :options="renderers"
label="name" label="name"
track-by="name" track-by="name"
:close-on-select="true" :close-on-select="true"
:allow-empty="false" :allow-empty="false"
:show-labels="false" :show-labels="false"
:hideSelected="true" :hide-selected="true"
:option-height="29" :option-height="29"
open-direction="bottom" open-direction="bottom"
placeholder="Choose a view" placeholder="Choose a view"
@@ -129,7 +129,7 @@
</multiselect> </multiselect>
</div> </div>
</div> </div>
<span @click="collapsed = !collapsed" class="switcher"> <span class="switcher" @click="collapsed = !collapsed">
{{ collapsed ? 'Show pivot settings' : 'Hide pivot settings' }} {{ collapsed ? 'Show pivot settings' : 'Hide pivot settings' }}
</span> </span>
</div> </div>
@@ -147,13 +147,16 @@ import {
} from '../pivotHelper' } from '../pivotHelper'
export default { export default {
name: 'pivotUi', name: 'PivotUi',
props: ['keyNames', 'modelValue'],
emits: ['update:modelValue', 'update'],
components: { components: {
Multiselect, Multiselect,
PivotSortBtn PivotSortBtn
}, },
props: {
keyNames: Array,
modelValue: Object
},
emits: ['update:modelValue', 'update'],
data() { data() {
const aggregatorName = const aggregatorName =
(this.modelValue && this.modelValue.aggregatorName) || 'Count' (this.modelValue && this.modelValue.aggregatorName) || 'Count'

View File

@@ -1,25 +1,25 @@
<template> <template>
<div class="pivot-container"> <div class="pivot-container">
<div class="warning pivot-warning" v-show="!dataSources"> <div v-show="!dataSources" class="warning pivot-warning">
There is no data to build a pivot. Run your SQL query and make sure the There is no data to build a pivot. Run your SQL query and make sure the
result is not empty. result is not empty.
</div> </div>
<pivot-ui <pivot-ui
:key-names="columns"
v-model="pivotOptions" v-model="pivotOptions"
:key-names="columns"
@update="$emit('update')" @update="$emit('update')"
/> />
<div ref="pivotOutput" class="pivot-output" /> <div ref="pivotOutput" class="pivot-output" />
<div <div
ref="customChartOutput"
v-show="viewCustomChart" v-show="viewCustomChart"
ref="customChartOutput"
class="custom-chart-output" class="custom-chart-output"
> >
<chart <chart
ref="customChart" ref="customChart"
v-bind="customChartComponentProps" v-bind="customChartComponentProps"
@update="$emit('update')" @update="$emit('update')"
@loadingImageCompleted="$emit('loadingImageCompleted')" @loading-image-completed="$emit('loadingImageCompleted')"
/> />
</div> </div>
</div> </div>
@@ -32,28 +32,28 @@ import 'pivottable'
import 'pivottable/dist/pivot.css' import 'pivottable/dist/pivot.css'
import PivotUi from './PivotUi' import PivotUi from './PivotUi'
import pivotHelper from './pivotHelper' import pivotHelper from './pivotHelper'
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart' import Chart from '@/views/MainView/Workspace/Tabs/Tab/DataView/Chart'
import chartHelper from '@/lib/chartHelper' import chartHelper from '@/lib/chartHelper'
import events from '@/lib/utils/events' import events from '@/lib/utils/events'
export default { export default {
name: 'pivot', name: 'Pivot',
props: [ components: {
'dataSources', PivotUi,
'initOptions', Chart
'importToPngEnabled', },
'importToSvgEnabled' props: {
], dataSources: Object,
initOptions: Object,
importToPngEnabled: Boolean,
importToSvgEnabled: Boolean
},
emits: [ emits: [
'loadingImageCompleted', 'loadingImageCompleted',
'update', 'update',
'update:importToSvgEnabled', 'update:importToSvgEnabled',
'update:importToPngEnabled' 'update:importToPngEnabled'
], ],
components: {
PivotUi,
Chart
},
data() { data() {
return { return {
resizeObserver: null, resizeObserver: null,

View File

@@ -3,30 +3,30 @@
<div class="data-view-panel-content"> <div class="data-view-panel-content">
<component <component
:is="mode" :is="mode"
:init-options="mode === initMode ? initOptions : undefined" ref="viewComponent"
:data-sources="dataSource"
v-model:import-to-png-enabled="importToPngEnabled" v-model:import-to-png-enabled="importToPngEnabled"
v-model:import-to-svg-enabled="importToSvgEnabled" v-model:import-to-svg-enabled="importToSvgEnabled"
@loadingImageCompleted="loadingImage = false" :init-options="mode === initMode ? initOptions : undefined"
ref="viewComponent" :data-sources="dataSource"
@loading-image-completed="loadingImage = false"
@update="$emit('update')" @update="$emit('update')"
/> />
</div> </div>
<side-tool-bar panel="dataView" @switchTo="$emit('switchTo', $event)"> <side-tool-bar panel="dataView" @switch-to="$emit('switchTo', $event)">
<icon-button <icon-button
:active="mode === 'chart'" :active="mode === 'chart'"
@click="mode = 'chart'"
tooltip="Switch to chart" tooltip="Switch to chart"
tooltip-position="top-left" tooltip-position="top-left"
@click="mode = 'chart'"
> >
<chart-icon /> <chart-icon />
</icon-button> </icon-button>
<icon-button <icon-button
ref="pivotBtn" ref="pivotBtn"
:active="mode === 'pivot'" :active="mode === 'pivot'"
@click="mode = 'pivot'"
tooltip="Switch to pivot" tooltip="Switch to pivot"
tooltip-position="top-left" tooltip-position="top-left"
@click="mode = 'pivot'"
> >
<pivot-icon /> <pivot-icon />
</icon-button> </icon-button>
@@ -72,9 +72,9 @@
</side-tool-bar> </side-tool-bar>
<loading-dialog <loading-dialog
loadingMsg="Rendering the visualisation..." loading-msg="Rendering the visualisation..."
successMsg="Image is ready" success-msg="Image is ready"
actionBtnName="Copy" action-btn-name="Copy"
name="prepareCopy" name="prepareCopy"
title="Copy to clipboard" title="Copy to clipboard"
:loading="preparingCopy" :loading="preparingCopy"
@@ -102,8 +102,6 @@ import events from '@/lib/utils/events'
export default { export default {
name: 'DataView', name: 'DataView',
props: ['dataSource', 'initOptions', 'initMode'],
emits: ['update', 'switchTo'],
components: { components: {
Chart, Chart,
Pivot, Pivot,
@@ -117,6 +115,12 @@ export default {
ClipboardIcon, ClipboardIcon,
loadingDialog loadingDialog
}, },
props: {
dataSource: Object,
initOptions: Object,
initMode: String
},
emits: ['update', 'switchTo'],
data() { data() {
return { return {
mode: this.initMode || 'chart', mode: this.initMode || 'chart',

View File

@@ -53,7 +53,8 @@ export default {
props: { props: {
modelValue: Number, modelValue: Number,
total: Number total: Number
} },
emits: ['update:modelValue']
} }
</script> </script>

View File

@@ -21,11 +21,11 @@
{{ col }} {{ col }}
</th> </th>
<td <td
:key="index"
:data-col="index" :data-col="index"
:data-row="currentRowIndex" :data-row="currentRowIndex"
:data-isNull="isNull(getCellValue(col))" :data-isNull="isNull(getCellValue(col))"
:data-isBlob="isBlob(getCellValue(col))" :data-isBlob="isBlob(getCellValue(col))"
:key="index"
:aria-selected="false" :aria-selected="false"
@click="onCellClick" @click="onCellClick"
> >
@@ -75,16 +75,6 @@ export default {
return this.dataSet.values[this.columns[0]].length return this.dataSet.values[this.columns[0]].length
} }
}, },
mounted() {
const col = this.selectedColumnIndex
const row = this.currentRowIndex
const cell = this.$refs.table.querySelector(
`td[data-col="${col}"][data-row="${row}"]`
)
if (cell) {
this.selectCell(cell)
}
},
watch: { watch: {
async currentRowIndex() { async currentRowIndex() {
await nextTick() await nextTick()
@@ -95,6 +85,16 @@ export default {
} }
} }
}, },
mounted() {
const col = this.selectedColumnIndex
const row = this.currentRowIndex
const cell = this.$refs.table.querySelector(
`td[data-col="${col}"][data-row="${row}"]`
)
if (cell) {
this.selectCell(cell)
}
},
methods: { methods: {
isBlob(value) { isBlob(value) {
return value && ArrayBuffer.isView(value) return value && ArrayBuffer.isView(value)

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="run-result-panel" ref="runResultPanel"> <div ref="runResultPanel" class="run-result-panel">
<component <component
:is="viewValuePanelVisible ? 'splitpanes' : 'div'" :is="viewValuePanelVisible ? 'splitpanes' : 'div'"
:before="{ size: 50, max: 100 }" :before="{ size: 50, max: 100 }"
@@ -17,11 +17,11 @@
:id="'run-result-result-set-' + tab.id" :id="'run-result-result-set-' + tab.id"
class="result-set-container" class="result-set-container"
/> />
<template #right-pane v-if="viewValuePanelVisible"> <template v-if="viewValuePanelVisible" #right-pane>
<div class="value-viewer-container"> <div class="value-viewer-container">
<value-viewer <value-viewer
v-show="selectedCell" v-show="selectedCell"
:cellValue=" :cell-value="
selectedCell selectedCell
? result.values[result.columns[selectedCell.dataset.col]][ ? result.values[result.columns[selectedCell.dataset.col]][
selectedCell.dataset.row selectedCell.dataset.row
@@ -36,7 +36,7 @@
</template> </template>
</component> </component>
<side-tool-bar @switchTo="$emit('switchTo', $event)" panel="table"> <side-tool-bar panel="table" @switch-to="$emit('switchTo', $event)">
<icon-button <icon-button
:disabled="!result" :disabled="!result"
tooltip="Export result set to CSV file" tooltip="Export result set to CSV file"
@@ -80,9 +80,9 @@
</side-tool-bar> </side-tool-bar>
<loading-dialog <loading-dialog
loadingMsg="Building CSV..." loading-msg="Building CSV..."
successMsg="CSV is ready" success-msg="CSV is ready"
actionBtnName="Copy" action-btn-name="Copy"
name="prepareCSVCopy" name="prepareCSVCopy"
title="Copy to clipboard" title="Copy to clipboard"
:loading="preparingCopy" :loading="preparingCopy"
@@ -113,21 +113,21 @@
v-if="result && !viewRecord" v-if="result && !viewRecord"
:data-set="result" :data-set="result"
:time="time" :time="time"
:pageSize="pageSize" :page-size="pageSize"
:page="defaultPage" :page="defaultPage"
:selected-cell-coordinates="defaultSelectedCell" :selected-cell-coordinates="defaultSelectedCell"
class="straight" class="straight"
@updateSelectedCell="onUpdateSelectedCell" @update-selected-cell="onUpdateSelectedCell"
/> />
<record <record
ref="recordView"
v-if="result && viewRecord" v-if="result && viewRecord"
ref="recordView"
:data-set="result" :data-set="result"
:time="time" :time="time"
:selected-column-index="selectedCell ? +selectedCell.dataset.col : 0" :selected-column-index="selectedCell ? +selectedCell.dataset.col : 0"
:rowIndex="selectedCell ? +selectedCell.dataset.row : 0" :row-index="selectedCell ? +selectedCell.dataset.row : 0"
@updateSelectedCell="onUpdateSelectedCell" @update-selected-cell="onUpdateSelectedCell"
/> />
</div> </div>
</teleport> </teleport>
@@ -156,6 +156,21 @@ import Record from './Record/index.vue'
export default { export default {
name: 'RunResult', name: 'RunResult',
components: {
SqlTable,
LoadingIndicator,
Logs,
SideToolBar,
ExportToCsvIcon,
IconButton,
ClipboardIcon,
ViewCellValueIcon,
RowIcon,
loadingDialog,
ValueViewer,
Record,
Splitpanes
},
props: { props: {
tab: Object, tab: Object,
result: Object, result: Object,
@@ -178,21 +193,6 @@ export default {
enableTeleport: this.$store.state.isWorkspaceVisible enableTeleport: this.$store.state.isWorkspaceVisible
} }
}, },
components: {
SqlTable,
LoadingIndicator,
Logs,
SideToolBar,
ExportToCsvIcon,
IconButton,
ClipboardIcon,
ViewCellValueIcon,
RowIcon,
loadingDialog,
ValueViewer,
Record,
Splitpanes
},
computed: { computed: {
resultSetTeleportTarget() { resultSetTeleportTarget() {
if (!this.enableTeleport) { if (!this.enableTeleport) {
@@ -207,6 +207,12 @@ export default {
return base + tabIdPostfix return base + tabIdPostfix
} }
}, },
watch: {
result() {
this.defaultSelectedCell = null
this.selectedCell = null
}
},
activated() { activated() {
this.enableTeleport = true this.enableTeleport = true
}, },
@@ -221,12 +227,6 @@ export default {
beforeUnmount() { beforeUnmount() {
this.resizeObserver.unobserve(this.$refs.runResultPanel) this.resizeObserver.unobserve(this.$refs.runResultPanel)
}, },
watch: {
result() {
this.defaultSelectedCell = null
this.selectedCell = null
}
},
methods: { methods: {
handleResize() { handleResize() {
this.calculatePageSize() this.calculatePageSize()

View File

@@ -30,7 +30,7 @@
<data-view-icon /> <data-view-icon />
</icon-button> </icon-button>
<div class="side-tool-bar-divider" v-if="$slots.default" /> <div v-if="$slots.default" class="side-tool-bar-divider" />
<slot /> <slot />
</div> </div>
@@ -44,14 +44,16 @@ import DataViewIcon from '@/components/svg/dataView'
export default { export default {
name: 'SideToolBar', name: 'SideToolBar',
props: ['panel'],
emits: ['switchTo'],
components: { components: {
IconButton, IconButton,
SqlEditorIcon, SqlEditorIcon,
DataViewIcon, DataViewIcon,
TableIcon TableIcon
} },
props: {
panel: String
},
emits: ['switchTo']
} }
</script> </script>

View File

@@ -9,7 +9,7 @@
@change="onChange" @change="onChange"
/> />
</div> </div>
<side-tool-bar panel="sqlEditor" @switchTo="$emit('switchTo', $event)"> <side-tool-bar panel="sqlEditor" @switch-to="$emit('switchTo', $event)">
<icon-button <icon-button
ref="runBtn" ref="runBtn"
:disabled="runDisabled" :disabled="runDisabled"
@@ -39,14 +39,14 @@ import RunIcon from '@/components/svg/run'
export default { export default {
name: 'SqlEditor', name: 'SqlEditor',
props: ['modelValue', 'isGettingResults'],
emits: ['update:modelValue', 'run', 'switchTo'],
components: { components: {
Codemirror, Codemirror,
SideToolBar, SideToolBar,
IconButton, IconButton,
RunIcon RunIcon
}, },
props: { modelValue: String, isGettingResults: Boolean },
emits: ['update:modelValue', 'run', 'switchTo'],
data() { data() {
return { return {
query: this.modelValue, query: this.modelValue,

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="tab-content-container" v-show="isActive"> <div v-show="isActive" class="tab-content-container">
<splitpanes <splitpanes
class="query-results-splitter" class="query-results-splitter"
horizontal horizontal
@@ -26,7 +26,7 @@
ref="sqlEditor" ref="sqlEditor"
v-model="tab.query" v-model="tab.query"
:is-getting-results="tab.isGettingResults" :is-getting-results="tab.isGettingResults"
@switchTo="onSwitchView('sqlEditor', $event)" @switch-to="onSwitchView('sqlEditor', $event)"
@run="tab.execute()" @run="tab.execute()"
/> />
</teleport> </teleport>
@@ -42,7 +42,7 @@
:is-getting-results="tab.isGettingResults" :is-getting-results="tab.isGettingResults"
:error="tab.error" :error="tab.error"
:time="tab.time" :time="tab.time"
@switchTo="onSwitchView('table', $event)" @switch-to="onSwitchView('table', $event)"
/> />
</teleport> </teleport>
@@ -52,11 +52,11 @@
:disabled="!enableTeleport" :disabled="!enableTeleport"
> >
<data-view <data-view
ref="dataView"
:data-source="(tab.result && tab.result.values) || null" :data-source="(tab.result && tab.result.values) || null"
:init-options="tab.viewOptions" :init-options="tab.viewOptions"
:init-mode="tab.viewType" :init-mode="tab.viewType"
ref="dataView" @switch-to="onSwitchView('dataView', $event)"
@switchTo="onSwitchView('dataView', $event)"
@update="onDataViewUpdate" @update="onDataViewUpdate"
/> />
</teleport> </teleport>
@@ -74,16 +74,16 @@ import events from '@/lib/utils/events'
export default { export default {
name: 'Tab', name: 'Tab',
props: {
tab: Object
},
emits: [],
components: { components: {
SqlEditor, SqlEditor,
DataView, DataView,
RunResult, RunResult,
Splitpanes Splitpanes
}, },
props: {
tab: Object
},
emits: [],
data() { data() {
return { return {
topPaneSize: this.tab.maximize topPaneSize: this.tab.maximize

View File

@@ -1,11 +1,11 @@
<template> <template>
<div id="tabs"> <div id="tabs">
<div id="tabs-header" v-if="tabs.length > 0"> <div v-if="tabs.length > 0" id="tabs-header">
<div <div
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="index" :key="index"
@click="selectTab(tab.id)"
:class="[{ 'tab-selected': tab.id === selectedTabId }, 'tab']" :class="[{ 'tab-selected': tab.id === selectedTabId }, 'tab']"
@click="selectTab(tab.id)"
> >
<div class="tab-name"> <div class="tab-name">
<span v-show="!tab.isSaved" class="star">*</span> <span v-show="!tab.isSaved" class="star">*</span>
@@ -69,11 +69,11 @@ import CloseIcon from '@/components/svg/close'
import eventBus from '@/lib/eventBus' import eventBus from '@/lib/eventBus'
export default { export default {
emits: [],
components: { components: {
Tab, Tab,
CloseIcon CloseIcon
}, },
emits: [],
data() { data() {
return { return {
closingTab: null closingTab: null

View File

@@ -14,7 +14,7 @@ import MainMenu from './MainMenu'
import '@/assets/styles/scrollbars.css' import '@/assets/styles/scrollbars.css'
export default { export default {
name: 'Main', name: 'MainView',
components: { MainMenu } components: { MainMenu }
} }
</script> </script>

View File

@@ -2,7 +2,7 @@ import { expect } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils' import { mount, shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import Inquiries from '@/views/Main/Inquiries' import Inquiries from '@/views/MainView/Inquiries'
import storedInquiries from '@/lib/storedInquiries' import storedInquiries from '@/lib/storedInquiries'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import actions from '@/store/actions' import actions from '@/store/actions'

View File

@@ -2,7 +2,7 @@ import { expect } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils' import { mount, shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import MainMenu from '@/views/Main/MainMenu' import MainMenu from '@/views/MainView/MainMenu'
import storedInquiries from '@/lib/storedInquiries' import storedInquiries from '@/lib/storedInquiries'
import { nextTick } from 'vue' import { nextTick } from 'vue'
import eventBus from '@/lib/eventBus' import eventBus from '@/lib/eventBus'

View File

@@ -4,8 +4,8 @@ import { mount } from '@vue/test-utils'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import actions from '@/store/actions' import actions from '@/store/actions'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import Schema from '@/views/Main/Workspace/Schema' import Schema from '@/views/MainView/Workspace/Schema'
import TableDescription from '@/views/Main/Workspace/Schema/TableDescription' import TableDescription from '@/views/MainView/Workspace/Schema/TableDescription'
import database from '@/lib/database' import database from '@/lib/database'
import fIo from '@/lib/utils/fileIo' import fIo from '@/lib/utils/fileIo'
import csv from '@/lib/csv' import csv from '@/lib/csv'

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { shallowMount } from '@vue/test-utils' import { shallowMount } from '@vue/test-utils'
import TableDescription from '@/views/Main/Workspace/Schema/TableDescription' import TableDescription from '@/views/MainView/Workspace/Schema/TableDescription'
describe('TableDescription.vue', () => { describe('TableDescription.vue', () => {
it('Initially the columns are hidden and table name is rendered', () => { it('Initially the columns are hidden and table name is rendered', () => {

View File

@@ -1,7 +1,7 @@
import { expect } from 'chai' import { expect } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
import { mount, flushPromises } from '@vue/test-utils' import { mount, flushPromises } from '@vue/test-utils'
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart/index.vue' import Chart from '@/views/MainView/Workspace/Tabs/Tab/DataView/Chart/index.vue'
import chartHelper from '@/lib/chartHelper' import chartHelper from '@/lib/chartHelper'
import * as dereference from 'react-chart-editor/lib/lib/dereference' import * as dereference from 'react-chart-editor/lib/lib/dereference'
import fIo from '@/lib/utils/fileIo' import fIo from '@/lib/utils/fileIo'

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import DataView from '@/views/Main/Workspace/Tabs/Tab/DataView' import DataView from '@/views/MainView/Workspace/Tabs/Tab/DataView'
import sinon from 'sinon' import sinon from 'sinon'
import { nextTick } from 'vue' import { nextTick } from 'vue'

View File

@@ -1,11 +1,11 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import Pivot from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot' import Pivot from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot'
import chartHelper from '@/lib/chartHelper' import chartHelper from '@/lib/chartHelper'
import fIo from '@/lib/utils/fileIo' import fIo from '@/lib/utils/fileIo'
import $ from 'jquery' import $ from 'jquery'
import sinon from 'sinon' import sinon from 'sinon'
import pivotHelper from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper' import pivotHelper from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
describe('Pivot.vue', () => { describe('Pivot.vue', () => {
let container let container

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { shallowMount } from '@vue/test-utils' import { shallowMount } from '@vue/test-utils'
import PivotSortBtn from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/PivotUi/PivotSortBtn' import PivotSortBtn from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/PivotUi/PivotSortBtn'
describe('PivotSortBtn.vue', () => { describe('PivotSortBtn.vue', () => {
it('switches order', async () => { it('switches order', async () => {

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import PivotUi from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/PivotUi' import PivotUi from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/PivotUi'
describe('PivotUi.vue', () => { describe('PivotUi.vue', () => {
it('returns value when settings changed', async () => { it('returns value when settings changed', async () => {

View File

@@ -3,7 +3,7 @@ import {
_getDataSources, _getDataSources,
getPivotCanvas, getPivotCanvas,
getPivotHtml getPivotHtml
} from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper' } from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
describe('pivotHelper.js', () => { describe('pivotHelper.js', () => {
it('_getDataSources returns data sources', () => { it('_getDataSources returns data sources', () => {

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import Record from '@/views/Main/Workspace/Tabs/Tab/RunResult/Record' import Record from '@/views/MainView/Workspace/Tabs/Tab/RunResult/Record'
describe('Record.vue', () => { describe('Record.vue', () => {
it('shows record with selected cell', async () => { it('shows record with selected cell', async () => {

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import RunResult from '@/views/Main/Workspace/Tabs/Tab/RunResult' import RunResult from '@/views/MainView/Workspace/Tabs/Tab/RunResult'
import csv from '@/lib/csv' import csv from '@/lib/csv'
import sinon from 'sinon' import sinon from 'sinon'
import { nextTick } from 'vue' import { nextTick } from 'vue'

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ValueViewer from '@/views/Main/Workspace/Tabs/Tab/RunResult/ValueViewer.vue' import ValueViewer from '@/views/MainView/Workspace/Tabs/Tab/RunResult/ValueViewer.vue'
import sinon from 'sinon' import sinon from 'sinon'
describe('ValueViewer.vue', () => { describe('ValueViewer.vue', () => {

View File

@@ -1,7 +1,7 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import SqlEditor from '@/views/Main/Workspace/Tabs/Tab/SqlEditor' import SqlEditor from '@/views/MainView/Workspace/Tabs/Tab/SqlEditor'
import { nextTick } from 'vue' import { nextTick } from 'vue'
describe('SqlEditor.vue', () => { describe('SqlEditor.vue', () => {

View File

@@ -3,7 +3,7 @@ import sinon from 'sinon'
import state from '@/store/state' import state from '@/store/state'
import showHint, { import showHint, {
getHints getHints
} from '@/views/Main/Workspace/Tabs/Tab/SqlEditor/hint' } from '@/views/MainView/Workspace/Tabs/Tab/SqlEditor/hint'
import CM from 'codemirror' import CM from 'codemirror'
describe('hint.js', () => { describe('hint.js', () => {

View File

@@ -3,7 +3,7 @@ import sinon from 'sinon'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import Tab from '@/views/Main/Workspace/Tabs/Tab' import Tab from '@/views/MainView/Workspace/Tabs/Tab'
import { nextTick } from 'vue' import { nextTick } from 'vue'
let place let place

View File

@@ -3,7 +3,7 @@ import sinon from 'sinon'
import { shallowMount, mount } from '@vue/test-utils' import { shallowMount, mount } from '@vue/test-utils'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import Tabs from '@/views/Main/Workspace/Tabs' import Tabs from '@/views/MainView/Workspace/Tabs'
import eventBus from '@/lib/eventBus' import eventBus from '@/lib/eventBus'
describe('Tabs.vue', () => { describe('Tabs.vue', () => {

View File

@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'
import actions from '@/store/actions' import actions from '@/store/actions'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import Workspace from '@/views/Main/Workspace' import Workspace from '@/views/MainView/Workspace'
describe('Workspace.vue', () => { describe('Workspace.vue', () => {
it('Creates a tab with example if schema is empty', () => { it('Creates a tab with example if schema is empty', () => {