1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-07 02:28:54 +08:00

#63 migrate to Vue 3

This commit is contained in:
lana-k
2024-10-13 16:12:21 +02:00
parent b30b2181e4
commit 637d8d26dd
60 changed files with 16279 additions and 17440 deletions

View File

@@ -5,7 +5,11 @@
:src="require('@/assets/images/info.svg')"
@click="$modal.show('app-info')"
/>
<modal name="app-info" classes="dialog" height="auto" width="400px">
<modal
modal-id="app-info"
class="dialog"
content-class="app-info-modal"
>
<div class="dialog-header">
App info
<close-icon @click="$modal.hide('app-info')"/>
@@ -59,6 +63,12 @@ export default {
}
</script>
<style>
.app-info-modal {
width: 400px;
}
</style>
<style scoped>
#app-info-icon {
cursor: pointer;

View File

@@ -2,7 +2,7 @@
<div id="my-inquiries-container">
<div id="start-guide" v-if="allInquiries.length === 0">
You don't have saved inquiries so far.
<span class="link" @click="$root.$emit('createNewInquiry')">Create</span>
<span class="link" @click="emitCreateTabEvent">Create</span>
the one from scratch or
<span @click="importInquiries" class="link">import</span> from a file.
</div>
@@ -89,7 +89,9 @@
</td>
<td>
<div class="second-column">
<div class="date-container">{{ inquiry.createdAt | date }}</div>
<div class="date-container">
{{ createdAtFormatted(inquiry.createdAt) }}
</div>
<div class="icons-container">
<rename-icon
v-if="!inquiry.isPredefined"
@@ -116,7 +118,7 @@
</div>
<!--Rename Inquiry dialog -->
<modal name="rename" classes="dialog" height="auto">
<modal modal-id="rename" class="dialog">
<div class="dialog-header">
Rename inquiry
<close-icon @click="$modal.hide('rename')"/>
@@ -136,7 +138,7 @@
</modal>
<!--Delete Inquiry dialog -->
<modal name="delete" classes="dialog" height="auto">
<modal modal-id="delete" class="dialog">
<div class="dialog-header">
Delete {{ deleteGroup ? 'inquiries' : 'inquiry' }}
<close-icon @click="$modal.hide('delete')"/>
@@ -167,6 +169,7 @@ import CheckBox from '@/components/CheckBox'
import LoadingIndicator from '@/components/LoadingIndicator'
import tooltipMixin from '@/tooltipMixin'
import storedInquiries from '@/lib/storedInquiries'
import eventBus from '@/lib/eventBus'
export default {
name: 'Inquiries',
@@ -242,19 +245,24 @@ export default {
}
},
watch: {
showedInquiries () {
this.selectedInquiriesIds = new Set(this.showedInquiries
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
.map(inquiry => inquiry.id)
)
this.selectedInquiriesCount = this.selectedInquiriesIds.size
this.selectedNotPredefinedCount = ([...this.selectedInquiriesIds]
.filter(id => !this.predefinedInquiriesIds.has(id))).length
showedInquiries: {
handler () {
this.selectedInquiriesIds = new Set(this.showedInquiries
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
.map(inquiry => inquiry.id)
)
this.selectedInquiriesCount = this.selectedInquiriesIds.size
this.selectedNotPredefinedCount = ([...this.selectedInquiriesIds]
.filter(id => !this.predefinedInquiriesIds.has(id))).length
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
this.$refs.mainCheckBox.checked = false
this.selectAll = false
}
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
if (this.$refs.mainCheckBox) {
this.$refs.mainCheckBox.checked = false
}
this.selectAll = false
}
},
deep: true
}
},
async created () {
@@ -282,12 +290,15 @@ export default {
this.calcNameWidth()
this.calcMaxTableHeight()
},
beforeDestroy () {
beforeUnmount () {
this.resizeObserver.unobserve(this.$refs['my-inquiries-content'])
this.tableResizeObserver.unobserve(this.$refs.table)
},
filters: {
date (value) {
methods: {
emitCreateTabEvent () {
eventBus.$emit('createNewInquiry')
},
createdAtFormatted (value) {
if (!value) {
return ''
}
@@ -299,9 +310,7 @@ export default {
}
return new Date(value).toLocaleDateString('en-GB', dateOptions) + ' ' +
new Date(value).toLocaleTimeString('en-GB', timeOptions)
}
},
methods: {
},
calcNameWidth () {
const nameWidth = this.$refs['name-td'] && this.$refs['name-td'][0]
? this.$refs['name-td'][0].getBoundingClientRect().width
@@ -314,9 +323,11 @@ export default {
},
openInquiry (index) {
const tab = this.showedInquiries[index]
this.$store.dispatch('addTab', tab).then(id => {
this.$store.commit('setCurrentTabId', id)
this.$router.push('/workspace')
setTimeout(() => {
this.$store.dispatch('addTab', tab).then(id => {
this.$store.commit('setCurrentTabId', id)
this.$router.push('/workspace')
})
})
},
showRenameDialog (id) {
@@ -332,7 +343,7 @@ export default {
}
const processedInquiry = this.inquiries[this.processedInquiryIndex]
processedInquiry.name = this.newName
this.$set(this.inquiries, this.processedInquiryIndex, processedInquiry)
this.inquiries[this.processedInquiryIndex] = processedInquiry
// update inquiries in local storage
storedInquiries.updateStorage(this.inquiries)
@@ -430,7 +441,7 @@ export default {
this.selectedInquiriesCount = this.selectedInquiriesIds.size
this.selectedNotPredefinedCount = checked
? ([...this.selectedInquiriesIds].filter(id => !this.predefinedInquiriesIds.has(id)))
.length
.length
: 0
},

View File

@@ -31,6 +31,7 @@ import tooltipMixin from '@/tooltipMixin'
export default {
name: 'CopyIcon',
emits: ['click'],
mixins: [tooltipMixin],
methods: {
onClick () {

View File

@@ -29,6 +29,7 @@ import tooltipMixin from '@/tooltipMixin'
export default {
name: 'DeleteIcon',
emits: ['click'],
mixins: [tooltipMixin],
methods: {
onClick () {

View File

@@ -29,6 +29,7 @@ import tooltipMixin from '@/tooltipMixin'
export default {
name: 'RenameIcon',
emits: ['click'],
mixins: [tooltipMixin],
methods: {
onClick () {

View File

@@ -29,7 +29,7 @@
</div>
<!--Save Inquiry dialog -->
<modal name="save" classes="dialog" height="auto">
<modal modal-id="save" class="dialog">
<div class="dialog-header">
Save inquiry
<close-icon @click="cancelSave"/>
@@ -61,6 +61,7 @@ import CloseIcon from '@/components/svg/close'
import storedInquiries from '@/lib/storedInquiries'
import AppDiagnosticInfo from './AppDiagnosticInfo'
import events from '@/lib/utils/events'
import eventBus from '@/lib/eventBus'
export default {
name: 'MainMenu',
@@ -90,11 +91,11 @@ export default {
}
},
created () {
this.$root.$on('createNewInquiry', this.createNewInquiry)
this.$root.$on('saveInquiry', this.checkInquiryBeforeSave)
eventBus.$on('createNewInquiry', this.createNewInquiry)
eventBus.$on('saveInquiry', this.checkInquiryBeforeSave)
document.addEventListener('keydown', this._keyListener)
},
beforeDestroy () {
beforeUnmount () {
document.removeEventListener('keydown', this._keyListener)
},
methods: {
@@ -110,7 +111,7 @@ export default {
},
cancelSave () {
this.$modal.hide('save')
this.$root.$off('inquirySaved')
eventBus.$off('inquirySaved')
},
checkInquiryBeforeSave () {
this.errorMsg = null
@@ -161,7 +162,7 @@ export default {
this.$modal.hide('save')
// Signal about saving
this.$root.$emit('inquirySaved')
eventBus.$emit('inquirySaved')
events.send('inquiry.save')
},
_keyListener (e) {

View File

@@ -141,7 +141,7 @@ export default {
}
.db-name:hover .chevron-icon path,
>>> .table-name:hover .chevron-icon path {
:deep(.table-name:hover .chevron-icon path) {
fill: var(--color-gray-dark);
}
</style>

View File

@@ -11,23 +11,24 @@
:dataSources="dataSources"
:dataSourceOptions="dataSourceOptions"
:plotly="plotly"
@onUpdate="update"
@onRender="onRender"
:useResizeHandler="true"
:useResizeHandler="useResizeHandler"
:debug="true"
:advancedTraceTypeSelector="true"
class="chart"
ref="plotlyEditor"
:style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }"
@update="update"
@render="onRender"
/>
</div>
</template>
<script>
import { applyPureReactInVue } from 'veaury'
import plotly from 'plotly.js'
import 'react-chart-editor/lib/react-chart-editor.css'
import PlotlyEditor from 'react-chart-editor'
import ReactPlotlyEditor from 'react-chart-editor'
import chartHelper from '@/lib/chartHelper'
import dereference from 'react-chart-editor/lib/lib/dereference'
import fIo from '@/lib/utils/fileIo'
@@ -40,19 +41,21 @@ export default {
'importToPngEnabled', 'importToSvgEnabled',
'forPivot'
],
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
components: {
PlotlyEditor
PlotlyEditor: applyPureReactInVue(ReactPlotlyEditor)
},
data () {
return {
plotly: plotly,
plotly,
state: this.initOptions || {
data: [],
layout: {},
frames: []
},
visible: true,
resizeObserver: null
resizeObserver: null,
useResizeHandler: false
}
},
computed: {
@@ -83,7 +86,13 @@ export default {
this.resizeObserver = new ResizeObserver(this.handleResize)
this.resizeObserver.observe(this.$refs.chartContainer)
},
beforeDestroy () {
activated () {
this.useResizeHandler = true
},
deactivated () {
this.useResizeHandler = false
},
beforeUnmount () {
this.resizeObserver.unobserve(this.$refs.chartContainer)
},
watch: {
@@ -153,7 +162,7 @@ export default {
min-height: 242px;
}
>>> .editor_controls .sidebar__item:before {
:deep(.editor_controls .sidebar__item:before) {
width: 0;
}
</style>

View File

@@ -1,10 +1,10 @@
<template>
<div :class="['pivot-sort-btn', direction] " @click="changeSorting">
{{ value.includes('key') ? 'key' : 'value' }}
{{ modelValue.includes('key') ? 'key' : 'value' }}
<sort-icon
class="sort-icon"
:horizontal="direction === 'col'"
:asc="value.includes('a_to_z')"
:asc="modelValue.includes('a_to_z')"
/>
</div>
</template>
@@ -14,18 +14,18 @@ import SortIcon from '@/components/svg/sort'
export default {
name: 'PivotSortBtn',
props: ['direction', 'value'],
props: ['direction', 'modelValue'],
components: {
SortIcon
},
methods: {
changeSorting () {
if (this.value === 'key_a_to_z') {
this.$emit('input', 'value_a_to_z')
} else if (this.value === 'value_a_to_z') {
this.$emit('input', 'value_z_to_a')
if (this.modelValue === 'key_a_to_z') {
this.$emit('update:modelValue', 'value_a_to_z')
} else if (this.modelValue === 'value_a_to_z') {
this.$emit('update:modelValue', 'value_z_to_a')
} else {
this.$emit('input', 'key_a_to_z')
this.$emit('update:modelValue', 'key_a_to_z')
}
}
}
@@ -52,7 +52,7 @@ export default {
color: var(--color-text-active);
border-color: var(--color-border-dark);
}
.pivot-sort-btn:hover >>> .sort-icon path {
.pivot-sort-btn:hover deep(.sort-icon path) {
fill: var(--color-text-active);
}

View File

@@ -141,35 +141,35 @@ import Multiselect from 'vue-multiselect'
import PivotSortBtn from './PivotSortBtn'
import { renderers, aggregators, zeroValAggregators, twoValAggregators } from '../pivotHelper'
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart'
import Vue from 'vue'
const ChartClass = Vue.extend(Chart)
import { createApp } from 'vue'
export default {
name: 'pivotUi',
props: ['keyNames', 'value'],
props: ['keyNames', 'modelValue'],
emits: ['loadingCustomChartImageCompleted', 'update:modelValue', 'update'],
components: {
Multiselect,
PivotSortBtn
},
data () {
const aggregatorName = (this.value && this.value.aggregatorName) || 'Count'
const rendererName = (this.value && this.value.rendererName) || 'Table'
const aggregatorName = (this.modelValue && this.modelValue.aggregatorName) || 'Count'
const rendererName = (this.modelValue && this.modelValue.rendererName) || 'Table'
return {
collapsed: false,
renderer: { name: rendererName, fun: $.pivotUtilities.renderers[rendererName] },
aggregator: { name: aggregatorName, fun: $.pivotUtilities.aggregators[aggregatorName] },
rows: (this.value && this.value.rows) || [],
cols: (this.value && this.value.cols) || [],
val1: (this.value && this.value.vals && this.value.vals[0]) || '',
val2: (this.value && this.value.vals && this.value.vals[1]) || '',
colOrder: (this.value && this.value.colOrder) || 'key_a_to_z',
rowOrder: (this.value && this.value.rowOrder) || 'key_a_to_z',
rows: (this.modelValue && this.modelValue.rows) || [],
cols: (this.modelValue && this.modelValue.cols) || [],
val1: (this.modelValue && this.modelValue.vals && this.modelValue.vals[0]) || '',
val2: (this.modelValue && this.modelValue.vals && this.modelValue.vals[1]) || '',
colOrder: (this.modelValue && this.modelValue.colOrder) || 'key_a_to_z',
rowOrder: (this.modelValue && this.modelValue.rowOrder) || 'key_a_to_z',
customChartComponent:
(
this.value &&
this.value.rendererOptions &&
this.value.rendererOptions.customChartComponent
) || new ChartClass()
this.modelValue &&
this.modelValue.rendererOptions &&
this.modelValue.rendererOptions.customChartComponent
) || createApp(Chart)
}
},
computed: {
@@ -224,11 +224,10 @@ export default {
}
},
created () {
this.customChartComponent.$on('update', () => { this.$emit('update') })
this.customChartComponent.$on(
'loadingImageCompleted',
value => { this.$emit('loadingCustomChartImageCompleted') }
)
this.customChartComponent.onUpdate = () => { this.$emit('update') }
this.customChartComponent.onLoadingImageCompleted = () => {
this.$emit('loadingCustomChartImageCompleted')
}
},
methods: {
returnValue () {
@@ -237,7 +236,7 @@ export default {
vals.push(this[`val${i}`])
}
this.$emit('update')
this.$emit('input', {
this.$emit('update:modelValue', {
rows: this.rows,
cols: this.cols,
colOrder: this.colOrder,
@@ -246,9 +245,11 @@ export default {
aggregatorName: this.aggregator.name,
renderer: this.renderer.fun,
rendererName: this.renderer.name,
rendererOptions: this.renderer.name !== 'Custom chart' ? undefined : {
customChartComponent: this.customChartComponent
},
rendererOptions: this.renderer.name !== 'Custom chart'
? undefined
: {
customChartComponent: this.customChartComponent
},
vals
})
}

View File

@@ -14,6 +14,7 @@
</template>
<script>
import { createApp } from 'vue'
import fIo from '@/lib/utils/fileIo'
import $ from 'jquery'
import 'pivottable'
@@ -22,13 +23,17 @@ import PivotUi from './PivotUi'
import pivotHelper from './pivotHelper'
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart'
import chartHelper from '@/lib/chartHelper'
import Vue from 'vue'
import events from '@/lib/utils/events'
const ChartClass = Vue.extend(Chart)
export default {
name: 'pivot',
props: ['dataSources', 'initOptions', 'importToPngEnabled', 'importToSvgEnabled'],
emits: [
'loadingImageCompleted',
'update',
'update:importToSvgEnabled',
'update:importToPngEnabled'
],
components: {
PivotUi
},
@@ -37,35 +42,38 @@ export default {
resizeObserver: null,
pivotOptions: !this.initOptions
? {
rows: [],
cols: [],
colOrder: 'key_a_to_z',
rowOrder: 'key_a_to_z',
aggregatorName: 'Count',
aggregator: $.pivotUtilities.aggregators.Count(),
vals: [],
rendererName: 'Table',
renderer: $.pivotUtilities.renderers.Table,
rendererOptions: undefined
}
: {
rows: this.initOptions.rows,
cols: this.initOptions.cols,
colOrder: this.initOptions.colOrder,
rowOrder: this.initOptions.rowOrder,
aggregatorName: this.initOptions.aggregatorName,
aggregator: $.pivotUtilities.aggregators[
this.initOptions.aggregatorName
](this.initOptions.vals),
vals: this.initOptions.vals,
rendererName: this.initOptions.rendererName,
renderer: $.pivotUtilities.renderers[this.initOptions.rendererName],
rendererOptions: !this.initOptions.rendererOptions ? undefined : {
customChartComponent: new ChartClass({
propsData: { initOptions: this.initOptions.rendererOptions.customChartOptions }
})
rows: [],
cols: [],
colOrder: 'key_a_to_z',
rowOrder: 'key_a_to_z',
aggregatorName: 'Count',
aggregator: $.pivotUtilities.aggregators.Count(),
vals: [],
rendererName: 'Table',
renderer: $.pivotUtilities.renderers.Table,
rendererOptions: undefined
}
: {
rows: this.initOptions.rows,
cols: this.initOptions.cols,
colOrder: this.initOptions.colOrder,
rowOrder: this.initOptions.rowOrder,
aggregatorName: this.initOptions.aggregatorName,
aggregator: $.pivotUtilities.aggregators[
this.initOptions.aggregatorName
](this.initOptions.vals),
vals: this.initOptions.vals,
rendererName: this.initOptions.rendererName,
renderer: $.pivotUtilities.renderers[this.initOptions.rendererName],
rendererOptions: !this.initOptions.rendererOptions
? undefined
: {
customChartComponent: createApp(Chart, {
initOptions: this.initOptions
.rendererOptions.customChartOptions
})
}
}
}
}
},
computed: {
@@ -112,7 +120,7 @@ export default {
this.resizeObserver = new ResizeObserver(this.handleResize)
this.resizeObserver.observe(this.$refs.pivotOutput)
},
beforeDestroy () {
beforeUnmount () {
this.resizeObserver.unobserve(this.$refs.pivotOutput)
},
methods: {
@@ -249,26 +257,26 @@ export default {
line-height: 40px;
box-sizing: border-box;
}
>>> .pvtTable {
:deep(.pvtTable) {
min-width: 100%;
}
>>> table.pvtTable tbody tr td,
>>> table.pvtTable thead tr th,
>>> table.pvtTable tbody tr th {
:deep(table.pvtTable tbody tr td),
:deep(table.pvtTable thead tr th),
:deep(table.pvtTable tbody tr th) {
border-color: var(--color-border-light);
}
>>> table.pvtTable thead tr th,
>>> table.pvtTable tbody tr th {
:deep(table.pvtTable thead tr th),
:deep(table.pvtTable tbody tr th) {
background-color: var(--color-bg-dark);
color: var(--color-text-light);
}
>>> table.pvtTable tbody tr td {
:deep(table.pvtTable tbody tr td) {
color: var(--color-text-base);
}
.pivot-output >>> textarea {
.pivot-output :deep(textarea) {
color: var(--color-text-base);
min-width: 100%;
height: 100% !important;
@@ -277,7 +285,7 @@ export default {
border-width: 0;
}
.pivot-output >>> textarea:focus-visible {
.pivot-output :deep(textarea:focus-visible) {
outline: none;
}
</style>

View File

@@ -53,9 +53,11 @@ function customChartRenderer (data, options) {
options.customChartComponent.dataSources = _getDataSources(data)
options.customChartComponent.forPivot = true
options.customChartComponent.$mount()
const container = document.createElement('div')
return $(options.customChartComponent.$el)
options.customChartComponent.mount(container)
return $(container)
}
$.extend(

View File

@@ -5,8 +5,8 @@
:is="mode"
:init-options="mode === initMode ? initOptions : undefined"
:data-sources="dataSource"
:import-to-png-enabled.sync="importToPngEnabled"
:import-to-svg-enabled.sync="importToSvgEnabled"
v-model:import-to-png-enabled="importToPngEnabled"
v-model:import-to-svg-enabled="importToSvgEnabled"
@loadingImageCompleted="loadingImage = false"
ref="viewComponent"
@update="$emit('update')"
@@ -99,6 +99,7 @@ import events from '@/lib/utils/events'
export default {
name: 'DataView',
props: ['dataSource', 'initOptions', 'initMode'],
emits: ['update', 'switchTo'],
components: {
Chart,
Pivot,
@@ -233,7 +234,7 @@ export default {
height: 100%;
overflow: auto;
}
>>>.vm--container {
:deep(.vm--container) {
animation: show-modal 1s linear 0s 1;
}

View File

@@ -1,38 +1,38 @@
<template>
<div class="record-navigator">
<icon-button
:disabled="value === 0"
:disabled="modelValue === 0"
tooltip="First row"
tooltip-position="top-left"
class="first"
@click="$emit('input', 0)"
@click="$emit('update:modelValue', 0)"
>
<edge-arrow-icon :disabled="false" />
</icon-button>
<icon-button
:disabled="value === 0"
:disabled="modelValue === 0"
tooltip="Previous row"
tooltip-position="top-left"
class="prev"
@click="$emit('input', value - 1)"
@click="$emit('update:modelValue', modelValue - 1)"
>
<arrow-icon :disabled="false" />
</icon-button>
<icon-button
:disabled="value === total - 1"
:disabled="modelValue === total - 1"
tooltip="Next row"
tooltip-position="top-left"
class="next"
@click="$emit('input', value + 1)"
@click="$emit('update:modelValue', modelValue + 1)"
>
<arrow-icon :disabled="false" />
</icon-button>
<icon-button
:disabled="value === total - 1"
:disabled="modelValue === total - 1"
tooltip="Last row"
tooltip-position="top-left"
class="last"
@click="$emit('input', total - 1)"
@click="$emit('update:modelValue', total - 1)"
>
<edge-arrow-icon :disabled="false" />
</icon-button>
@@ -51,7 +51,7 @@ export default {
EdgeArrowIcon
},
props: {
value: Number,
modelValue: Number,
total: Number
}
}

View File

@@ -50,6 +50,7 @@
<script>
import RowNavigator from './RowNavigator.vue'
import { nextTick } from 'vue'
export default {
components: { RowNavigator },
@@ -59,6 +60,7 @@ export default {
rowIndex: { type: Number, default: 0 },
selectedColumnIndex: Number
},
emits: ['updateSelectedCell'],
data () {
return {
selectedCellElement: null,
@@ -84,7 +86,7 @@ export default {
},
watch: {
async currentRowIndex () {
await this.$nextTick()
await nextTick()
if (this.selectedCellElement) {
const previouslySelected = this.selectedCellElement
this.selectCell(null)

View File

@@ -41,7 +41,7 @@
</template>
<script>
import { codemirror } from 'vue-codemirror'
import Codemirror from 'codemirror-editor-vue3'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/addon/fold/foldcode.js'
@@ -54,7 +54,7 @@ import Logs from '@/components/Logs'
export default {
components: {
codemirror,
Codemirror,
Logs
},
props: {
@@ -192,15 +192,15 @@ export default {
color: var(--color-accent);
}
>>> .vue-codemirror {
:deep(.vue-codemirror) {
height: 100%;
max-height: 100%;
}
>>> .CodeMirror {
:deep(.CodeMirror) {
height: 100%;
max-height: 100%;
}
>>> .CodeMirror-cursor {
:deep(.CodeMirror-cursor) {
width: 1px;
background: var(--color-text-base);
}

View File

@@ -77,7 +77,11 @@
@cancel="cancelCopy"
/>
<teleport :to="resultSetTeleportTarget">
<teleport
defer
:to="resultSetTeleportTarget"
:disabled="!enableTeleport"
>
<div>
<div
v-show="result === null && !isGettingResults && !error"
@@ -138,7 +142,6 @@ import cIo from '@/lib/utils/clipboardIo'
import time from '@/lib/utils/time'
import loadingDialog from '@/components/LoadingDialog'
import events from '@/lib/utils/events'
import Teleport from 'vue2-teleport'
import ValueViewer from './ValueViewer'
import Record from './Record/index.vue'
@@ -151,6 +154,7 @@ export default {
error: Object,
time: [String, Number]
},
emits: ['switchTo'],
data () {
return {
resizeObserver: null,
@@ -161,7 +165,8 @@ export default {
selectedCell: null,
viewRecord: false,
defaultPage: 1,
defaultSelectedCell: null
defaultSelectedCell: null,
enableTeleport: false
}
},
components: {
@@ -177,11 +182,13 @@ export default {
loadingDialog,
ValueViewer,
Record,
Splitpanes,
Teleport
Splitpanes
},
computed: {
resultSetTeleportTarget () {
if (!this.enableTeleport) {
return undefined
}
const base = `#${this.viewValuePanelVisible
? 'run-result-left-pane'
: 'run-result-result-set'
@@ -190,12 +197,21 @@ export default {
return base + tabIdPostfix
}
},
activated () {
this.enableTeleport = true
},
deactivated () {
this.enableTeleport = false
},
mounted () {
if (this.$store.state.isWorkspaceVisible) {
this.enableTeleport = true
}
this.resizeObserver = new ResizeObserver(this.handleResize)
this.resizeObserver.observe(this.$refs.runResultPanel)
this.calculatePageSize()
},
beforeDestroy () {
beforeUnmount () {
this.resizeObserver.unobserve(this.$refs.runResultPanel)
},
watch: {
@@ -361,7 +377,7 @@ export default {
}
}
>>>.vm--container {
:deep(.vm--container) {
animation: show-modal 1s linear 0s 1;
}

View File

@@ -4,7 +4,7 @@
:active="panel === 'sqlEditor'"
tooltip="Switch panel to SQL editor"
tooltip-position="top-left"
@click.native="$emit('switchTo', 'sqlEditor')"
@click="$emit('switchTo', 'sqlEditor')"
>
<sql-editor-icon />
</icon-button>
@@ -13,7 +13,7 @@
:active="panel === 'table'"
tooltip="Switch panel to result set"
tooltip-position="top-left"
@click.native="$emit('switchTo', 'table')"
@click="$emit('switchTo', 'table')"
>
<table-icon/>
</icon-button>
@@ -22,7 +22,7 @@
:active="panel === 'dataView'"
tooltip="Switch panel to data view"
tooltip-position="top-left"
@click.native="$emit('switchTo', 'dataView')"
@click="$emit('switchTo', 'dataView')"
>
<data-view-icon />
</icon-button>
@@ -42,6 +42,7 @@ import DataViewIcon from '@/components/svg/dataView'
export default {
name: 'SideToolBar',
props: ['panel'],
emits: ['switchTo'],
components: {
IconButton,
SqlEditorIcon,

View File

@@ -43,7 +43,7 @@ export function showHintOnDemand (editor) {
CM.showHint(editor, getHints, hintOptions)
}
export default function showHint (editor) {
export default function showHint (value, editor) {
// Don't show autocomplete after a space or semicolon or in string literals
const token = editor.getTokenAt(editor.getCursor())
const ch = token.string.slice(-1)

View File

@@ -1,11 +1,12 @@
<template>
<div class="sql-editor-panel">
<div class="codemirror-container">
<div class="codemirror-container original-style">
<codemirror
ref="cm"
v-model="query"
v-model:value="query"
:options="cmOptions"
@changes="onChange"
:original-style="true"
@change="onChange"
/>
</div>
<side-tool-bar panel="sqlEditor" @switchTo="$emit('switchTo', $event)">
@@ -25,7 +26,7 @@
<script>
import showHint, { showHintOnDemand } from './hint'
import time from '@/lib/utils/time'
import { codemirror } from 'vue-codemirror'
import Codemirror from 'codemirror-editor-vue3'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/sql/sql.js'
import 'codemirror/theme/neo.css'
@@ -37,16 +38,17 @@ import RunIcon from '@/components/svg/run'
export default {
name: 'SqlEditor',
props: ['value', 'isGettingResults'],
props: ['modelValue', 'isGettingResults'],
emits: ['update:modelValue', 'run', 'switchTo'],
components: {
codemirror,
Codemirror,
SideToolBar,
IconButton,
RunIcon
},
data () {
return {
query: this.value,
query: this.modelValue,
cmOptions: {
tabSize: 4,
mode: 'text/x-mysql',
@@ -54,6 +56,7 @@ export default {
lineNumbers: true,
line: true,
autoRefresh: true,
styleActiveLine: false,
extraKeys: { 'Ctrl-Space': showHintOnDemand }
}
}
@@ -65,13 +68,13 @@ export default {
},
watch: {
query () {
this.$emit('input', this.query)
this.$emit('update:modelValue', this.query)
}
},
methods: {
onChange: time.debounce(showHint, 400),
focus () {
this.$refs.cm.codemirror.focus()
this.$refs.cm.cminstance.focus()
}
}
}
@@ -92,15 +95,15 @@ export default {
overflow: auto;
}
>>> .vue-codemirror {
:deep(.vue-codemirror) {
height: 100%;
max-height: 100%;
}
>>> .CodeMirror {
:deep(.CodeMirror) {
height: 100%;
max-height: 100%;
}
>>> .CodeMirror-cursor {
:deep(.CodeMirror-cursor) {
width: 1px;
background: var(--color-text-base);
}

View File

@@ -17,7 +17,11 @@
<div :id="'hidden-'+ tab.id" class="hidden-part" />
<teleport :to="`#${tab.layout.sqlEditor}-${tab.id}`">
<teleport
defer
:to="enableTeleport ? `#${tab.layout.sqlEditor}-${tab.id}`: undefined"
:disabled="!enableTeleport"
>
<sql-editor
ref="sqlEditor"
v-model="tab.query"
@@ -27,7 +31,11 @@
/>
</teleport>
<teleport :to="`#${tab.layout.table}-${tab.id}`">
<teleport
defer
:to="enableTeleport ? `#${tab.layout.table}-${tab.id}`: undefined"
:disabled="!enableTeleport"
>
<run-result
:tab="tab"
:result="tab.result"
@@ -38,7 +46,11 @@
/>
</teleport>
<teleport :to="`#${tab.layout.dataView}-${tab.id}`">
<teleport
defer
:to="enableTeleport ? `#${tab.layout.dataView}-${tab.id}` : undefined"
:disabled="!enableTeleport"
>
<data-view
:data-source="(tab.result && tab.result.values) || null"
:init-options="tab.viewOptions"
@@ -56,8 +68,8 @@ import Splitpanes from '@/components/Splitpanes'
import SqlEditor from './SqlEditor'
import DataView from './DataView'
import RunResult from './RunResult'
import { nextTick } from 'vue'
import Teleport from 'vue2-teleport'
import events from '@/lib/utils/events'
export default {
@@ -65,18 +77,19 @@ export default {
props: {
tab: Object
},
emits: [],
components: {
SqlEditor,
DataView,
RunResult,
Splitpanes,
Teleport
Splitpanes
},
data () {
return {
topPaneSize: this.tab.maximize
? this.tab.layout[this.tab.maximize] === 'above' ? 100 : 0
: 50
: 50,
enableTeleport: false
}
},
computed: {
@@ -89,7 +102,7 @@ export default {
immediate: true,
async handler () {
if (this.isActive) {
await this.$nextTick()
await nextTick()
this.$refs.sqlEditor.focus()
}
}
@@ -101,7 +114,16 @@ export default {
})
}
},
activated () {
this.enableTeleport = true
},
deactivated () {
this.enableTeleport = false
},
mounted () {
if (this.$store.state.isWorkspaceVisible) {
this.enableTeleport = true
}
this.tab.dataView = this.$refs.dataView
},
methods: {

View File

@@ -23,13 +23,13 @@
:tab="tab"
/>
<div v-show="tabs.length === 0" id="start-guide">
<span class="link" @click="$root.$emit('createNewInquiry')">Create</span>
<span class="link" @click="emitCreateTabEvent">Create</span>
new inquiry from scratch or open one from
<router-link class="link" to="/inquiries">Inquiries</router-link>
</div>
<!--Close tab warning dialog -->
<modal name="close-warn" classes="dialog" height="auto">
<modal modal-id="close-warn" class="dialog">
<div class="dialog-header">
Close tab {{
closingTab !== null
@@ -59,8 +59,10 @@
<script>
import Tab from './Tab'
import CloseIcon from '@/components/svg/close'
import eventBus from '@/lib/eventBus'
export default {
emits: [],
components: {
Tab,
CloseIcon
@@ -82,6 +84,9 @@ export default {
window.addEventListener('beforeunload', this.leavingSqliteviz)
},
methods: {
emitCreateTabEvent () {
eventBus.$emit('createNewInquiry')
},
leavingSqliteviz (event) {
if (this.tabs.some(tab => !tab.isSaved)) {
event.preventDefault()
@@ -104,14 +109,14 @@ export default {
this.$store.commit('deleteTab', tab)
},
saveAndClose (tab) {
this.$root.$on('inquirySaved', () => {
eventBus.$on('inquirySaved', () => {
this.closeTab(tab)
this.$root.$off('inquirySaved')
eventBus.$off('inquirySaved')
})
this.selectTab(tab.id)
this.$modal.hide('close-warn')
this.$nextTick(() => {
this.$root.$emit('saveInquiry')
eventBus.$emit('saveInquiry')
})
}
}

View File

@@ -59,6 +59,12 @@ export default {
events.send('inquiry.create', null, { auto: true })
}
},
activated () {
this.$store.commit('setIsWorkspaceVisible', true)
},
deactivated () {
this.$store.commit('setIsWorkspaceVisible', false)
}
}
</script>

View File

@@ -1,9 +1,11 @@
<template>
<div>
<main-menu />
<keep-alive include="Workspace,Inquiries">
<router-view id="main-view" />
</keep-alive>
<router-view id="main-view" v-slot="{ Component }">
<keep-alive include="Workspace,Inquiries">
<component :is="Component"/>
</keep-alive>
</router-view>
</div>
</template>

View File

@@ -39,14 +39,14 @@ export default {
margin-top: 42px;
}
>>>.drop-area {
:deep(.drop-area) {
width: 706px;
height: 482px;
padding: 0 150px;
position: relative;
}
>>>.drop-area .text {
:deep(.drop-area .text) {
position: absolute;
bottom: 42px;
max-width: 300px;