1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00

add predefined queries

This commit is contained in:
lana-k
2020-11-04 19:13:27 +01:00
parent fec8fb5ac0
commit 1037185a6a
12 changed files with 359 additions and 87 deletions

47
public/queries.json Normal file
View File

@@ -0,0 +1,47 @@
{
"query": "select * from invoices",
"chart": {
"data": [
{
"type": "scatter",
"mode": "lines",
"x": null,
"xsrc": "InvoiceId",
"meta": {
"columnNames": {
"x": "InvoiceId",
"y": "Total"
}
},
"y": null,
"ysrc": "Total"
}
],
"layout": {
"xaxis": {
"range": [
1,
412
],
"autorange": true,
"type": "linear"
},
"yaxis": {
"range": [
-0.39166666666666683,
27.241666666666667
],
"autorange": true,
"type": "linear"
},
"autosize": true,
"mapbox": {
"style": "open-street-map"
}
},
"frames": []
},
"name": "Invoices",
"id": "ieZfcITwDUTADwOmQlYyL",
"createdAt": "2020-11-03T14:17:49.524Z"
}

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 9H13V7H11V9ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20ZM12 2C10.6868 2 9.38642 2.25866 8.17317 2.7612C6.95991 3.26375 5.85752 4.00035 4.92893 4.92893C3.05357 6.8043 2 9.34784 2 12C2 14.6522 3.05357 17.1957 4.92893 19.0711C5.85752 19.9997 6.95991 20.7362 8.17317 21.2388C9.38642 21.7413 10.6868 22 12 22C14.6522 22 17.1957 20.9464 19.0711 19.0711C20.9464 17.1957 22 14.6522 22 12C22 10.6868 21.7413 9.38642 21.2388 8.17317C20.7362 6.95991 19.9997 5.85752 19.0711 4.92893C18.1425 4.00035 17.0401 3.26375 15.8268 2.7612C14.6136 2.25866 13.3132 2 12 2V2ZM11 17H13V11H11V17Z" fill="#A2B1C6"/>
</svg>

After

Width:  |  Height:  |  Size: 754 B

View File

