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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,7 @@
</clipPath>
</defs>
</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
</span>
</span>
@@ -45,7 +45,7 @@ import tooltipMixin from '@/tooltipMixin'
export default {
name: 'AddTableIcon',
mixins: [tooltipMixin],
props: ['tooltip'],
props: { tooltip: String },
emits: ['click'],
methods: {
onClick() {

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
<template>
<svg
@click.stop="$emit('click')"
:class="['icon', { disabled: disabled }]"
:width="size"
:height="size"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@click.stop="$emit('click')"
>
<path
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>
export default {
name: 'DropDownChevron',
props: ['disabled']
props: { disabled: Boolean }
}
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
<template>
<div id="schema-container">
<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 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" />
{{ dbName }}
</div>

View File

@@ -1,6 +1,6 @@
<template>
<div class="chart-container" ref="chartContainer">
<div class="warning chart-warning" v-show="!dataSources && visible">
<div ref="chartContainer" class="chart-container">
<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
result is not empty.
</div>
@@ -10,17 +10,17 @@
>
<PlotlyEditor
v-show="visible"
ref="plotlyEditor"
:data="state.data"
:layout="state.layout"
:frames="state.frames"
:config="config"
:dataSources="dataSources"
:dataSourceOptions="dataSourceOptions"
:data-sources="dataSources"
:data-source-options="dataSourceOptions"
:plotly="plotly"
:useResizeHandler="useResizeHandler"
:use-resize-handler="useResizeHandler"
:debug="true"
:advancedTraceTypeSelector="true"
ref="plotlyEditor"
:advanced-trace-type-selector="true"
@update="update"
@render="onRender"
/>
@@ -40,17 +40,17 @@ import events from '@/lib/utils/events'
export default {
name: 'Chart',
props: [
'dataSources',
'initOptions',
'importToPngEnabled',
'importToSvgEnabled',
'forPivot'
],
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
components: {
PlotlyEditor: applyPureReactInVue(ReactPlotlyEditorWithPlotRef)
},
props: {
dataSources: Object,
initOptions: Object,
importToPngEnabled: Boolean,
importToSvgEnabled: Boolean,
forPivot: Boolean
},
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
data() {
return {
plotly,
@@ -74,6 +74,16 @@ export default {
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() {
// https://github.com/plotly/plotly.js/issues/4555
plotly.setPlotConfig({
@@ -113,21 +123,11 @@ export default {
beforeUnmount() {
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: {
async handleResize() {
this.updatePlotly()
},
onRender(data, layout, frames) {
onRender() {
// TODO: check changes and enable Save button if needed
},
update(data, layout, frames) {

View File

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

View File

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

View File

@@ -1,25 +1,25 @@
<template>
<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
result is not empty.
</div>
<pivot-ui
:key-names="columns"
v-model="pivotOptions"
:key-names="columns"
@update="$emit('update')"
/>
<div ref="pivotOutput" class="pivot-output" />
<div
ref="customChartOutput"
v-show="viewCustomChart"
ref="customChartOutput"
class="custom-chart-output"
>
<chart
ref="customChart"
v-bind="customChartComponentProps"
@update="$emit('update')"
@loadingImageCompleted="$emit('loadingImageCompleted')"
@loading-image-completed="$emit('loadingImageCompleted')"
/>
</div>
</div>
@@ -32,28 +32,28 @@ import 'pivottable'
import 'pivottable/dist/pivot.css'
import PivotUi from './PivotUi'
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 events from '@/lib/utils/events'
export default {
name: 'pivot',
props: [
'dataSources',
'initOptions',
'importToPngEnabled',
'importToSvgEnabled'
],
name: 'Pivot',
components: {
PivotUi,
Chart
},
props: {
dataSources: Object,
initOptions: Object,
importToPngEnabled: Boolean,
importToSvgEnabled: Boolean
},
emits: [
'loadingImageCompleted',
'update',
'update:importToSvgEnabled',
'update:importToPngEnabled'
],
components: {
PivotUi,
Chart
},
data() {
return {
resizeObserver: null,

View File

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

View File

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

View File

@@ -21,11 +21,11 @@
{{ col }}
</th>
<td
:key="index"
:data-col="index"
:data-row="currentRowIndex"
:data-isNull="isNull(getCellValue(col))"
:data-isBlob="isBlob(getCellValue(col))"
:key="index"
:aria-selected="false"
@click="onCellClick"
>
@@ -75,16 +75,6 @@ export default {
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: {
async currentRowIndex() {
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: {
isBlob(value) {
return value && ArrayBuffer.isView(value)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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