mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 18:18:53 +08:00
#63 migrate to Vue 3
This commit is contained in:
@@ -8,13 +8,17 @@ module.exports = {
|
|||||||
'@vue/standard'
|
'@vue/standard'
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: 'babel-eslint'
|
parser: '@babel/eslint-parser'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-case-declarations': 'off',
|
'no-case-declarations': 'off',
|
||||||
'max-len': [2, 100, 4, { ignoreUrls: true }]
|
'max-len': [2, 100, 4, { ignoreUrls: true }],
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-mutating-props': 'warn',
|
||||||
|
'vue/no-reserved-component-names': 'warn',
|
||||||
|
'vue/no-v-model-argument': 'off'
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { VuePlugin } from 'vuera'
|
// import { VuePlugin } from 'vuera'
|
||||||
import VModal from 'vue-js-modal'
|
// import VModal from 'vue-js-modal'
|
||||||
|
|
||||||
Vue.use(VuePlugin)
|
// Vue.use(VuePlugin)
|
||||||
Vue.use(VModal)
|
// Vue.use(VModal)
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
// require all test files (files that ends with .spec.js)
|
// require all test files (files that ends with .spec.js)
|
||||||
|
|||||||
32895
package-lock.json
generated
32895
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
@@ -10,7 +10,9 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"assert": "^2.1.0",
|
||||||
"codemirror": "^5.65.18",
|
"codemirror": "^5.65.18",
|
||||||
|
"codemirror-editor-vue3": "^2.8.0",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"dataurl-to-blob": "^0.0.1",
|
"dataurl-to-blob": "^0.0.1",
|
||||||
"html2canvas": "^1.1.4",
|
"html2canvas": "^1.1.4",
|
||||||
@@ -24,39 +26,41 @@
|
|||||||
"react-chart-editor": "^0.46.1",
|
"react-chart-editor": "^0.46.1",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^16.14.0",
|
||||||
"sql.js": "file:./lib/sql-js",
|
"sql.js": "file:./lib/sql-js",
|
||||||
"vue": "^2.6.11",
|
"stream-browserify": "^3.0.0",
|
||||||
"vue-codemirror": "^4.0.6",
|
"tiny-emitter": "^2.1.0",
|
||||||
"vue-js-modal": "^2.0.0-rc.6",
|
"url-loader": "^4.1.1",
|
||||||
"vue-multiselect": "^2.1.6",
|
"veaury": "^2.5.1",
|
||||||
"vue-router": "^3.2.0",
|
"vue": "^3.5.11",
|
||||||
"vue2-teleport": "^1.0.1",
|
"vue-final-modal": "^4.5.5",
|
||||||
"vuejs-paginate": "^2.1.0",
|
"vue-multiselect": "^3.0.0-beta.3",
|
||||||
"vuera": "^0.2.7",
|
"vue-router": "^4.4.5",
|
||||||
"vuex": "^3.4.0"
|
"vuejs-paginate-next": "^1.0.2",
|
||||||
|
"vuex": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.4.0",
|
"@babel/core": "^7.25.7",
|
||||||
"@vue/cli-plugin-eslint": "^4.4.0",
|
"@babel/eslint-parser": "^7.25.7",
|
||||||
"@vue/cli-plugin-router": "^4.4.0",
|
"@vue/cli-plugin-babel": "^5.0.8",
|
||||||
"@vue/cli-plugin-vuex": "^4.4.0",
|
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||||
"@vue/cli-service": "^4.4.0",
|
"@vue/cli-plugin-router": "^5.0.8",
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@vue/cli-plugin-vuex": "^5.0.8",
|
||||||
"@vue/test-utils": "^1.1.2",
|
"@vue/cli-service": "^5.0.8",
|
||||||
"babel-eslint": "^10.1.0",
|
"@vue/eslint-config-standard": "^8.0.1",
|
||||||
|
"@vue/test-utils": "^2.4.6",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"eslint": "^6.7.2",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
|
"eslint": "^8.57.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
"flush-promises": "^1.0.2",
|
"flush-promises": "^1.0.2",
|
||||||
"karma": "^3.1.4",
|
"karma": "^3.1.4",
|
||||||
"karma-firefox-launcher": "^2.1.0",
|
"karma-firefox-launcher": "^2.1.0",
|
||||||
"karma-webpack": "^4.0.2",
|
"karma-webpack": "^5.0.1",
|
||||||
"vue-cli-plugin-ui-karma": "^0.2.5",
|
"vue-cli-plugin-ui-karma": "^0.2.5",
|
||||||
"vue-template-compiler": "^2.6.11",
|
|
||||||
"workbox-webpack-plugin": "^6.1.5",
|
"workbox-webpack-plugin": "^6.1.5",
|
||||||
"worker-loader": "^3.0.8"
|
"worker-loader": "^3.0.8"
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/App.vue
13
src/App.vue
@@ -1,9 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<router-view/>
|
<router-view/>
|
||||||
|
<modals-container/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ModalsContainer } from 'vue-final-modal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ModalsContainer }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: "Open Sans";
|
||||||
@@ -48,10 +57,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#app,
|
#app,
|
||||||
|
.dialog,
|
||||||
input,
|
input,
|
||||||
label,
|
label,
|
||||||
button,
|
button,
|
||||||
.plotly_editor * {
|
.plotly_editor *,
|
||||||
|
.CodeMirror pre.CodeMirror-line {
|
||||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
.dialog {
|
.dialog {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.dialog .vfm__content {
|
||||||
border-radius: var(--border-radius-big);
|
border-radius: var(--border-radius-big);
|
||||||
box-shadow: 0px 2px 9px rgba(80, 103, 132, 0.8);
|
box-shadow: 0px 2px 9px rgba(80, 103, 132, 0.8);
|
||||||
|
background-color: white;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-header {
|
.dialog-header {
|
||||||
@@ -35,6 +42,6 @@
|
|||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vm--overlay {
|
.vfm__overlay.vfm--overlay {
|
||||||
background-color: rgba(162, 177, 198, 0.5);
|
background-color: rgba(162, 177, 198, 0.5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default {
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits: ['click'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
checked: this.init
|
checked: this.init
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
@click.stop
|
@click.stop
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
/>
|
/>
|
||||||
<div class="name">{{ getSymbolName(value) }}</div>
|
<div class="name">{{ getSymbolName(modelValue) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls" @click.stop>
|
<div class="controls" @click.stop>
|
||||||
<clear-icon @click.native="clear" :disabled="disabled"/>
|
<clear-icon @click="clear" :disabled="disabled"/>
|
||||||
<drop-down-chevron
|
<drop-down-chevron
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@click.native="!disabled && (showOptions = !showOptions)"
|
@click="!disabled && (showOptions = !showOptions)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +46,7 @@ import ClearIcon from '@/components/svg/clear'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DelimiterSelector',
|
name: 'DelimiterSelector',
|
||||||
props: ['value', 'width', 'disabled'],
|
props: ['modelValue', 'width', 'disabled'],
|
||||||
components: { DropDownChevron, ClearIcon },
|
components: { DropDownChevron, ClearIcon },
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -60,8 +60,8 @@ export default {
|
|||||||
inputValue () {
|
inputValue () {
|
||||||
if (this.inputValue) {
|
if (this.inputValue) {
|
||||||
this.filled = true
|
this.filled = true
|
||||||
if (this.inputValue !== this.value) {
|
if (this.inputValue !== this.modelValue) {
|
||||||
this.$emit('input', this.inputValue)
|
this.$emit('update:modelValue', this.inputValue)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.filled = false
|
this.filled = false
|
||||||
@@ -69,7 +69,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.inputValue = this.value
|
this.inputValue = this.modelValue
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getSymbolName (str) {
|
getSymbolName (str) {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<modal
|
<modal
|
||||||
:name="dialogName"
|
:modal-id="dialogName"
|
||||||
classes="dialog"
|
class="dialog"
|
||||||
height="auto"
|
content-class="import-modal"
|
||||||
width="80%"
|
|
||||||
scrollable
|
scrollable
|
||||||
:clickToClose="false"
|
:clickToClose="false"
|
||||||
>
|
>
|
||||||
@@ -130,6 +129,7 @@ export default {
|
|||||||
db: Object,
|
db: Object,
|
||||||
dialogName: String
|
dialogName: String
|
||||||
},
|
},
|
||||||
|
emits: ['cancel', 'finish'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
disableDialog: false,
|
disableDialog: false,
|
||||||
@@ -304,7 +304,7 @@ export default {
|
|||||||
let importLoadingIndicator = null
|
let importLoadingIndicator = null
|
||||||
|
|
||||||
const updateProgress = progress => {
|
const updateProgress = progress => {
|
||||||
this.$set(importMsg, 'progress', progress)
|
importMsg.progress = progress
|
||||||
}
|
}
|
||||||
const progressCounterId = this.db.createProgressCounter(updateProgress)
|
const progressCounterId = this.db.createProgressCounter(updateProgress)
|
||||||
|
|
||||||
@@ -392,15 +392,17 @@ export default {
|
|||||||
events.send('inquiry.create', null, { auto: true })
|
events.send('inquiry.create', null, { auto: true })
|
||||||
},
|
},
|
||||||
getQueryExample () {
|
getQueryExample () {
|
||||||
return this.isNdJson ? this.getNdJsonQueryExample()
|
return this.isNdJson
|
||||||
: this.isJson ? this.getJsonQueryExample()
|
? this.getNdJsonQueryExample()
|
||||||
|
: this.isJson
|
||||||
|
? this.getJsonQueryExample()
|
||||||
: [
|
: [
|
||||||
'/*',
|
'/*',
|
||||||
` * Your CSV file has been imported into ${this.addedTable} table.`,
|
` * Your CSV file has been imported into ${this.addedTable} table.`,
|
||||||
' * You can run this SQL query to make all CSV records available for charting.',
|
' * You can run this SQL query to make all CSV records available for charting.',
|
||||||
' */',
|
' */',
|
||||||
`SELECT * FROM "${this.addedTable}"`
|
`SELECT * FROM "${this.addedTable}"`
|
||||||
].join('\n')
|
].join('\n')
|
||||||
},
|
},
|
||||||
getNdJsonQueryExample () {
|
getNdJsonQueryExample () {
|
||||||
try {
|
try {
|
||||||
@@ -455,6 +457,15 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.import-modal {
|
||||||
|
width: 80%;
|
||||||
|
max-width: 1152px;
|
||||||
|
margin: auto;
|
||||||
|
left: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dialog-body {
|
.dialog-body {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
@@ -495,10 +506,4 @@ margin-bottom: 24px;
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* https://github.com/euvl/vue-js-modal/issues/623 */
|
|
||||||
>>> .vm--modal {
|
|
||||||
max-width: 1152px;
|
|
||||||
margin: auto;
|
|
||||||
left: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export default {
|
|||||||
default: 'unset'
|
default: 'unset'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits: [],
|
||||||
components: {
|
components: {
|
||||||
ChangeDbIcon,
|
ChangeDbIcon,
|
||||||
CsvJsonImport
|
CsvJsonImport
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import LoadingIndicator from '@/components/LoadingIndicator'
|
|||||||
export default {
|
export default {
|
||||||
name: 'SideBarButton',
|
name: 'SideBarButton',
|
||||||
props: ['active', 'disabled', 'tooltip', 'tooltipPosition', 'loading'],
|
props: ['active', 'disabled', 'tooltip', 'tooltipPosition', 'loading'],
|
||||||
|
emits: ['click'],
|
||||||
components: { LoadingIndicator },
|
components: { LoadingIndicator },
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
methods: {
|
methods: {
|
||||||
@@ -51,15 +52,15 @@ export default {
|
|||||||
border-radius: var(--border-radius-medium-2);
|
border-radius: var(--border-radius-medium-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-btn:hover .icon >>> path,
|
.icon-btn:hover .icon :deep(path),
|
||||||
.icon-btn.active .icon >>> path,
|
.icon-btn.active .icon :deep(path),
|
||||||
.icon-btn:hover .icon >>> circle,
|
.icon-btn:hover .icon :deep(circle),
|
||||||
.icon-btn.active .icon >>> circle {
|
.icon-btn.active .icon :deep(circle) {
|
||||||
fill: var(--color-accent);
|
fill: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-btn:disabled .icon >>> path,
|
.icon-btn:disabled .icon :deep(path),
|
||||||
.icon-btn:disabled .icon >>> circle {
|
.icon-btn:disabled .icon :deep(circle) {
|
||||||
fill: var(--color-border);
|
fill: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ export default {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled.icon-btn:hover .icon >>> path {
|
.disabled.icon-btn:hover .icon :deep(path) {
|
||||||
fill: var(--color-border);
|
fill: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<modal
|
<modal
|
||||||
:name="name"
|
:modal-id="name"
|
||||||
classes="dialog"
|
class="dialog"
|
||||||
height="auto"
|
|
||||||
:clickToClose="false"
|
:clickToClose="false"
|
||||||
>
|
>
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
@@ -52,6 +51,7 @@ export default {
|
|||||||
title: String,
|
title: String,
|
||||||
loading: Boolean
|
loading: Boolean
|
||||||
},
|
},
|
||||||
|
emits: ['cancel', 'action'],
|
||||||
watch: {
|
watch: {
|
||||||
loading () {
|
loading () {
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export default {
|
|||||||
default: 20
|
default: 20
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits: [],
|
||||||
computed: {
|
computed: {
|
||||||
circleProgress () {
|
circleProgress () {
|
||||||
const circle = this.radius * 3.14 * 2
|
const circle = this.radius * 3.14 * 2
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import LoadingIndicator from '@/components/LoadingIndicator'
|
|||||||
export default {
|
export default {
|
||||||
name: 'logs',
|
name: 'logs',
|
||||||
props: ['messages'],
|
props: ['messages'],
|
||||||
|
emits: [],
|
||||||
components: { LoadingIndicator },
|
components: { LoadingIndicator },
|
||||||
watch: {
|
watch: {
|
||||||
'messages.length': 'scrollToBottom'
|
'messages.length': 'scrollToBottom'
|
||||||
|
|||||||
@@ -86,15 +86,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits: [],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
container: null,
|
container: null,
|
||||||
paneBefore: this.before,
|
paneBefore: this.before,
|
||||||
paneAfter: this.after,
|
paneAfter: this.after,
|
||||||
beforeMinimising: !this.after.size || !this.before.size ? this.default : {
|
beforeMinimising: !this.after.size || !this.before.size
|
||||||
before: this.before.size,
|
? this.default
|
||||||
after: this.after.size
|
: {
|
||||||
},
|
before: this.before.size,
|
||||||
|
after: this.after.size
|
||||||
|
},
|
||||||
dragging: false,
|
dragging: false,
|
||||||
movableSplitter: {
|
movableSplitter: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|||||||
@@ -18,15 +18,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Paginate from 'vuejs-paginate'
|
import Paginate from 'vuejs-paginate-next'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Pager',
|
name: 'Pager',
|
||||||
components: { Paginate },
|
components: { Paginate },
|
||||||
props: ['pageCount', 'value'],
|
props: ['pageCount', 'modelValue'],
|
||||||
|
emits: ['update:modelValue'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
page: this.value,
|
page: this.modelValue,
|
||||||
chevron: `
|
chevron: `
|
||||||
<svg width="9" height="9" viewBox="0 0 8 12" fill="none">
|
<svg width="9" height="9" viewBox="0 0 8 12" fill="none">
|
||||||
<path
|
<path
|
||||||
@@ -39,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
page () {
|
page () {
|
||||||
this.$emit('input', this.page)
|
this.$emit('update:modelValue', this.page)
|
||||||
},
|
},
|
||||||
value () {
|
modelValue () {
|
||||||
this.page = this.value
|
this.page = this.modelValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,48 +55,48 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
}
|
}
|
||||||
>>> .paginator-page-link {
|
:deep(.paginator-page-link) {
|
||||||
padding: 2px 3px;
|
padding: 2px 3px;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
>>> .paginator-page-link:hover {
|
:deep(.paginator-page-link:hover) {
|
||||||
color: var(--color-text-active);
|
color: var(--color-text-active);
|
||||||
}
|
}
|
||||||
>>> .paginator-page-link:active,
|
:deep(.paginator-page-link:active),
|
||||||
>>> .paginator-page-link:visited,
|
:deep(.paginator-page-link:visited),
|
||||||
>>> .paginator-page-link:focus,
|
:deep(.paginator-page-link:focus),
|
||||||
>>> .paginator-next:active,
|
:deep(.paginator-next:active),
|
||||||
>>> .paginator-next:visited,
|
:deep(.paginator-next:visited),
|
||||||
>>> .paginator-next:focus,
|
:deep(.paginator-next:focus),
|
||||||
>>> .paginator-prev:active,
|
:deep(.paginator-prev:active),
|
||||||
>>> .paginator-prev:visited,
|
:deep(.paginator-prev:visited),
|
||||||
>>> .paginator-prev:focus {
|
:deep(.paginator-prev:focus) {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .paginator-active-page,
|
:deep(.paginator-active-page),
|
||||||
>>> .paginator-active-page:hover {
|
:deep(.paginator-active-page:hover) {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .paginator-break:hover,
|
:deep(.paginator-break:hover),
|
||||||
>>> .paginator-disabled:hover {
|
:deep(.paginator-disabled:hover) {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .paginator-prev svg {
|
:deep(.paginator-prev svg) {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .paginator-next:hover path,
|
:deep(.paginator-next:hover path),
|
||||||
>>> .paginator-prev:hover path {
|
:deep(.paginator-prev:hover path) {
|
||||||
fill: var(--color-text-active);
|
fill: var(--color-text-active);
|
||||||
}
|
}
|
||||||
>>> .paginator-disabled path,
|
:deep(.paginator-disabled path),
|
||||||
>>> .paginator-disabled:hover path {
|
:deep(.paginator-disabled:hover path) {
|
||||||
fill: var(--color-text-light-2);
|
fill: var(--color-text-light-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export default {
|
|||||||
preview: Boolean,
|
preview: Boolean,
|
||||||
selectedCellCoordinates: Object
|
selectedCellCoordinates: Object
|
||||||
},
|
},
|
||||||
|
emits: ['updateSelectedCell'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
header: null,
|
header: null,
|
||||||
@@ -248,7 +249,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeUnmount () {
|
||||||
this.resizeObserver.unobserve(this.$refs.table)
|
this.resizeObserver.unobserve(this.$refs.table)
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:class="{ error: errorMsg }"
|
:class="{ error: errorMsg }"
|
||||||
:style="{ width: width }"
|
:style="{ width: width }"
|
||||||
:value="value"
|
:value="modelValue"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@input="$emit('input', $event.target.value)"
|
@input="$emit('update:modelValue', $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<div v-show="errorMsg" class="text-field-error">{{ errorMsg }}</div>
|
<div v-show="errorMsg" class="text-field-error">{{ errorMsg }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,7 +21,16 @@
|
|||||||
import HintIcon from '@/components/svg/hint'
|
import HintIcon from '@/components/svg/hint'
|
||||||
export default {
|
export default {
|
||||||
name: 'textField',
|
name: 'textField',
|
||||||
props: ['placeholder', 'label', 'errorMsg', 'value', 'width', 'hint', 'maxHintWidth', 'disabled'],
|
props: [
|
||||||
|
'placeholder',
|
||||||
|
'label',
|
||||||
|
'errorMsg',
|
||||||
|
'modelValue',
|
||||||
|
'width',
|
||||||
|
'hint',
|
||||||
|
'maxHintWidth',
|
||||||
|
'disabled'
|
||||||
|
],
|
||||||
components: { HintIcon }
|
components: { HintIcon }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default {
|
|||||||
name: 'AddTableIcon',
|
name: 'AddTableIcon',
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
props: ['tooltip'],
|
props: ['tooltip'],
|
||||||
|
emits: ['click'],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
this.hideTooltip()
|
this.hideTooltip()
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import tooltipMixin from '@/tooltipMixin'
|
|||||||
export default {
|
export default {
|
||||||
name: 'changeDbIcon',
|
name: 'changeDbIcon',
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
|
emits: ['click'],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
this.hideTooltip()
|
this.hideTooltip()
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
emits: ['click']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default {
|
|||||||
name: 'ExportIcon',
|
name: 'ExportIcon',
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
props: ['tooltip', 'tooltipPosition'],
|
props: ['tooltip', 'tooltipPosition'],
|
||||||
|
emits: ['click'],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
this.hideTooltip()
|
this.hideTooltip()
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import tooltipMixin from '@/tooltipMixin'
|
|||||||
export default {
|
export default {
|
||||||
name: 'HintIcon',
|
name: 'HintIcon',
|
||||||
props: ['hint', 'maxWidth'],
|
props: ['hint', 'maxWidth'],
|
||||||
|
emits: ['click'],
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
|
|||||||
8
src/lib/eventBus.js
Normal file
8
src/lib/eventBus.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import emitter from 'tiny-emitter/instance'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
$on: (...args) => emitter.on(...args),
|
||||||
|
$once: (...args) => emitter.once(...args),
|
||||||
|
$off: (...args) => emitter.off(...args),
|
||||||
|
$emit: (...args) => emitter.emit(...args)
|
||||||
|
}
|
||||||
@@ -48,11 +48,13 @@ export default {
|
|||||||
// Get inquiries from local storage
|
// Get inquiries from local storage
|
||||||
const myInquiries = this.getStoredInquiries()
|
const myInquiries = this.getStoredInquiries()
|
||||||
|
|
||||||
|
let inquiryIndex
|
||||||
// Set createdAt
|
// Set createdAt
|
||||||
if (newName) {
|
if (newName) {
|
||||||
value.createdAt = new Date()
|
value.createdAt = new Date()
|
||||||
} else {
|
} else {
|
||||||
var inquiryIndex = myInquiries.findIndex(oldInquiry => oldInquiry.id === inquiryTab.id)
|
inquiryIndex = myInquiries
|
||||||
|
.findIndex(oldInquiry => oldInquiry.id === inquiryTab.id)
|
||||||
value.createdAt = myInquiries[inquiryIndex].createdAt
|
value.createdAt = myInquiries[inquiryIndex].createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
src/main.js
28
src/main.js
@@ -1,9 +1,8 @@
|
|||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from '@/App.vue'
|
import App from '@/App.vue'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import { VuePlugin } from 'vuera'
|
import { createVfm, VueFinalModal, useVfm } from 'vue-final-modal'
|
||||||
import VModal from 'vue-js-modal'
|
|
||||||
|
|
||||||
import '@/assets/styles/variables.css'
|
import '@/assets/styles/variables.css'
|
||||||
import '@/assets/styles/buttons.css'
|
import '@/assets/styles/buttons.css'
|
||||||
@@ -11,20 +10,23 @@ import '@/assets/styles/tables.css'
|
|||||||
import '@/assets/styles/dialogs.css'
|
import '@/assets/styles/dialogs.css'
|
||||||
import '@/assets/styles/tooltips.css'
|
import '@/assets/styles/tooltips.css'
|
||||||
import '@/assets/styles/messages.css'
|
import '@/assets/styles/messages.css'
|
||||||
import 'vue-multiselect/dist/vue-multiselect.min.css'
|
import 'vue-multiselect/dist/vue-multiselect.css'
|
||||||
import '@/assets/styles/multiselect.css'
|
import '@/assets/styles/multiselect.css'
|
||||||
|
import 'vue-final-modal/style.css'
|
||||||
|
|
||||||
if (!['localhost', '127.0.0.1'].includes(location.hostname)) {
|
if (!['localhost', '127.0.0.1'].includes(location.hostname)) {
|
||||||
import('./registerServiceWorker') // eslint-disable-line no-unused-expressions
|
import('./registerServiceWorker') // eslint-disable-line no-unused-expressions
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.use(VuePlugin)
|
const app = createApp(App)
|
||||||
Vue.use(VModal)
|
.use(router)
|
||||||
|
.use(store)
|
||||||
|
.use(createVfm())
|
||||||
|
.component('modal', VueFinalModal)
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
const vfm = useVfm()
|
||||||
|
app.config.globalProperties.$modal = {
|
||||||
new Vue({
|
show: modalId => vfm.open(modalId),
|
||||||
router,
|
hide: modalId => vfm.close(modalId)
|
||||||
store,
|
}
|
||||||
render: h => h(App)
|
app.mount('#app')
|
||||||
}).$mount('#app')
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Vue from 'vue'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import VueRouter from 'vue-router'
|
|
||||||
import Workspace from '@/views/Main/Workspace'
|
import Workspace from '@/views/Main/Workspace'
|
||||||
import Inquiries from '@/views/Main/Inquiries'
|
import Inquiries from '@/views/Main/Inquiries'
|
||||||
import Welcome from '@/views/Welcome'
|
import Welcome from '@/views/Welcome'
|
||||||
@@ -8,8 +7,6 @@ import LoadView from '@/views/LoadView'
|
|||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -40,7 +37,8 @@ const routes = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
routes
|
routes
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import Vue from 'vue'
|
import { createStore } from 'vuex'
|
||||||
import Vuex from 'vuex'
|
|
||||||
import state from '@/store/state'
|
import state from '@/store/state'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import actions from '@/store/actions'
|
import actions from '@/store/actions'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
export default createStore({
|
||||||
|
|
||||||
export default new Vuex.Store({
|
|
||||||
state,
|
state,
|
||||||
mutations,
|
mutations,
|
||||||
actions
|
actions
|
||||||
|
|||||||
@@ -60,5 +60,8 @@ export default {
|
|||||||
},
|
},
|
||||||
setPredefinedInquiriesLoaded (state, value) {
|
setPredefinedInquiriesLoaded (state, value) {
|
||||||
state.predefinedInquiriesLoaded = value
|
state.predefinedInquiriesLoaded = value
|
||||||
|
},
|
||||||
|
setIsWorkspaceVisible (state, value) {
|
||||||
|
state.isWorkspaceVisible = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export default {
|
|||||||
predefinedInquiries: [],
|
predefinedInquiries: [],
|
||||||
loadingPredefinedInquiries: false,
|
loadingPredefinedInquiries: false,
|
||||||
predefinedInquiriesLoaded: false,
|
predefinedInquiriesLoaded: false,
|
||||||
db: null
|
db: null,
|
||||||
|
isWorkspaceVisible: false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,11 @@
|
|||||||
:src="require('@/assets/images/info.svg')"
|
:src="require('@/assets/images/info.svg')"
|
||||||
@click="$modal.show('app-info')"
|
@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">
|
<div class="dialog-header">
|
||||||
App info
|
App info
|
||||||
<close-icon @click="$modal.hide('app-info')"/>
|
<close-icon @click="$modal.hide('app-info')"/>
|
||||||
@@ -59,6 +63,12 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.app-info-modal {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#app-info-icon {
|
#app-info-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div id="my-inquiries-container">
|
<div id="my-inquiries-container">
|
||||||
<div id="start-guide" v-if="allInquiries.length === 0">
|
<div id="start-guide" v-if="allInquiries.length === 0">
|
||||||
You don't have saved inquiries so far.
|
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
|
the one from scratch or
|
||||||
<span @click="importInquiries" class="link">import</span> from a file.
|
<span @click="importInquiries" class="link">import</span> from a file.
|
||||||
</div>
|
</div>
|
||||||
@@ -89,7 +89,9 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="second-column">
|
<div class="second-column">
|
||||||
<div class="date-container">{{ inquiry.createdAt | date }}</div>
|
<div class="date-container">
|
||||||
|
{{ createdAtFormatted(inquiry.createdAt) }}
|
||||||
|
</div>
|
||||||
<div class="icons-container">
|
<div class="icons-container">
|
||||||
<rename-icon
|
<rename-icon
|
||||||
v-if="!inquiry.isPredefined"
|
v-if="!inquiry.isPredefined"
|
||||||
@@ -116,7 +118,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Rename Inquiry dialog -->
|
<!--Rename Inquiry dialog -->
|
||||||
<modal name="rename" classes="dialog" height="auto">
|
<modal modal-id="rename" class="dialog">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
Rename inquiry
|
Rename inquiry
|
||||||
<close-icon @click="$modal.hide('rename')"/>
|
<close-icon @click="$modal.hide('rename')"/>
|
||||||
@@ -136,7 +138,7 @@
|
|||||||
</modal>
|
</modal>
|
||||||
|
|
||||||
<!--Delete Inquiry dialog -->
|
<!--Delete Inquiry dialog -->
|
||||||
<modal name="delete" classes="dialog" height="auto">
|
<modal modal-id="delete" class="dialog">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
Delete {{ deleteGroup ? 'inquiries' : 'inquiry' }}
|
Delete {{ deleteGroup ? 'inquiries' : 'inquiry' }}
|
||||||
<close-icon @click="$modal.hide('delete')"/>
|
<close-icon @click="$modal.hide('delete')"/>
|
||||||
@@ -167,6 +169,7 @@ import CheckBox from '@/components/CheckBox'
|
|||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/LoadingIndicator'
|
||||||
import tooltipMixin from '@/tooltipMixin'
|
import tooltipMixin from '@/tooltipMixin'
|
||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
|
import eventBus from '@/lib/eventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Inquiries',
|
name: 'Inquiries',
|
||||||
@@ -242,19 +245,24 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
showedInquiries () {
|
showedInquiries: {
|
||||||
this.selectedInquiriesIds = new Set(this.showedInquiries
|
handler () {
|
||||||
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
|
this.selectedInquiriesIds = new Set(this.showedInquiries
|
||||||
.map(inquiry => inquiry.id)
|
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
|
||||||
)
|
.map(inquiry => inquiry.id)
|
||||||
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
)
|
||||||
this.selectedNotPredefinedCount = ([...this.selectedInquiriesIds]
|
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
||||||
.filter(id => !this.predefinedInquiriesIds.has(id))).length
|
this.selectedNotPredefinedCount = ([...this.selectedInquiriesIds]
|
||||||
|
.filter(id => !this.predefinedInquiriesIds.has(id))).length
|
||||||
|
|
||||||
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
|
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
|
||||||
this.$refs.mainCheckBox.checked = false
|
if (this.$refs.mainCheckBox) {
|
||||||
this.selectAll = false
|
this.$refs.mainCheckBox.checked = false
|
||||||
}
|
}
|
||||||
|
this.selectAll = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
@@ -282,12 +290,15 @@ export default {
|
|||||||
this.calcNameWidth()
|
this.calcNameWidth()
|
||||||
this.calcMaxTableHeight()
|
this.calcMaxTableHeight()
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeUnmount () {
|
||||||
this.resizeObserver.unobserve(this.$refs['my-inquiries-content'])
|
this.resizeObserver.unobserve(this.$refs['my-inquiries-content'])
|
||||||
this.tableResizeObserver.unobserve(this.$refs.table)
|
this.tableResizeObserver.unobserve(this.$refs.table)
|
||||||
},
|
},
|
||||||
filters: {
|
methods: {
|
||||||
date (value) {
|
emitCreateTabEvent () {
|
||||||
|
eventBus.$emit('createNewInquiry')
|
||||||
|
},
|
||||||
|
createdAtFormatted (value) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -299,9 +310,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return new Date(value).toLocaleDateString('en-GB', dateOptions) + ' ' +
|
return new Date(value).toLocaleDateString('en-GB', dateOptions) + ' ' +
|
||||||
new Date(value).toLocaleTimeString('en-GB', timeOptions)
|
new Date(value).toLocaleTimeString('en-GB', timeOptions)
|
||||||
}
|
},
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
calcNameWidth () {
|
calcNameWidth () {
|
||||||
const nameWidth = this.$refs['name-td'] && this.$refs['name-td'][0]
|
const nameWidth = this.$refs['name-td'] && this.$refs['name-td'][0]
|
||||||
? this.$refs['name-td'][0].getBoundingClientRect().width
|
? this.$refs['name-td'][0].getBoundingClientRect().width
|
||||||
@@ -314,9 +323,11 @@ export default {
|
|||||||
},
|
},
|
||||||
openInquiry (index) {
|
openInquiry (index) {
|
||||||
const tab = this.showedInquiries[index]
|
const tab = this.showedInquiries[index]
|
||||||
this.$store.dispatch('addTab', tab).then(id => {
|
setTimeout(() => {
|
||||||
this.$store.commit('setCurrentTabId', id)
|
this.$store.dispatch('addTab', tab).then(id => {
|
||||||
this.$router.push('/workspace')
|
this.$store.commit('setCurrentTabId', id)
|
||||||
|
this.$router.push('/workspace')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
showRenameDialog (id) {
|
showRenameDialog (id) {
|
||||||
@@ -332,7 +343,7 @@ export default {
|
|||||||
}
|
}
|
||||||
const processedInquiry = this.inquiries[this.processedInquiryIndex]
|
const processedInquiry = this.inquiries[this.processedInquiryIndex]
|
||||||
processedInquiry.name = this.newName
|
processedInquiry.name = this.newName
|
||||||
this.$set(this.inquiries, this.processedInquiryIndex, processedInquiry)
|
this.inquiries[this.processedInquiryIndex] = processedInquiry
|
||||||
|
|
||||||
// update inquiries in local storage
|
// update inquiries in local storage
|
||||||
storedInquiries.updateStorage(this.inquiries)
|
storedInquiries.updateStorage(this.inquiries)
|
||||||
@@ -430,7 +441,7 @@ export default {
|
|||||||
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
||||||
this.selectedNotPredefinedCount = checked
|
this.selectedNotPredefinedCount = checked
|
||||||
? ([...this.selectedInquiriesIds].filter(id => !this.predefinedInquiriesIds.has(id)))
|
? ([...this.selectedInquiriesIds].filter(id => !this.predefinedInquiriesIds.has(id)))
|
||||||
.length
|
.length
|
||||||
: 0
|
: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import tooltipMixin from '@/tooltipMixin'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CopyIcon',
|
name: 'CopyIcon',
|
||||||
|
emits: ['click'],
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import tooltipMixin from '@/tooltipMixin'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DeleteIcon',
|
name: 'DeleteIcon',
|
||||||
|
emits: ['click'],
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import tooltipMixin from '@/tooltipMixin'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RenameIcon',
|
name: 'RenameIcon',
|
||||||
|
emits: ['click'],
|
||||||
mixins: [tooltipMixin],
|
mixins: [tooltipMixin],
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Save Inquiry dialog -->
|
<!--Save Inquiry dialog -->
|
||||||
<modal name="save" classes="dialog" height="auto">
|
<modal modal-id="save" class="dialog">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
Save inquiry
|
Save inquiry
|
||||||
<close-icon @click="cancelSave"/>
|
<close-icon @click="cancelSave"/>
|
||||||
@@ -61,6 +61,7 @@ import CloseIcon from '@/components/svg/close'
|
|||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
import AppDiagnosticInfo from './AppDiagnosticInfo'
|
import AppDiagnosticInfo from './AppDiagnosticInfo'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
import eventBus from '@/lib/eventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MainMenu',
|
name: 'MainMenu',
|
||||||
@@ -90,11 +91,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.$root.$on('createNewInquiry', this.createNewInquiry)
|
eventBus.$on('createNewInquiry', this.createNewInquiry)
|
||||||
this.$root.$on('saveInquiry', this.checkInquiryBeforeSave)
|
eventBus.$on('saveInquiry', this.checkInquiryBeforeSave)
|
||||||
document.addEventListener('keydown', this._keyListener)
|
document.addEventListener('keydown', this._keyListener)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeUnmount () {
|
||||||
document.removeEventListener('keydown', this._keyListener)
|
document.removeEventListener('keydown', this._keyListener)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -110,7 +111,7 @@ export default {
|
|||||||
},
|
},
|
||||||
cancelSave () {
|
cancelSave () {
|
||||||
this.$modal.hide('save')
|
this.$modal.hide('save')
|
||||||
this.$root.$off('inquirySaved')
|
eventBus.$off('inquirySaved')
|
||||||
},
|
},
|
||||||
checkInquiryBeforeSave () {
|
checkInquiryBeforeSave () {
|
||||||
this.errorMsg = null
|
this.errorMsg = null
|
||||||
@@ -161,7 +162,7 @@ export default {
|
|||||||
this.$modal.hide('save')
|
this.$modal.hide('save')
|
||||||
|
|
||||||
// Signal about saving
|
// Signal about saving
|
||||||
this.$root.$emit('inquirySaved')
|
eventBus.$emit('inquirySaved')
|
||||||
events.send('inquiry.save')
|
events.send('inquiry.save')
|
||||||
},
|
},
|
||||||
_keyListener (e) {
|
_keyListener (e) {
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.db-name:hover .chevron-icon path,
|
.db-name:hover .chevron-icon path,
|
||||||
>>> .table-name:hover .chevron-icon path {
|
:deep(.table-name:hover .chevron-icon path) {
|
||||||
fill: var(--color-gray-dark);
|
fill: var(--color-gray-dark);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,23 +11,24 @@
|
|||||||
:dataSources="dataSources"
|
:dataSources="dataSources"
|
||||||
:dataSourceOptions="dataSourceOptions"
|
:dataSourceOptions="dataSourceOptions"
|
||||||
:plotly="plotly"
|
:plotly="plotly"
|
||||||
@onUpdate="update"
|
:useResizeHandler="useResizeHandler"
|
||||||
@onRender="onRender"
|
|
||||||
:useResizeHandler="true"
|
|
||||||
:debug="true"
|
:debug="true"
|
||||||
:advancedTraceTypeSelector="true"
|
:advancedTraceTypeSelector="true"
|
||||||
class="chart"
|
class="chart"
|
||||||
ref="plotlyEditor"
|
ref="plotlyEditor"
|
||||||
:style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }"
|
:style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }"
|
||||||
|
@update="update"
|
||||||
|
@render="onRender"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { applyPureReactInVue } from 'veaury'
|
||||||
import plotly from 'plotly.js'
|
import plotly from 'plotly.js'
|
||||||
import 'react-chart-editor/lib/react-chart-editor.css'
|
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 chartHelper from '@/lib/chartHelper'
|
||||||
import dereference from 'react-chart-editor/lib/lib/dereference'
|
import dereference from 'react-chart-editor/lib/lib/dereference'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
@@ -40,19 +41,21 @@ export default {
|
|||||||
'importToPngEnabled', 'importToSvgEnabled',
|
'importToPngEnabled', 'importToSvgEnabled',
|
||||||
'forPivot'
|
'forPivot'
|
||||||
],
|
],
|
||||||
|
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
|
||||||
components: {
|
components: {
|
||||||
PlotlyEditor
|
PlotlyEditor: applyPureReactInVue(ReactPlotlyEditor)
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
plotly: plotly,
|
plotly,
|
||||||
state: this.initOptions || {
|
state: this.initOptions || {
|
||||||
data: [],
|
data: [],
|
||||||
layout: {},
|
layout: {},
|
||||||
frames: []
|
frames: []
|
||||||
},
|
},
|
||||||
visible: true,
|
visible: true,
|
||||||
resizeObserver: null
|
resizeObserver: null,
|
||||||
|
useResizeHandler: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -83,7 +86,13 @@ export default {
|
|||||||
this.resizeObserver = new ResizeObserver(this.handleResize)
|
this.resizeObserver = new ResizeObserver(this.handleResize)
|
||||||
this.resizeObserver.observe(this.$refs.chartContainer)
|
this.resizeObserver.observe(this.$refs.chartContainer)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
activated () {
|
||||||
|
this.useResizeHandler = true
|
||||||
|
},
|
||||||
|
deactivated () {
|
||||||
|
this.useResizeHandler = false
|
||||||
|
},
|
||||||
|
beforeUnmount () {
|
||||||
this.resizeObserver.unobserve(this.$refs.chartContainer)
|
this.resizeObserver.unobserve(this.$refs.chartContainer)
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -153,7 +162,7 @@ export default {
|
|||||||
min-height: 242px;
|
min-height: 242px;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .editor_controls .sidebar__item:before {
|
:deep(.editor_controls .sidebar__item:before) {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="['pivot-sort-btn', direction] " @click="changeSorting">
|
<div :class="['pivot-sort-btn', direction] " @click="changeSorting">
|
||||||
{{ value.includes('key') ? 'key' : 'value' }}
|
{{ modelValue.includes('key') ? 'key' : 'value' }}
|
||||||
<sort-icon
|
<sort-icon
|
||||||
class="sort-icon"
|
class="sort-icon"
|
||||||
:horizontal="direction === 'col'"
|
:horizontal="direction === 'col'"
|
||||||
:asc="value.includes('a_to_z')"
|
:asc="modelValue.includes('a_to_z')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -14,18 +14,18 @@ import SortIcon from '@/components/svg/sort'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PivotSortBtn',
|
name: 'PivotSortBtn',
|
||||||
props: ['direction', 'value'],
|
props: ['direction', 'modelValue'],
|
||||||
components: {
|
components: {
|
||||||
SortIcon
|
SortIcon
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeSorting () {
|
changeSorting () {
|
||||||
if (this.value === 'key_a_to_z') {
|
if (this.modelValue === 'key_a_to_z') {
|
||||||
this.$emit('input', 'value_a_to_z')
|
this.$emit('update:modelValue', 'value_a_to_z')
|
||||||
} else if (this.value === 'value_a_to_z') {
|
} else if (this.modelValue === 'value_a_to_z') {
|
||||||
this.$emit('input', 'value_z_to_a')
|
this.$emit('update:modelValue', 'value_z_to_a')
|
||||||
} else {
|
} 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);
|
color: var(--color-text-active);
|
||||||
border-color: var(--color-border-dark);
|
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);
|
fill: var(--color-text-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,35 +141,35 @@ import Multiselect from 'vue-multiselect'
|
|||||||
import PivotSortBtn from './PivotSortBtn'
|
import PivotSortBtn from './PivotSortBtn'
|
||||||
import { renderers, aggregators, zeroValAggregators, twoValAggregators } from '../pivotHelper'
|
import { renderers, aggregators, zeroValAggregators, twoValAggregators } from '../pivotHelper'
|
||||||
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart'
|
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart'
|
||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
const ChartClass = Vue.extend(Chart)
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'pivotUi',
|
name: 'pivotUi',
|
||||||
props: ['keyNames', 'value'],
|
props: ['keyNames', 'modelValue'],
|
||||||
|
emits: ['loadingCustomChartImageCompleted', 'update:modelValue', 'update'],
|
||||||
components: {
|
components: {
|
||||||
Multiselect,
|
Multiselect,
|
||||||
PivotSortBtn
|
PivotSortBtn
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
const aggregatorName = (this.value && this.value.aggregatorName) || 'Count'
|
const aggregatorName = (this.modelValue && this.modelValue.aggregatorName) || 'Count'
|
||||||
const rendererName = (this.value && this.value.rendererName) || 'Table'
|
const rendererName = (this.modelValue && this.modelValue.rendererName) || 'Table'
|
||||||
return {
|
return {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
renderer: { name: rendererName, fun: $.pivotUtilities.renderers[rendererName] },
|
renderer: { name: rendererName, fun: $.pivotUtilities.renderers[rendererName] },
|
||||||
aggregator: { name: aggregatorName, fun: $.pivotUtilities.aggregators[aggregatorName] },
|
aggregator: { name: aggregatorName, fun: $.pivotUtilities.aggregators[aggregatorName] },
|
||||||
rows: (this.value && this.value.rows) || [],
|
rows: (this.modelValue && this.modelValue.rows) || [],
|
||||||
cols: (this.value && this.value.cols) || [],
|
cols: (this.modelValue && this.modelValue.cols) || [],
|
||||||
val1: (this.value && this.value.vals && this.value.vals[0]) || '',
|
val1: (this.modelValue && this.modelValue.vals && this.modelValue.vals[0]) || '',
|
||||||
val2: (this.value && this.value.vals && this.value.vals[1]) || '',
|
val2: (this.modelValue && this.modelValue.vals && this.modelValue.vals[1]) || '',
|
||||||
colOrder: (this.value && this.value.colOrder) || 'key_a_to_z',
|
colOrder: (this.modelValue && this.modelValue.colOrder) || 'key_a_to_z',
|
||||||
rowOrder: (this.value && this.value.rowOrder) || 'key_a_to_z',
|
rowOrder: (this.modelValue && this.modelValue.rowOrder) || 'key_a_to_z',
|
||||||
customChartComponent:
|
customChartComponent:
|
||||||
(
|
(
|
||||||
this.value &&
|
this.modelValue &&
|
||||||
this.value.rendererOptions &&
|
this.modelValue.rendererOptions &&
|
||||||
this.value.rendererOptions.customChartComponent
|
this.modelValue.rendererOptions.customChartComponent
|
||||||
) || new ChartClass()
|
) || createApp(Chart)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -224,11 +224,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.customChartComponent.$on('update', () => { this.$emit('update') })
|
this.customChartComponent.onUpdate = () => { this.$emit('update') }
|
||||||
this.customChartComponent.$on(
|
this.customChartComponent.onLoadingImageCompleted = () => {
|
||||||
'loadingImageCompleted',
|
this.$emit('loadingCustomChartImageCompleted')
|
||||||
value => { this.$emit('loadingCustomChartImageCompleted') }
|
}
|
||||||
)
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
returnValue () {
|
returnValue () {
|
||||||
@@ -237,7 +236,7 @@ export default {
|
|||||||
vals.push(this[`val${i}`])
|
vals.push(this[`val${i}`])
|
||||||
}
|
}
|
||||||
this.$emit('update')
|
this.$emit('update')
|
||||||
this.$emit('input', {
|
this.$emit('update:modelValue', {
|
||||||
rows: this.rows,
|
rows: this.rows,
|
||||||
cols: this.cols,
|
cols: this.cols,
|
||||||
colOrder: this.colOrder,
|
colOrder: this.colOrder,
|
||||||
@@ -246,9 +245,11 @@ export default {
|
|||||||
aggregatorName: this.aggregator.name,
|
aggregatorName: this.aggregator.name,
|
||||||
renderer: this.renderer.fun,
|
renderer: this.renderer.fun,
|
||||||
rendererName: this.renderer.name,
|
rendererName: this.renderer.name,
|
||||||
rendererOptions: this.renderer.name !== 'Custom chart' ? undefined : {
|
rendererOptions: this.renderer.name !== 'Custom chart'
|
||||||
customChartComponent: this.customChartComponent
|
? undefined
|
||||||
},
|
: {
|
||||||
|
customChartComponent: this.customChartComponent
|
||||||
|
},
|
||||||
vals
|
vals
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { createApp } from 'vue'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import 'pivottable'
|
import 'pivottable'
|
||||||
@@ -22,13 +23,17 @@ 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/Main/Workspace/Tabs/Tab/DataView/Chart'
|
||||||
import chartHelper from '@/lib/chartHelper'
|
import chartHelper from '@/lib/chartHelper'
|
||||||
import Vue from 'vue'
|
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
const ChartClass = Vue.extend(Chart)
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'pivot',
|
name: 'pivot',
|
||||||
props: ['dataSources', 'initOptions', 'importToPngEnabled', 'importToSvgEnabled'],
|
props: ['dataSources', 'initOptions', 'importToPngEnabled', 'importToSvgEnabled'],
|
||||||
|
emits: [
|
||||||
|
'loadingImageCompleted',
|
||||||
|
'update',
|
||||||
|
'update:importToSvgEnabled',
|
||||||
|
'update:importToPngEnabled'
|
||||||
|
],
|
||||||
components: {
|
components: {
|
||||||
PivotUi
|
PivotUi
|
||||||
},
|
},
|
||||||
@@ -37,35 +42,38 @@ export default {
|
|||||||
resizeObserver: null,
|
resizeObserver: null,
|
||||||
pivotOptions: !this.initOptions
|
pivotOptions: !this.initOptions
|
||||||
? {
|
? {
|
||||||
rows: [],
|
rows: [],
|
||||||
cols: [],
|
cols: [],
|
||||||
colOrder: 'key_a_to_z',
|
colOrder: 'key_a_to_z',
|
||||||
rowOrder: 'key_a_to_z',
|
rowOrder: 'key_a_to_z',
|
||||||
aggregatorName: 'Count',
|
aggregatorName: 'Count',
|
||||||
aggregator: $.pivotUtilities.aggregators.Count(),
|
aggregator: $.pivotUtilities.aggregators.Count(),
|
||||||
vals: [],
|
vals: [],
|
||||||
rendererName: 'Table',
|
rendererName: 'Table',
|
||||||
renderer: $.pivotUtilities.renderers.Table,
|
renderer: $.pivotUtilities.renderers.Table,
|
||||||
rendererOptions: undefined
|
rendererOptions: undefined
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
rows: this.initOptions.rows,
|
rows: this.initOptions.rows,
|
||||||
cols: this.initOptions.cols,
|
cols: this.initOptions.cols,
|
||||||
colOrder: this.initOptions.colOrder,
|
colOrder: this.initOptions.colOrder,
|
||||||
rowOrder: this.initOptions.rowOrder,
|
rowOrder: this.initOptions.rowOrder,
|
||||||
aggregatorName: this.initOptions.aggregatorName,
|
aggregatorName: this.initOptions.aggregatorName,
|
||||||
aggregator: $.pivotUtilities.aggregators[
|
aggregator: $.pivotUtilities.aggregators[
|
||||||
this.initOptions.aggregatorName
|
this.initOptions.aggregatorName
|
||||||
](this.initOptions.vals),
|
](this.initOptions.vals),
|
||||||
vals: this.initOptions.vals,
|
vals: this.initOptions.vals,
|
||||||
rendererName: this.initOptions.rendererName,
|
rendererName: this.initOptions.rendererName,
|
||||||
renderer: $.pivotUtilities.renderers[this.initOptions.rendererName],
|
renderer: $.pivotUtilities.renderers[this.initOptions.rendererName],
|
||||||
rendererOptions: !this.initOptions.rendererOptions ? undefined : {
|
rendererOptions: !this.initOptions.rendererOptions
|
||||||
customChartComponent: new ChartClass({
|
? undefined
|
||||||
propsData: { initOptions: this.initOptions.rendererOptions.customChartOptions }
|
: {
|
||||||
})
|
customChartComponent: createApp(Chart, {
|
||||||
|
initOptions: this.initOptions
|
||||||
|
.rendererOptions.customChartOptions
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -112,7 +120,7 @@ export default {
|
|||||||
this.resizeObserver = new ResizeObserver(this.handleResize)
|
this.resizeObserver = new ResizeObserver(this.handleResize)
|
||||||
this.resizeObserver.observe(this.$refs.pivotOutput)
|
this.resizeObserver.observe(this.$refs.pivotOutput)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeUnmount () {
|
||||||
this.resizeObserver.unobserve(this.$refs.pivotOutput)
|
this.resizeObserver.unobserve(this.$refs.pivotOutput)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -249,26 +257,26 @@ export default {
|
|||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
>>> .pvtTable {
|
:deep(.pvtTable) {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> table.pvtTable tbody tr td,
|
:deep(table.pvtTable tbody tr td),
|
||||||
>>> table.pvtTable thead tr th,
|
:deep(table.pvtTable thead tr th),
|
||||||
>>> table.pvtTable tbody tr th {
|
:deep(table.pvtTable tbody tr th) {
|
||||||
border-color: var(--color-border-light);
|
border-color: var(--color-border-light);
|
||||||
}
|
}
|
||||||
>>> table.pvtTable thead tr th,
|
:deep(table.pvtTable thead tr th),
|
||||||
>>> table.pvtTable tbody tr th {
|
:deep(table.pvtTable tbody tr th) {
|
||||||
background-color: var(--color-bg-dark);
|
background-color: var(--color-bg-dark);
|
||||||
color: var(--color-text-light);
|
color: var(--color-text-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> table.pvtTable tbody tr td {
|
:deep(table.pvtTable tbody tr td) {
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pivot-output >>> textarea {
|
.pivot-output :deep(textarea) {
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
@@ -277,7 +285,7 @@ export default {
|
|||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pivot-output >>> textarea:focus-visible {
|
.pivot-output :deep(textarea:focus-visible) {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -53,9 +53,11 @@ function customChartRenderer (data, options) {
|
|||||||
options.customChartComponent.dataSources = _getDataSources(data)
|
options.customChartComponent.dataSources = _getDataSources(data)
|
||||||
options.customChartComponent.forPivot = true
|
options.customChartComponent.forPivot = true
|
||||||
|
|
||||||
options.customChartComponent.$mount()
|
const container = document.createElement('div')
|
||||||
|
|
||||||
return $(options.customChartComponent.$el)
|
options.customChartComponent.mount(container)
|
||||||
|
|
||||||
|
return $(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
$.extend(
|
$.extend(
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
:is="mode"
|
:is="mode"
|
||||||
:init-options="mode === initMode ? initOptions : undefined"
|
:init-options="mode === initMode ? initOptions : undefined"
|
||||||
:data-sources="dataSource"
|
:data-sources="dataSource"
|
||||||
:import-to-png-enabled.sync="importToPngEnabled"
|
v-model:import-to-png-enabled="importToPngEnabled"
|
||||||
:import-to-svg-enabled.sync="importToSvgEnabled"
|
v-model:import-to-svg-enabled="importToSvgEnabled"
|
||||||
@loadingImageCompleted="loadingImage = false"
|
@loadingImageCompleted="loadingImage = false"
|
||||||
ref="viewComponent"
|
ref="viewComponent"
|
||||||
@update="$emit('update')"
|
@update="$emit('update')"
|
||||||
@@ -99,6 +99,7 @@ import events from '@/lib/utils/events'
|
|||||||
export default {
|
export default {
|
||||||
name: 'DataView',
|
name: 'DataView',
|
||||||
props: ['dataSource', 'initOptions', 'initMode'],
|
props: ['dataSource', 'initOptions', 'initMode'],
|
||||||
|
emits: ['update', 'switchTo'],
|
||||||
components: {
|
components: {
|
||||||
Chart,
|
Chart,
|
||||||
Pivot,
|
Pivot,
|
||||||
@@ -233,7 +234,7 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
>>>.vm--container {
|
:deep(.vm--container) {
|
||||||
animation: show-modal 1s linear 0s 1;
|
animation: show-modal 1s linear 0s 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="record-navigator">
|
<div class="record-navigator">
|
||||||
<icon-button
|
<icon-button
|
||||||
:disabled="value === 0"
|
:disabled="modelValue === 0"
|
||||||
tooltip="First row"
|
tooltip="First row"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
class="first"
|
class="first"
|
||||||
@click="$emit('input', 0)"
|
@click="$emit('update:modelValue', 0)"
|
||||||
>
|
>
|
||||||
<edge-arrow-icon :disabled="false" />
|
<edge-arrow-icon :disabled="false" />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
<icon-button
|
<icon-button
|
||||||
:disabled="value === 0"
|
:disabled="modelValue === 0"
|
||||||
tooltip="Previous row"
|
tooltip="Previous row"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
class="prev"
|
class="prev"
|
||||||
@click="$emit('input', value - 1)"
|
@click="$emit('update:modelValue', modelValue - 1)"
|
||||||
>
|
>
|
||||||
<arrow-icon :disabled="false" />
|
<arrow-icon :disabled="false" />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
<icon-button
|
<icon-button
|
||||||
:disabled="value === total - 1"
|
:disabled="modelValue === total - 1"
|
||||||
tooltip="Next row"
|
tooltip="Next row"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
class="next"
|
class="next"
|
||||||
@click="$emit('input', value + 1)"
|
@click="$emit('update:modelValue', modelValue + 1)"
|
||||||
>
|
>
|
||||||
<arrow-icon :disabled="false" />
|
<arrow-icon :disabled="false" />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
<icon-button
|
<icon-button
|
||||||
:disabled="value === total - 1"
|
:disabled="modelValue === total - 1"
|
||||||
tooltip="Last row"
|
tooltip="Last row"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
class="last"
|
class="last"
|
||||||
@click="$emit('input', total - 1)"
|
@click="$emit('update:modelValue', total - 1)"
|
||||||
>
|
>
|
||||||
<edge-arrow-icon :disabled="false" />
|
<edge-arrow-icon :disabled="false" />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
@@ -51,7 +51,7 @@ export default {
|
|||||||
EdgeArrowIcon
|
EdgeArrowIcon
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: Number,
|
modelValue: Number,
|
||||||
total: Number
|
total: Number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RowNavigator from './RowNavigator.vue'
|
import RowNavigator from './RowNavigator.vue'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { RowNavigator },
|
components: { RowNavigator },
|
||||||
@@ -59,6 +60,7 @@ export default {
|
|||||||
rowIndex: { type: Number, default: 0 },
|
rowIndex: { type: Number, default: 0 },
|
||||||
selectedColumnIndex: Number
|
selectedColumnIndex: Number
|
||||||
},
|
},
|
||||||
|
emits: ['updateSelectedCell'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
selectedCellElement: null,
|
selectedCellElement: null,
|
||||||
@@ -84,7 +86,7 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
async currentRowIndex () {
|
async currentRowIndex () {
|
||||||
await this.$nextTick()
|
await nextTick()
|
||||||
if (this.selectedCellElement) {
|
if (this.selectedCellElement) {
|
||||||
const previouslySelected = this.selectedCellElement
|
const previouslySelected = this.selectedCellElement
|
||||||
this.selectCell(null)
|
this.selectCell(null)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { codemirror } from 'vue-codemirror'
|
import Codemirror from 'codemirror-editor-vue3'
|
||||||
import 'codemirror/lib/codemirror.css'
|
import 'codemirror/lib/codemirror.css'
|
||||||
import 'codemirror/mode/javascript/javascript.js'
|
import 'codemirror/mode/javascript/javascript.js'
|
||||||
import 'codemirror/addon/fold/foldcode.js'
|
import 'codemirror/addon/fold/foldcode.js'
|
||||||
@@ -54,7 +54,7 @@ import Logs from '@/components/Logs'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
codemirror,
|
Codemirror,
|
||||||
Logs
|
Logs
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -192,15 +192,15 @@ export default {
|
|||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .vue-codemirror {
|
:deep(.vue-codemirror) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
>>> .CodeMirror {
|
:deep(.CodeMirror) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
>>> .CodeMirror-cursor {
|
:deep(.CodeMirror-cursor) {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: var(--color-text-base);
|
background: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,11 @@
|
|||||||
@cancel="cancelCopy"
|
@cancel="cancelCopy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<teleport :to="resultSetTeleportTarget">
|
<teleport
|
||||||
|
defer
|
||||||
|
:to="resultSetTeleportTarget"
|
||||||
|
:disabled="!enableTeleport"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-show="result === null && !isGettingResults && !error"
|
v-show="result === null && !isGettingResults && !error"
|
||||||
@@ -138,7 +142,6 @@ import cIo from '@/lib/utils/clipboardIo'
|
|||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import loadingDialog from '@/components/LoadingDialog'
|
import loadingDialog from '@/components/LoadingDialog'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
import Teleport from 'vue2-teleport'
|
|
||||||
import ValueViewer from './ValueViewer'
|
import ValueViewer from './ValueViewer'
|
||||||
import Record from './Record/index.vue'
|
import Record from './Record/index.vue'
|
||||||
|
|
||||||
@@ -151,6 +154,7 @@ export default {
|
|||||||
error: Object,
|
error: Object,
|
||||||
time: [String, Number]
|
time: [String, Number]
|
||||||
},
|
},
|
||||||
|
emits: ['switchTo'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
resizeObserver: null,
|
resizeObserver: null,
|
||||||
@@ -161,7 +165,8 @@ export default {
|
|||||||
selectedCell: null,
|
selectedCell: null,
|
||||||
viewRecord: false,
|
viewRecord: false,
|
||||||
defaultPage: 1,
|
defaultPage: 1,
|
||||||
defaultSelectedCell: null
|
defaultSelectedCell: null,
|
||||||
|
enableTeleport: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -177,11 +182,13 @@ export default {
|
|||||||
loadingDialog,
|
loadingDialog,
|
||||||
ValueViewer,
|
ValueViewer,
|
||||||
Record,
|
Record,
|
||||||
Splitpanes,
|
Splitpanes
|
||||||
Teleport
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
resultSetTeleportTarget () {
|
resultSetTeleportTarget () {
|
||||||
|
if (!this.enableTeleport) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
const base = `#${this.viewValuePanelVisible
|
const base = `#${this.viewValuePanelVisible
|
||||||
? 'run-result-left-pane'
|
? 'run-result-left-pane'
|
||||||
: 'run-result-result-set'
|
: 'run-result-result-set'
|
||||||
@@ -190,12 +197,21 @@ export default {
|
|||||||
return base + tabIdPostfix
|
return base + tabIdPostfix
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activated () {
|
||||||
|
this.enableTeleport = true
|
||||||
|
},
|
||||||
|
deactivated () {
|
||||||
|
this.enableTeleport = false
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
if (this.$store.state.isWorkspaceVisible) {
|
||||||
|
this.enableTeleport = true
|
||||||
|
}
|
||||||
this.resizeObserver = new ResizeObserver(this.handleResize)
|
this.resizeObserver = new ResizeObserver(this.handleResize)
|
||||||
this.resizeObserver.observe(this.$refs.runResultPanel)
|
this.resizeObserver.observe(this.$refs.runResultPanel)
|
||||||
this.calculatePageSize()
|
this.calculatePageSize()
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeUnmount () {
|
||||||
this.resizeObserver.unobserve(this.$refs.runResultPanel)
|
this.resizeObserver.unobserve(this.$refs.runResultPanel)
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -361,7 +377,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>>>.vm--container {
|
:deep(.vm--container) {
|
||||||
animation: show-modal 1s linear 0s 1;
|
animation: show-modal 1s linear 0s 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
:active="panel === 'sqlEditor'"
|
:active="panel === 'sqlEditor'"
|
||||||
tooltip="Switch panel to SQL editor"
|
tooltip="Switch panel to SQL editor"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
@click.native="$emit('switchTo', 'sqlEditor')"
|
@click="$emit('switchTo', 'sqlEditor')"
|
||||||
>
|
>
|
||||||
<sql-editor-icon />
|
<sql-editor-icon />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
:active="panel === 'table'"
|
:active="panel === 'table'"
|
||||||
tooltip="Switch panel to result set"
|
tooltip="Switch panel to result set"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
@click.native="$emit('switchTo', 'table')"
|
@click="$emit('switchTo', 'table')"
|
||||||
>
|
>
|
||||||
<table-icon/>
|
<table-icon/>
|
||||||
</icon-button>
|
</icon-button>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
:active="panel === 'dataView'"
|
:active="panel === 'dataView'"
|
||||||
tooltip="Switch panel to data view"
|
tooltip="Switch panel to data view"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
@click.native="$emit('switchTo', 'dataView')"
|
@click="$emit('switchTo', 'dataView')"
|
||||||
>
|
>
|
||||||
<data-view-icon />
|
<data-view-icon />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
@@ -42,6 +42,7 @@ import DataViewIcon from '@/components/svg/dataView'
|
|||||||
export default {
|
export default {
|
||||||
name: 'SideToolBar',
|
name: 'SideToolBar',
|
||||||
props: ['panel'],
|
props: ['panel'],
|
||||||
|
emits: ['switchTo'],
|
||||||
components: {
|
components: {
|
||||||
IconButton,
|
IconButton,
|
||||||
SqlEditorIcon,
|
SqlEditorIcon,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function showHintOnDemand (editor) {
|
|||||||
CM.showHint(editor, getHints, hintOptions)
|
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
|
// Don't show autocomplete after a space or semicolon or in string literals
|
||||||
const token = editor.getTokenAt(editor.getCursor())
|
const token = editor.getTokenAt(editor.getCursor())
|
||||||
const ch = token.string.slice(-1)
|
const ch = token.string.slice(-1)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sql-editor-panel">
|
<div class="sql-editor-panel">
|
||||||
<div class="codemirror-container">
|
<div class="codemirror-container original-style">
|
||||||
<codemirror
|
<codemirror
|
||||||
ref="cm"
|
ref="cm"
|
||||||
v-model="query"
|
v-model:value="query"
|
||||||
:options="cmOptions"
|
:options="cmOptions"
|
||||||
@changes="onChange"
|
:original-style="true"
|
||||||
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<side-tool-bar panel="sqlEditor" @switchTo="$emit('switchTo', $event)">
|
<side-tool-bar panel="sqlEditor" @switchTo="$emit('switchTo', $event)">
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import showHint, { showHintOnDemand } from './hint'
|
import showHint, { showHintOnDemand } from './hint'
|
||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import { codemirror } from 'vue-codemirror'
|
import Codemirror from 'codemirror-editor-vue3'
|
||||||
import 'codemirror/lib/codemirror.css'
|
import 'codemirror/lib/codemirror.css'
|
||||||
import 'codemirror/mode/sql/sql.js'
|
import 'codemirror/mode/sql/sql.js'
|
||||||
import 'codemirror/theme/neo.css'
|
import 'codemirror/theme/neo.css'
|
||||||
@@ -37,16 +38,17 @@ import RunIcon from '@/components/svg/run'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SqlEditor',
|
name: 'SqlEditor',
|
||||||
props: ['value', 'isGettingResults'],
|
props: ['modelValue', 'isGettingResults'],
|
||||||
|
emits: ['update:modelValue', 'run', 'switchTo'],
|
||||||
components: {
|
components: {
|
||||||
codemirror,
|
Codemirror,
|
||||||
SideToolBar,
|
SideToolBar,
|
||||||
IconButton,
|
IconButton,
|
||||||
RunIcon
|
RunIcon
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
query: this.value,
|
query: this.modelValue,
|
||||||
cmOptions: {
|
cmOptions: {
|
||||||
tabSize: 4,
|
tabSize: 4,
|
||||||
mode: 'text/x-mysql',
|
mode: 'text/x-mysql',
|
||||||
@@ -54,6 +56,7 @@ export default {
|
|||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
line: true,
|
line: true,
|
||||||
autoRefresh: true,
|
autoRefresh: true,
|
||||||
|
styleActiveLine: false,
|
||||||
extraKeys: { 'Ctrl-Space': showHintOnDemand }
|
extraKeys: { 'Ctrl-Space': showHintOnDemand }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,13 +68,13 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
query () {
|
query () {
|
||||||
this.$emit('input', this.query)
|
this.$emit('update:modelValue', this.query)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onChange: time.debounce(showHint, 400),
|
onChange: time.debounce(showHint, 400),
|
||||||
focus () {
|
focus () {
|
||||||
this.$refs.cm.codemirror.focus()
|
this.$refs.cm.cminstance.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,15 +95,15 @@ export default {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> .vue-codemirror {
|
:deep(.vue-codemirror) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
>>> .CodeMirror {
|
:deep(.CodeMirror) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
>>> .CodeMirror-cursor {
|
:deep(.CodeMirror-cursor) {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: var(--color-text-base);
|
background: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
|
|
||||||
<div :id="'hidden-'+ tab.id" class="hidden-part" />
|
<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
|
<sql-editor
|
||||||
ref="sqlEditor"
|
ref="sqlEditor"
|
||||||
v-model="tab.query"
|
v-model="tab.query"
|
||||||
@@ -27,7 +31,11 @@
|
|||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
|
||||||
<teleport :to="`#${tab.layout.table}-${tab.id}`">
|
<teleport
|
||||||
|
defer
|
||||||
|
:to="enableTeleport ? `#${tab.layout.table}-${tab.id}`: undefined"
|
||||||
|
:disabled="!enableTeleport"
|
||||||
|
>
|
||||||
<run-result
|
<run-result
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:result="tab.result"
|
:result="tab.result"
|
||||||
@@ -38,7 +46,11 @@
|
|||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
|
||||||
<teleport :to="`#${tab.layout.dataView}-${tab.id}`">
|
<teleport
|
||||||
|
defer
|
||||||
|
:to="enableTeleport ? `#${tab.layout.dataView}-${tab.id}` : undefined"
|
||||||
|
:disabled="!enableTeleport"
|
||||||
|
>
|
||||||
<data-view
|
<data-view
|
||||||
:data-source="(tab.result && tab.result.values) || null"
|
:data-source="(tab.result && tab.result.values) || null"
|
||||||
:init-options="tab.viewOptions"
|
:init-options="tab.viewOptions"
|
||||||
@@ -56,8 +68,8 @@ import Splitpanes from '@/components/Splitpanes'
|
|||||||
import SqlEditor from './SqlEditor'
|
import SqlEditor from './SqlEditor'
|
||||||
import DataView from './DataView'
|
import DataView from './DataView'
|
||||||
import RunResult from './RunResult'
|
import RunResult from './RunResult'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
import Teleport from 'vue2-teleport'
|
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -65,18 +77,19 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
tab: Object
|
tab: Object
|
||||||
},
|
},
|
||||||
|
emits: [],
|
||||||
components: {
|
components: {
|
||||||
SqlEditor,
|
SqlEditor,
|
||||||
DataView,
|
DataView,
|
||||||
RunResult,
|
RunResult,
|
||||||
Splitpanes,
|
Splitpanes
|
||||||
Teleport
|
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
topPaneSize: this.tab.maximize
|
topPaneSize: this.tab.maximize
|
||||||
? this.tab.layout[this.tab.maximize] === 'above' ? 100 : 0
|
? this.tab.layout[this.tab.maximize] === 'above' ? 100 : 0
|
||||||
: 50
|
: 50,
|
||||||
|
enableTeleport: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -89,7 +102,7 @@ export default {
|
|||||||
immediate: true,
|
immediate: true,
|
||||||
async handler () {
|
async handler () {
|
||||||
if (this.isActive) {
|
if (this.isActive) {
|
||||||
await this.$nextTick()
|
await nextTick()
|
||||||
this.$refs.sqlEditor.focus()
|
this.$refs.sqlEditor.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +114,16 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activated () {
|
||||||
|
this.enableTeleport = true
|
||||||
|
},
|
||||||
|
deactivated () {
|
||||||
|
this.enableTeleport = false
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
if (this.$store.state.isWorkspaceVisible) {
|
||||||
|
this.enableTeleport = true
|
||||||
|
}
|
||||||
this.tab.dataView = this.$refs.dataView
|
this.tab.dataView = this.$refs.dataView
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -23,13 +23,13 @@
|
|||||||
:tab="tab"
|
:tab="tab"
|
||||||
/>
|
/>
|
||||||
<div v-show="tabs.length === 0" id="start-guide">
|
<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
|
new inquiry from scratch or open one from
|
||||||
<router-link class="link" to="/inquiries">Inquiries</router-link>
|
<router-link class="link" to="/inquiries">Inquiries</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Close tab warning dialog -->
|
<!--Close tab warning dialog -->
|
||||||
<modal name="close-warn" classes="dialog" height="auto">
|
<modal modal-id="close-warn" class="dialog">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
Close tab {{
|
Close tab {{
|
||||||
closingTab !== null
|
closingTab !== null
|
||||||
@@ -59,8 +59,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import Tab from './Tab'
|
import Tab from './Tab'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
|
import eventBus from '@/lib/eventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
emits: [],
|
||||||
components: {
|
components: {
|
||||||
Tab,
|
Tab,
|
||||||
CloseIcon
|
CloseIcon
|
||||||
@@ -82,6 +84,9 @@ export default {
|
|||||||
window.addEventListener('beforeunload', this.leavingSqliteviz)
|
window.addEventListener('beforeunload', this.leavingSqliteviz)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
emitCreateTabEvent () {
|
||||||
|
eventBus.$emit('createNewInquiry')
|
||||||
|
},
|
||||||
leavingSqliteviz (event) {
|
leavingSqliteviz (event) {
|
||||||
if (this.tabs.some(tab => !tab.isSaved)) {
|
if (this.tabs.some(tab => !tab.isSaved)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -104,14 +109,14 @@ export default {
|
|||||||
this.$store.commit('deleteTab', tab)
|
this.$store.commit('deleteTab', tab)
|
||||||
},
|
},
|
||||||
saveAndClose (tab) {
|
saveAndClose (tab) {
|
||||||
this.$root.$on('inquirySaved', () => {
|
eventBus.$on('inquirySaved', () => {
|
||||||
this.closeTab(tab)
|
this.closeTab(tab)
|
||||||
this.$root.$off('inquirySaved')
|
eventBus.$off('inquirySaved')
|
||||||
})
|
})
|
||||||
this.selectTab(tab.id)
|
this.selectTab(tab.id)
|
||||||
this.$modal.hide('close-warn')
|
this.$modal.hide('close-warn')
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$root.$emit('saveInquiry')
|
eventBus.$emit('saveInquiry')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ export default {
|
|||||||
|
|
||||||
events.send('inquiry.create', null, { auto: true })
|
events.send('inquiry.create', null, { auto: true })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
activated () {
|
||||||
|
this.$store.commit('setIsWorkspaceVisible', true)
|
||||||
|
},
|
||||||
|
deactivated () {
|
||||||
|
this.$store.commit('setIsWorkspaceVisible', false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<main-menu />
|
<main-menu />
|
||||||
<keep-alive include="Workspace,Inquiries">
|
<router-view id="main-view" v-slot="{ Component }">
|
||||||
<router-view id="main-view" />
|
<keep-alive include="Workspace,Inquiries">
|
||||||
</keep-alive>
|
<component :is="Component"/>
|
||||||
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ export default {
|
|||||||
margin-top: 42px;
|
margin-top: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>>.drop-area {
|
:deep(.drop-area) {
|
||||||
width: 706px;
|
width: 706px;
|
||||||
height: 482px;
|
height: 482px;
|
||||||
padding: 0 150px;
|
padding: 0 150px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
>>>.drop-area .text {
|
:deep(.drop-area .text) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 42px;
|
bottom: 42px;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('tab.js', () => {
|
|||||||
error: null,
|
error: null,
|
||||||
time: 0,
|
time: 0,
|
||||||
isSaved: false,
|
isSaved: false,
|
||||||
state: state
|
state
|
||||||
})
|
})
|
||||||
expect(newTab.layout).to.include({
|
expect(newTab.layout).to.include({
|
||||||
sqlEditor: 'above',
|
sqlEditor: 'above',
|
||||||
@@ -59,7 +59,7 @@ describe('tab.js', () => {
|
|||||||
error: null,
|
error: null,
|
||||||
time: 0,
|
time: 0,
|
||||||
isSaved: true,
|
isSaved: true,
|
||||||
state: state
|
state
|
||||||
})
|
})
|
||||||
expect(newTab.layout).to.include({
|
expect(newTab.layout).to.include({
|
||||||
sqlEditor: 'above',
|
sqlEditor: 'above',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('actions', () => {
|
|||||||
|
|
||||||
let id = await addTab({ state })
|
let id = await addTab({ state })
|
||||||
expect(state.tabs[0]).to.include({
|
expect(state.tabs[0]).to.include({
|
||||||
id: id,
|
id,
|
||||||
name: null,
|
name: null,
|
||||||
tempName: 'Untitled',
|
tempName: 'Untitled',
|
||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
@@ -23,7 +23,7 @@ describe('actions', () => {
|
|||||||
|
|
||||||
id = await addTab({ state })
|
id = await addTab({ state })
|
||||||
expect(state.tabs[1]).to.include({
|
expect(state.tabs[1]).to.include({
|
||||||
id: id,
|
id,
|
||||||
name: null,
|
name: null,
|
||||||
tempName: 'Untitled 1',
|
tempName: 'Untitled 1',
|
||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe('MainMenu.vue', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
sinon.restore()
|
sinon.restore()
|
||||||
|
|
||||||
// We need explicitly destroy the component, so that beforeDestroy hook was called
|
// We need explicitly destroy the component, so that beforeUnmount hook was called
|
||||||
// It's important because in this hook MainMenu component removes keydown event listener.
|
// It's important because in this hook MainMenu component removes keydown event listener.
|
||||||
wrapper.destroy()
|
wrapper.destroy()
|
||||||
})
|
})
|
||||||
@@ -497,7 +497,7 @@ describe('MainMenu.vue', () => {
|
|||||||
|
|
||||||
// check that the tab was updated
|
// check that the tab was updated
|
||||||
expect(mutations.updateTab.calledOnceWith(state, sinon.match({
|
expect(mutations.updateTab.calledOnceWith(state, sinon.match({
|
||||||
tab: tab,
|
tab,
|
||||||
newValues: {
|
newValues: {
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { _getDataSources, getPivotCanvas, getPivotHtml }
|
import { _getDataSources, getPivotCanvas, getPivotHtml }
|
||||||
from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
|
from '@/views/Main/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
|
||||||
|
|
||||||
describe('pivotHelper.js', () => {
|
describe('pivotHelper.js', () => {
|
||||||
it('_getDataSources returns data sources', () => {
|
it('_getDataSources returns data sources', () => {
|
||||||
|
|||||||
@@ -1,36 +1,45 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
const CopyPlugin = require('copy-webpack-plugin')
|
const CopyPlugin = require('copy-webpack-plugin')
|
||||||
const WorkboxPlugin = require('workbox-webpack-plugin')
|
const WorkboxPlugin = require('workbox-webpack-plugin')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true,
|
||||||
publicPath: '',
|
publicPath: '',
|
||||||
// Workaround for https://github.com/vuejs/vue-cli/issues/5399 as described
|
// Workaround for https://github.com/vuejs/vue-cli/issues/5399 as described
|
||||||
// in https://stackoverflow.com/a/63185174
|
// in https://stackoverflow.com/a/63185174
|
||||||
lintOnSave: process.env.NODE_ENV === 'development',
|
lintOnSave: process.env.NODE_ENV === 'development',
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyPlugin([
|
new CopyPlugin({
|
||||||
// This wasm file will be fetched dynamically when we initialize sql.js
|
patterns: [
|
||||||
// It is important that we do not change its name,
|
// This wasm file will be fetched dynamically when we initialize sql.js
|
||||||
// and that it is in the same folder as the js
|
// It is important that we do not change its name,
|
||||||
{ from: 'node_modules/sql.js/dist/sql-wasm.wasm', to: 'js/' },
|
// and that it is in the same folder as the js
|
||||||
{ from: 'LICENSE', to: './' }
|
{ from: 'node_modules/sql.js/dist/sql-wasm.wasm', to: 'js/' },
|
||||||
]),
|
{ from: 'LICENSE', to: './' }
|
||||||
|
]
|
||||||
|
}),
|
||||||
new WorkboxPlugin.GenerateSW({
|
new WorkboxPlugin.GenerateSW({
|
||||||
exclude: [/\.map$/, 'LICENSE', 'inquiries.json'],
|
exclude: [/\.map$/, 'LICENSE', 'inquiries.json'],
|
||||||
clientsClaim: true,
|
clientsClaim: true,
|
||||||
skipWaiting: false,
|
skipWaiting: false,
|
||||||
maximumFileSizeToCacheInBytes: 40000000
|
maximumFileSizeToCacheInBytes: 40000000
|
||||||
})
|
})
|
||||||
]
|
],
|
||||||
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
asset: require.resolve('assert'),
|
||||||
|
stream: require.resolve('stream-browserify')
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
chainWebpack: config => {
|
chainWebpack: config => {
|
||||||
const svgRule = config.module.rule('svg')
|
config.module
|
||||||
svgRule.uses.clear()
|
.rule('images')
|
||||||
svgRule
|
.set('parser', {
|
||||||
.use('url-loader')
|
dataUrlCondition: {
|
||||||
.loader('url-loader')
|
maxSize: 10000
|
||||||
.options({
|
}
|
||||||
limit: 10000
|
|
||||||
})
|
})
|
||||||
|
|
||||||
config.module
|
config.module
|
||||||
@@ -49,4 +58,4 @@ module.exports = {
|
|||||||
return args
|
return args
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user