@@ -35,11 +35,6 @@ export default {
checked: this.init checked: this.init
} }
}, },
watch: {
checked () {
this.$emit('change', this.checked)
}
},
methods: { methods: {
onClick () { onClick () {
this.checked = !this.checked this.checked = !this.checked

View File

@@ -8,21 +8,70 @@
<button <button
v-if="$store.state.tabs.length > 0" v-if="$store.state.tabs.length > 0"
class="primary" class="primary"
:disabled="$store.state.currentTab && !$store.state.currentTab.isUnsaved" :disabled="currentQuery && !currentQuery.isUnsaved"
@click="saveQuery" @click="checkQueryBeforeSave"
> >
Save Save
</button> </button>
<button class="primary" @click="createNewQuery">Create</button> <button class="primary" @click="createNewQuery">Create</button>
</div> </div>
<!--Save Query dialog -->
<modal name="save" classes="dialog" height="auto">
<div class="dialog-header">
Save query
<close-icon @click="$modal.hide('save')"/>
</div>
<div class="dialog-body">
<div v-show="isPredefined" id="save-note">
<img :src="require('@/assets/images/info.svg')">
Note: Predefined queries can't be edited.
That's why your modifications will be saved as a new query. Enter the name for it.
</div>
<text-field
label="Query name"
:error-msg="errorMsg"
v-model="name"
width="100%"
/>
</div>
<div class="dialog-buttons-container">
<button class="secondary" @click="$modal.hide('save')">Cancel</button>
<button class="primary" @click="saveQuery">Save</button>
</div>
</modal>
</nav> </nav>
</template> </template>
<script> <script>
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import TextField from '@/components/TextField'
import CloseIcon from '@/components/svg/close'
export default { export default {
name: 'MainMenu', name: 'MainMenu',
components: {
TextField,
CloseIcon
},
data () {
return {
name: '',
errorMsg: null
}
},
computed: {
currentQuery () {
return this.$store.state.currentTab
},
isPredefined () {
if (this.currentQuery) {
return this.currentQuery.isPredefined
} else {
return false
}
}
},
created () { created () {
this.$root.$on('createNewQuery', this.createNewQuery) this.$root.$on('createNewQuery', this.createNewQuery)
}, },
@@ -38,38 +87,69 @@ export default {
} }
this.$store.commit('addTab', tab) this.$store.commit('addTab', tab)
this.$store.commit('setCurrentTabId', tab.id) this.$store.commit('setCurrentTabId', tab.id)
this.$store.commit('updateUntitledLastIndex') },
checkQueryBeforeSave () {
this.errorMsg = null
const isFromScratch = !this.currentQuery.initName
if (isFromScratch || this.isPredefined) {
this.$modal.show('save')
} else {
this.saveQuery()
}
}, },
saveQuery () { saveQuery () {
const currentQuery = this.$store.state.currentTab const isFromScratch = !this.currentQuery.initName
const isFromScratch = !this.$store.state.currentTab.initName if ((isFromScratch || this.isPredefined) && !this.name) {
this.errorMsg = 'Query name can\'t be empty'
return
}
const dataSet = this.currentQuery.result
const tabView = this.currentQuery.view
// Prepare query
const value = { const value = {
id: currentQuery.id, id: this.isPredefined ? nanoid() : this.currentQuery.id,
query: currentQuery.query, query: this.currentQuery.query,
chart: currentQuery.getChartSatateForSave() chart: this.currentQuery.getChartSatateForSave(),
} name: (!this.isPredefined && this.currentQuery.initName) || this.name,
createdAt: new Date()
if (isFromScratch) {
value.name = prompt('query name')
// TODO: create dialog
this.$store.commit('updateTabName', { index: currentQuery.tabIndex, newName: value.name })
value.createdAt = new Date()
} else {
value.name = currentQuery.initName
} }
// Save query
let myQueries = JSON.parse(localStorage.getItem('myQueries')) let myQueries = JSON.parse(localStorage.getItem('myQueries'))
if (!myQueries) { if (!myQueries) {
myQueries = [value] myQueries = [value]
} else if (isFromScratch) { } else if (isFromScratch || this.isPredefined) {
myQueries.push(value) myQueries.push(value)
} else { } else {
const queryIndex = myQueries.findIndex(query => query.id === currentQuery.id) const queryIndex = myQueries.findIndex(query => query.id === this.currentQuery.id)
value.createdAt = myQueries[queryIndex].createdAt value.createdAt = myQueries[queryIndex].createdAt
myQueries[queryIndex] = value myQueries[queryIndex] = value
} }
localStorage.setItem('myQueries', JSON.stringify(myQueries)) localStorage.setItem('myQueries', JSON.stringify(myQueries))
currentQuery.isUnsaved = false
// Update tab
this.$store.commit('updateTab', {
index: this.currentQuery.tabIndex,
name: value.name,
id: value.id,
query: value.query,
chart: value.chart,
isUnsaved: false
})
// Restore data:
// e.g. if we save predefined query the tab will be created again
// (because of new id) and
// it will be without sql result and has default view - table.
// That's why we need to restore data and view
this.$nextTick(() => {
this.currentQuery.result = dataSet
this.currentQuery.view = tabView
})
// Hide dialog
this.$modal.hide('save')
} }
} }
} }
@@ -89,6 +169,7 @@ nav {
left: 0; left: 0;
width: 100vw; width: 100vw;
padding: 0 52px; padding: 0 52px;
z-index: 999;
} }
a { a {
font-size: 18px; font-size: 18px;
@@ -103,4 +184,13 @@ a.router-link-active {
button { button {
margin-left: 16px; margin-left: 16px;
} }
#save-note {
margin-bottom: 24px;
display: flex;
align-items: flex-start;
}
#save-note img {
margin: -3px 6px 0 0;
}
</style> </style>

View File

@@ -22,7 +22,7 @@
<table ref="table"> <table ref="table">
<thead> <thead>
<tr> <tr>
<th v-for="(th,index) in data.columns" :key="index" ref="th"> <th v-for="(th,index) in dataSet.columns" :key="index" ref="th">
<div class="cell-data" :style="cellStyle">{{ th }}</div> <div class="cell-data" :style="cellStyle">{{ th }}</div>
</th> </th>
</tr> </tr>
@@ -39,7 +39,7 @@
</div> </div>
<div class="table-footer"> <div class="table-footer">
<div class="table-footer-count"> <div class="table-footer-count">
{{ data.values.length}} {{data.values.length === 1 ? 'row' : 'rows'}} retrieved {{ dataSet.values.length}} {{dataSet.values.length === 1 ? 'row' : 'rows'}} retrieved
</div> </div>
<pager v-show="pageCount > 1" :page-count="pageCount" v-model="currentPage" /> <pager v-show="pageCount > 1" :page-count="pageCount" v-model="currentPage" />
</div> </div>
@@ -52,17 +52,18 @@ import Pager from '@/components/Pager'
export default { export default {
name: 'SqlTable', name: 'SqlTable',
components: { Pager }, components: { Pager },
props: ['data', 'height'], props: ['dataSet', 'height'],
data () { data () {
return { return {
header: null, header: null,
tableWidth: null, tableWidth: null,
currentPage: 1 currentPage: 1,
resizeObserver: null
} }
}, },
computed: { computed: {
cellStyle () { cellStyle () {
const eq = this.tableWidth / this.data.columns.length const eq = this.tableWidth / this.dataSet.columns.length
return { maxWidth: `${Math.max(eq, 100)}px` } return { maxWidth: `${Math.max(eq, 100)}px` }
}, },
@@ -70,11 +71,11 @@ export default {
return Math.max(Math.floor(this.height / 40), 20) return Math.max(Math.floor(this.height / 40), 20)
}, },
pageCount () { pageCount () {
return Math.ceil(this.data.values.length / this.pageSize) return Math.ceil(this.dataSet.values.length / this.pageSize)
}, },
currentPageData () { currentPageData () {
const start = (this.currentPage - 1) * this.pageSize const start = (this.currentPage - 1) * this.pageSize
return this.data.values.slice(start, start + this.pageSize) return this.dataSet.values.slice(start, start + this.pageSize)
} }
}, },
methods: { methods: {
@@ -94,12 +95,16 @@ export default {
} }
}, },
mounted () { mounted () {
new ResizeObserver(this.calculateHeadersWidth).observe(this.$refs.table) this.resizeObserver = new ResizeObserver(this.calculateHeadersWidth)
this.resizeObserver.observe(this.$refs.table)
this.calculateHeadersWidth() this.calculateHeadersWidth()
}, },
beforeDestroy () {
this.resizeObserver.unobserve(this.$refs.table)
},
watch: { watch: {
currentPageData: 'calculateHeadersWidth', currentPageData: 'calculateHeadersWidth',
data () { dataSet () {
this.currentPage = 1 this.currentPage = 1
} }
} }

View File

@@ -36,7 +36,7 @@
<div v-show="error" class="table-preview error"> <div v-show="error" class="table-preview error">
{{ error }} {{ error }}
</div> </div>
<sql-table v-if="result" :data="result" :height="tableViewHeight" /> <sql-table v-if="result" :data-set="result" :height="tableViewHeight" />
</div> </div>
<chart <chart
:visible="view === 'chart'" :visible="view === 'chart'"
@@ -59,8 +59,8 @@ import ViewSwitcher from '@/components/ViewSwitcher'
import Chart from '@/components/Chart' import Chart from '@/components/Chart'
export default { export default {
name: 'TabContent', name: 'Tab',
props: ['id', 'initName', 'initQuery', 'initChart', 'tabIndex'], props: ['id', 'initName', 'initQuery', 'initChart', 'tabIndex', 'isPredefined'],
components: { components: {
SqlEditor, SqlEditor,
SqlTable, SqlTable,
@@ -76,7 +76,8 @@ export default {
tableViewHeight: 0, tableViewHeight: 0,
isUnsaved: !this.initName, isUnsaved: !this.initName,
isGettingResults: false, isGettingResults: false,
error: null error: null,
resizeObserver: null
} }
}, },
computed: { computed: {
@@ -88,9 +89,13 @@ export default {
this.$store.commit('setCurrentTab', this) this.$store.commit('setCurrentTab', this)
}, },
mounted () { mounted () {
new ResizeObserver(this.handleResize).observe(this.$refs.bottomPane) this.resizeObserver = new ResizeObserver(this.handleResize)
this.resizeObserver.observe(this.$refs.bottomPane)
this.calculateTableHeight() this.calculateTableHeight()
}, },
beforeDestroy () {
this.resizeObserver.unobserve(this.$refs.bottomPane)
},
watch: { watch: {
isActive () { isActive () {
if (this.isActive) { if (this.isActive) {

View File

@@ -3,7 +3,7 @@
<div id="tabs__header" v-if="tabs.length > 0"> <div id="tabs__header" v-if="tabs.length > 0">
<div <div
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="tab.id" :key="index"
@click="selectTab(tab.id)" @click="selectTab(tab.id)"
:class="[{'tab__selected': (tab.id === selectedIndex)}, 'tab']" :class="[{'tab__selected': (tab.id === selectedIndex)}, 'tab']"
> >
@@ -29,13 +29,14 @@
</div> </div>
</div> </div>
</div> </div>
<tab-content <tab
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="tab.id" :key="tab.id"
:id="tab.id" :id="tab.id"
:init-name="tab.name" :init-name="tab.name"
:init-query="tab.query" :init-query="tab.query"
:init-chart="tab.chart" :init-chart="tab.chart"
:is-predefined="tab.isPredefined"
:tab-index="index" :tab-index="index"
/> />
<div v-if="tabs.length === 0" id="start-guide"> <div v-if="tabs.length === 0" id="start-guide">
@@ -47,11 +48,11 @@
</template> </template>
<script> <script>
import TabContent from '@/components/TabContent' import Tab from '@/components/Tab'
export default { export default {
components: { components: {
TabContent Tab
}, },
data () { data () {
return { return {
@@ -153,5 +154,6 @@ export default {
color: var(--color-accent); color: var(--color-accent);
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
white-space: nowrap;
} }
</style> </style>

View File

@@ -13,7 +13,7 @@ export default {
<style scoped> <style scoped>
.icon { .icon {
vertical-align: middle; vertical-align: middle;
margin: 0 0 0 12px; margin: 0 12px;
} }
.icon:hover path { .icon:hover path {

View File

@@ -13,7 +13,7 @@ export default {
<style scoped> <style scoped>
.icon { .icon {
vertical-align: middle; vertical-align: middle;
margin: 0 12px 0 6px; margin: 0 12px;
} }
.icon:hover path { .icon:hover path {

View File

@@ -11,7 +11,8 @@ export default new Vuex.Store({
tabs: [], tabs: [],
currentTab: null, currentTab: null,
currentTabId: null, currentTabId: null,
untitledLastIndex: 0 untitledLastIndex: 0,
predefinedQueries: []
}, },
mutations: { mutations: {
saveSchema (state, schema) { saveSchema (state, schema) {
@@ -25,14 +26,29 @@ export default new Vuex.Store({
}, },
addTab (state, tab) { addTab (state, tab) {
state.tabs.push(tab) state.tabs.push(tab)
if (!tab.name) {
state.untitledLastIndex += 1
}
}, },
updateTabName (state, { index, newName }) { updateTab (state, { index, name, id, query, chart, isUnsaved }) {
const tab = state.tabs[index] const tab = state.tabs[index]
tab.name = newName const oldId = tab.id
if (state.currentTabId === oldId) {
state.currentTabId = id
}
tab.id = id
if (name) { tab.name = name }
if (query) { tab.query = query }
if (chart) { tab.chart = chart }
if (isUnsaved !== undefined) { tab.isUnsaved = isUnsaved }
delete tab.isPredefined
Vue.set(state.tabs, index, tab) Vue.set(state.tabs, index, tab)
}, },
updateTabState (state, { index, newValue }) { updateTabState (state, { index, newValue }) {
console.log(index, newValue)
const tab = state.tabs[index] const tab = state.tabs[index]
tab.isUnsaved = newValue tab.isUnsaved = newValue
Vue.set(state.tabs, index, tab) Vue.set(state.tabs, index, tab)
@@ -45,6 +61,7 @@ export default new Vuex.Store({
state.currentTabId = state.tabs[index - 1].id state.currentTabId = state.tabs[index - 1].id
} else { } else {
state.currentTabId = null state.currentTabId = null
state.currentTab = null
state.untitledLastIndex = 0 state.untitledLastIndex = 0
} }
state.tabs.splice(index, 1) state.tabs.splice(index, 1)
@@ -55,12 +72,14 @@ export default new Vuex.Store({
setCurrentTab (state, tab) { setCurrentTab (state, tab) {
state.currentTab = tab state.currentTab = tab
}, },
updateUntitledLastIndex (state) { updatePredefinedQueries (state, queries) {
state.untitledLastIndex += 1 if (Array.isArray(queries)) {
state.predefinedQueries = queries
} else {
state.predefinedQueries = [queries]
}
} }
}, },
actions: { actions: {
},
modules: {
} }
}) })

View File

@@ -13,7 +13,35 @@ import '@/assets/styles/scrollbars.css'
export default { export default {
name: 'MainView', name: 'MainView',
components: { MainMenu } components: { MainMenu },
created () {
this.readPredefinedQueries()
.then(queries => {
this.$store.commit('updatePredefinedQueries', queries)
})
.catch(console.error)
},
methods: {
readPredefinedQueries () {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', './queries.json')
xhr.onload = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else {
reject(xhr.statusText)
}
}
}
xhr.onerror = () => {
reject(xhr.statusText)
}
xhr.send()
})
}
}
} }
</script> </script>
<style scoped> <style scoped>

View File

@@ -24,7 +24,7 @@
</button> </button>
<button <button
class="toolbar" class="toolbar"
v-show="selectedQueriesCount > 0" v-show="selectedNotPredefinedCount > 0"
@click="showDeleteDialog(selectedQueriesIds)" @click="showDeleteDialog(selectedQueriesIds)"
> >
Delete Delete
@@ -52,25 +52,31 @@
> >
<table ref="table"> <table ref="table">
<tbody> <tbody>
<tr v-for="(query, index) in showedQueries" :key="query.id" @click="openQuery(index)"> <tr
v-for="(query, index) in showedQueries"
:key="query.id"
:class="{ 'predefined': query.isPredefined }"
@click="openQuery(index)"
>
<td ref="name-td"> <td ref="name-td">
<div class="cell-data"> <div class="cell-data">
<check-box <check-box
ref="rowCheckBox" ref="rowCheckBox"
:init="selectAll || selectedQueriesIds.has(query.id)" :init="selectAll || selectedQueriesIds.has(query.id)"
@change="toggleRow($event, query.id)" @click="toggleRow($event, query.id)"
/> />
<div class="name">{{ query.name }}</div> <div class="name">{{ query.name }}</div>
<div class="badge">Predefined</div>
</div> </div>
</td> </td>
<td> <td>
<div class="second-column"> <div class="second-column">
<div class="date-container">{{ query.createdAt | date }}</div> <div class="date-container">{{ query.createdAt | date }}</div>
<div class="icons-container"> <div class="icons-container">
<rename-icon @click="showRenameDialog(query.id)" /> <rename-icon v-if="!query.isPredefined" @click="showRenameDialog(query.id)" />
<copy-icon @click="duplicateQuery(index)"/> <copy-icon @click="duplicateQuery(index)"/>
<export-icon @click="exportQuery(index)"/> <export-icon @click="exportQuery(index)"/>
<delete-icon @click="showDeleteDialog(query.id)"/> <delete-icon v-if="!query.isPredefined" @click="showDeleteDialog(query.id)"/>
</div> </div>
</div> </div>
</td> </td>
@@ -119,9 +125,13 @@
> >
Are you sure you want to delete Are you sure you want to delete
{{ deleteGroup {{ deleteGroup
? `${selectedQueriesCount} ${selectedQueriesCount > 1 ? 'queries' : 'query'}` ? `${selectedNotPredefinedCount} ${selectedNotPredefinedCount > 1 ? 'queries' : 'query'}`
: `"${queries[currentQueryIndex].name}"` : `"${queries[currentQueryIndex].name}"`
}}? }}?
<div v-show="selectedQueriesCount > selectedNotPredefinedCount" id="note">
<img :src="require('@/assets/images/info.svg')">
Note: Predefined queries you've selected won't be deleted
</div>
</div> </div>
<div class="dialog-buttons-container"> <div class="dialog-buttons-container">
<button class="secondary" @click="$modal.hide('delete')">Cancel</button> <button class="secondary" @click="$modal.hide('delete')">Cancel</button>
@@ -162,29 +172,49 @@ export default {
errorMsg: null, errorMsg: null,
selectedQueriesIds: new Set(), selectedQueriesIds: new Set(),
selectedQueriesCount: 0, selectedQueriesCount: 0,
selectedNotPredefinedCount: 0,
selectAll: false, selectAll: false,
deleteGroup: false deleteGroup: false,
resizeObserver: null
} }
}, },
computed: { computed: {
predefinedQueries () {
return this.$store.state.predefinedQueries.map(query => {
query.isPredefined = true
return query
})
},
predefinedQueriesIds () {
return new Set(this.predefinedQueries.map(query => query.id))
},
showedQueries () { showedQueries () {
if (!this.filter) { let showedQueries = this.allQueries
return this.queries if (this.filter) {
} else { showedQueries = showedQueries.filter(
return this.queries.filter(query => query.name.toUpperCase().indexOf(this.filter.toUpperCase()) >= 0) query => query.name.toUpperCase().indexOf(this.filter.toUpperCase()) >= 0
)
} }
return showedQueries
},
allQueries () {
return this.predefinedQueries.concat(this.queries)
}, },
currentQueryIndex () { currentQueryIndex () {
return this.queries.findIndex(query => query.id === this.currentQueryId) return this.queries.findIndex(query => query.id === this.currentQueryId)
} }
}, },
created () { created () {
this.queries = JSON.parse(localStorage.getItem('myQueries')) this.queries = JSON.parse(localStorage.getItem('myQueries')) || []
}, },
mounted () { mounted () {
new ResizeObserver(this.calcNameWidth).observe(this.$refs.table) this.resizeObserver = new ResizeObserver(this.calcNameWidth)
this.resizeObserver.observe(this.$refs.table)
this.calcNameWidth() this.calcNameWidth()
}, },
beforeDestroy () {
this.resizeObserver.unobserve(this.$refs.table)
},
filters: { filters: {
date (value) { date (value) {
if (!value) { if (!value) {
@@ -205,7 +235,7 @@ export default {
this.$refs['name-th'].style = `width: ${this.$refs['name-td'][0].offsetWidth}px` this.$refs['name-th'].style = `width: ${this.$refs['name-td'][0].offsetWidth}px`
}, },
openQuery (index) { openQuery (index) {
const tab = this.showedQueries[index] const tab = JSON.parse(JSON.stringify(this.showedQueries[index]))
tab.isUnsaved = false tab.isUnsaved = false
this.$store.commit('addTab', tab) this.$store.commit('addTab', tab)
this.$store.commit('setCurrentTabId', tab.id) this.$store.commit('setCurrentTabId', tab.id)
@@ -225,23 +255,33 @@ export default {
const currentQuery = this.queries[this.currentQueryIndex] const currentQuery = this.queries[this.currentQueryIndex]
currentQuery.name = this.newName currentQuery.name = this.newName
this.$set(this.queries, this.currentQueryIndex, currentQuery) this.$set(this.queries, this.currentQueryIndex, currentQuery)
this.$modal.hide('rename')
// update queries in local storage
this.saveQueriesInLocalStorage() this.saveQueriesInLocalStorage()
// update tab, if renamed query is opened
const tabIndex = this.findTabIndex(currentQuery.id) const tabIndex = this.findTabIndex(currentQuery.id)
if (tabIndex >= 0) { if (tabIndex >= 0) {
this.$store.commit('updateTabName', { index: tabIndex, newName: this.newName }) this.$store.commit('updateTab', {
index: tabIndex,
name: this.newName,
id: currentQuery.id
})
} }
// hide dialog
this.$modal.hide('rename')
}, },
duplicateQuery (index) { duplicateQuery (index) {
const newQuery = JSON.parse(JSON.stringify(this.showedQueries[index])) const newQuery = JSON.parse(JSON.stringify(this.showedQueries[index]))
newQuery.name = newQuery.name + ' Copy' newQuery.name = newQuery.name + ' Copy'
newQuery.id = nanoid() newQuery.id = nanoid()
newQuery.createdAt = new Date() newQuery.createdAt = new Date()
this.queries.push(newQuery) delete newQuery.isPredefined
if (this.selectAll) { if (this.selectAll) {
this.selectedQueriesIds.add(newQuery.id) this.selectedQueriesIds.add(newQuery.id)
this.selectedQueriesCount = this.selectedQueriesIds.size this.selectedQueriesCount = this.selectedQueriesIds.size
} }
this.queries.push(newQuery)
this.saveQueriesInLocalStorage() this.saveQueriesInLocalStorage()
}, },
showDeleteDialog (id) { showDeleteDialog (id) {
@@ -286,22 +326,18 @@ export default {
// single operation // single operation
if (typeof index === 'number') { if (typeof index === 'number') {
console.log('single')
data = JSON.parse(JSON.stringify(this.showedQueries[index])) data = JSON.parse(JSON.stringify(this.showedQueries[index]))
name = data.name name = data.name
delete data.id delete data.isPredefined
delete data.createdAt
} else { } else {
// group operation // group operation
data = this.selectAll data = this.selectAll
? JSON.parse(JSON.stringify(this.queries)) ? JSON.parse(JSON.stringify(this.allQueries))
: this.queries.filter(query => this.selectedQueriesIds.has(query.id)) : this.allQueries.filter(query => this.selectedQueriesIds.has(query.id))
name = 'My sqliteviz queries' name = 'My sqliteviz queries'
data.forEach(query => { data.forEach(query => delete query.isPredefined)
delete query.id
delete query.createdAt
})
} }
// export data to file // export data to file
const downloader = this.$refs.downloader const downloader = this.$refs.downloader
const json = JSON.stringify(data, null, 4) const json = JSON.stringify(data, null, 4)
@@ -323,14 +359,19 @@ export default {
} }
importedQueries.forEach(query => { importedQueries.forEach(query => {
const allQueriesIds = this.allQueries.map(query => query.id)
if (new Set(allQueriesIds).has(query.id)) {
query.id = nanoid() query.id = nanoid()
query.createdAt = new Date()
if (this.selectAll) {
this.selectedQueriesIds.add(query.id)
this.selectedQueriesCount = this.selectedQueriesIds.size
} }
}) })
if (this.selectAll) {
importedQueries.forEach(query => {
this.selectedQueriesIds.add(query.id)
})
this.selectedQueriesCount = this.selectedQueriesIds.size
}
this.queries = this.queries.concat(importedQueries) this.queries = this.queries.concat(importedQueries)
this.saveQueriesInLocalStorage() this.saveQueriesInLocalStorage()
this.$refs.importFile.value = null this.$refs.importFile.value = null
@@ -343,18 +384,30 @@ export default {
toggleSelectAll (checked) { toggleSelectAll (checked) {
this.selectAll = checked this.selectAll = checked
this.$refs.rowCheckBox.forEach(item => { item.checked = checked }) this.$refs.rowCheckBox.forEach(item => { item.checked = checked })
this.selectedQueriesIds = checked ? new Set(this.queries.map(query => query.id)) : new Set()
this.selectedQueriesIds = checked
? new Set(this.allQueries.map(query => query.id))
: new Set()
this.selectedQueriesCount = this.selectedQueriesIds.size this.selectedQueriesCount = this.selectedQueriesIds.size
this.selectedNotPredefinedCount = checked ? this.queries.length : 0
}, },
toggleRow (checked, id) { toggleRow (checked, id) {
const isPredefined = this.predefinedQueriesIds.has(id)
if (checked) { if (checked) {
this.selectedQueriesIds.add(id) this.selectedQueriesIds.add(id)
if (!isPredefined) {
this.selectedNotPredefinedCount += 1
}
} else { } else {
if (this.selectedQueriesIds.size === this.queries.length) { if (this.selectedQueriesIds.size === this.allQueries.length) {
this.$refs.mainCheckBox.checked = false this.$refs.mainCheckBox.checked = false
this.selectAll = false this.selectAll = false
} }
this.selectedQueriesIds.delete(id) this.selectedQueriesIds.delete(id)
if (!isPredefined) {
this.selectedNotPredefinedCount -= 1
}
} }
this.selectedQueriesCount = this.selectedQueriesIds.size this.selectedQueriesCount = this.selectedQueriesIds.size
} }
@@ -413,6 +466,7 @@ tbody .cell-data {
display: flex; display: flex;
align-items: center; align-items: center;
max-width: 100%; max-width: 100%;
width: 100%;
} }
tbody .cell-data div.name { tbody .cell-data div.name {
overflow: hidden; overflow: hidden;
@@ -438,6 +492,7 @@ tbody tr:hover td {
.icons-container { .icons-container {
display: none; display: none;
margin-right: -12px;
} }
.date-container { .date-container {
flex-shrink: 1; flex-shrink: 1;
@@ -457,11 +512,34 @@ a, #import-file {
button.toolbar { button.toolbar {
margin-right: 16px; margin-right: 16px;
} }
button label { button label {
display: block; display: block;
line-height: 36px; line-height: 36px;
} }
button label:hover { button label:hover {
cursor: pointer; cursor: pointer;
} }
.badge {
display: none;
background-color: var(--color-gray-light-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-small);
padding: 2px 6px;
font-size: 11px;
line-height: normal;
margin-left: 12px;
}
tbody tr.predefined:hover .badge {
display: block;
}
#note {
margin-top: 24px;
}
#note img {
vertical-align: middle;
}
</style> </style>