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

adding and closing tabs; saving of queries

This commit is contained in:
lana-k
2020-10-07 16:03:36 +02:00
parent f898493d29
commit 4841e43a09
7 changed files with 191 additions and 27 deletions

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z" fill="#A2B1C6"/>
</svg>

After

Width:  |  Height:  |  Size: 232 B

View File

@@ -28,5 +28,9 @@ button.primary:disabled {
color: var(--color-text-light-2); color: var(--color-text-light-2);
text-shadow: none; text-shadow: none;
cursor: default; cursor: default;
} }
button.primary:focus {
outline: none;
}

View File

@@ -22,7 +22,6 @@
--color-text-light: var(--color-white); --color-text-light: var(--color-white);
--color-text-light-2: var(--color-gray-medium); --color-text-light-2: var(--color-gray-medium);
--color-text-base: var(--color-gray-dark); --color-text-base: var(--color-gray-dark);
--color-text-medium: var(--color-gray-medium);
--color-text-active: var(--color-blue-dark-2); --color-text-active: var(--color-blue-dark-2);
--shadow: 0 1px 2px rgba(42, 63, 95, 0.7); --shadow: 0 1px 2px rgba(42, 63, 95, 0.7);

View File

@@ -5,15 +5,68 @@
<router-link to="/my-queries">My queries</router-link> <router-link to="/my-queries">My queries</router-link>
</div> </div>
<div> <div>
<button class="primary" disabled>Save</button> <button
<button class="primary">Create</button> v-if="$store.state.tabs.length > 0"
class="primary"
:disabled="!$store.state.currentTab.isUnsaved"
@click="saveQuery"
>
Save
</button>
<button class="primary" @click="createNewQuery">Create</button>
</div> </div>
</nav> </nav>
</template> </template>
<script> <script>
export default { export default {
name: 'MainMenu' name: 'MainMenu',
methods: {
createNewQuery () {
const tab = {
id: Number(new Date()),
name: this.$store.state.untitledLastIndex === 3 ? 'Very good query' : null,
tempName: this.$store.state.untitledLastIndex
? `Untitled ${this.$store.state.untitledLastIndex}`
: 'Untitled',
isUnsaved: true
}
this.$store.commit('addTab', tab)
this.$store.commit('setCurrentTabId', tab.id)
this.$store.commit('updateUntitledLastIndex')
},
saveQuery () {
const currentQuery = this.$store.state.currentTab
const isFromScratch = !this.$store.state.currentTab.initName
const value = {
id: currentQuery.id,
query: currentQuery.query
// TODO: save plotly settings
}
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
}
let myQueries = JSON.parse(localStorage.getItem('myQueries'))
if (!myQueries) {
myQueries = [value]
} else if (isFromScratch) {
myQueries.push(value)
} else {
const queryIndex = myQueries.findIndex(query => query.id === currentQuery.id)
value.createdAt = myQueries[queryIndex].createdAt
myQueries[queryIndex] = value
}
localStorage.setItem('myQueries', JSON.stringify(myQueries))
currentQuery.isUnsaved = false
}
}
} }
</script> </script>
<style scoped> <style scoped>

View File

@@ -8,7 +8,7 @@
> >
<div slot="left-pane" class="query-editor"> <div slot="left-pane" class="query-editor">
<div class="codemirror-container"> <div class="codemirror-container">
<codemirror v-model="code" :options="cmOptions" @changes="onCmChange" /> <codemirror v-model="query" :options="cmOptions" @changes="onCmChange" ref="codemirror" />
</div> </div>
<div class="run-btn-container"> <div class="run-btn-container">
<button class="primary run-btn" @click="execEditorContents">Run</button> <button class="primary run-btn" @click="execEditorContents">Run</button>
@@ -59,7 +59,7 @@ import 'codemirror/addon/hint/sql-hint.js'
export default { export default {
name: 'TabContent', name: 'TabContent',
props: ['name', 'isActive'], props: ['id', 'initName', 'initQuery', 'initPlotly', 'tabIndex'],
components: { components: {
codemirror, codemirror,
SqlTable, SqlTable,
@@ -74,7 +74,7 @@ export default {
layout: {}, layout: {},
frames: [] frames: []
}, },
code: 'select * from albums', query: 'select * from albums',
cmOptions: { cmOptions: {
// codemirror options // codemirror options
tabSize: 4, tabSize: 4,
@@ -86,10 +86,14 @@ export default {
result: null, result: null,
view: 'table', view: 'table',
tableViewHeight: 0, tableViewHeight: 0,
worker: this.$store.state.worker worker: this.$store.state.worker,
isUnsaved: !this.name
} }
}, },
computed: { computed: {
isActive () {
return this.id === this.$store.state.currentTabId
},
dataSources () { dataSources () {
if (!this.result) { if (!this.result) {
return {} return {}
@@ -110,13 +114,30 @@ export default {
})) }))
} }
}, },
created () {
this.$store.commit('setCurrentTab', this)
},
mounted () { mounted () {
new ResizeObserver(this.calculateTableHeight).observe(this.$refs.bottomPane) new ResizeObserver(this.calculateTableHeight).observe(this.$refs.bottomPane)
this.calculateTableHeight() this.calculateTableHeight()
}, },
watch: {
isActive () {
if (this.isActive) {
this.$store.commit('setCurrentTab', this)
}
},
query () {
this.isUnsaved = true
},
isUnsaved () {
this.$store.commit('updateTabState', { index: this.tabIndex, newValue: this.isUnsaved })
}
},
methods: { methods: {
update (data, layout, frames) { update (data, layout, frames) {
this.state = { data, layout, frames } this.state = { data, layout, frames }
this.isUnsaved = true
console.log(this.state) console.log(this.state)
}, },
onCmChange (editor) { onCmChange (editor) {
@@ -154,7 +175,7 @@ export default {
// this.$refs.output.textContent = 'Fetching results...' // this.$refs.output.textContent = 'Fetching results...'
}, },
execEditorContents () { execEditorContents () {
this.execute(this.code + ';') this.execute(this.query + ';')
}, },
calculateTableHeight () { calculateTableHeight () {
const bottomPane = this.$refs.bottomPane const bottomPane = this.$refs.bottomPane

View File

@@ -2,19 +2,39 @@
<div> <div>
<div id="tabs__header"> <div id="tabs__header">
<div <div
v-for="tab in tabs" v-for="(tab, index) in tabs"
:key="tab.id" :key="tab.id"
@click="selectTab(tab.id)" @click="selectTab(tab.id)"
:class='{"tab__selected": (tab.id === selectedIndex)}' :class="[{'tab__selected': (tab.id === selectedIndex)}, 'tab']"
> >
{{ tab.name }} <div class="tab-name">
<span v-show="tab.isUnsaved">*</span>
<span v-if="tab.name">{{ tab.name }}</span>
<span v-else class="tab-untitled">{{ tab.tempName }}</span>
</div>
<div>
<svg
class="close-icon"
@click.stop="closeTab(index)"
width="10"
height="10"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z"
fill="#A2B1C6"/>
</svg>
</div>
</div> </div>
</div> </div>
<tab-content <tab-content
v-for="tab in tabs" v-for="(tab, index) in tabs"
:key="tab.id" :key="tab.id"
:is-active="tab.isActive" :id="tab.id"
:name="tab.name" :init-name="tab.name"
:tab-index="index"
/> />
</div> </div>
</template> </template>
@@ -28,19 +48,22 @@ export default {
}, },
data () { data () {
return { return {
selectedIndex: 0, }
tabs: [ },
{ id: 1, name: 'New query', isActive: true }, computed: {
{ id: 2, name: 'New query 2', isActive: false } tabs () {
] return this.$store.state.tabs
},
selectedIndex () {
return this.$store.state.currentTabId
} }
}, },
methods: { methods: {
selectTab (id) { selectTab (id) {
this.selectedIndex = id this.$store.commit('setCurrentTabId', id)
this.tabs.forEach(tab => { },
tab.isActive = (tab.id === id) closeTab (index) {
}) this.$store.commit('deleteTab', index)
} }
} }
} }
@@ -50,8 +73,10 @@ export default {
#tabs__header { #tabs__header {
display: flex; display: flex;
margin: 0; margin: 0;
max-width: 100%;
overflow: hidden;
} }
#tabs__header div { #tabs__header .tab {
height: 36px; height: 36px;
background-color: var(--color-bg-light); background-color: var(--color-bg-light);
border-right: 1px solid var(--color-border-light); border-right: 1px solid var(--color-border-light);
@@ -62,7 +87,18 @@ export default {
padding: 0 12px; padding: 0 12px;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
max-width: 200px;
display: flex;
flex-shrink: 1;
min-width: 0;
} }
#tabs__header .tab-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
}
#tabs__header div:hover { #tabs__header div:hover {
cursor: pointer; cursor: pointer;
} }
@@ -84,4 +120,13 @@ export default {
top: 0; top: 0;
left: 0; left: 0;
} }
.close-icon {
margin-left: 5px;
}
.close-icon:hover path {
fill: var(--color-text-base);
cursor: pointer;
}
</style> </style>

View File

@@ -8,7 +8,11 @@ export default new Vuex.Store({
schema: null, schema: null,
dbFile: null, dbFile: null,
dbName: null, dbName: null,
worker: new Worker('/js/worker.sql-wasm.js') worker: new Worker('/js/worker.sql-wasm.js'),
tabs: [],
currentTab: null,
currentTabId: null,
untitledLastIndex: 0
}, },
mutations: { mutations: {
saveSchema (state, schema) { saveSchema (state, schema) {
@@ -19,6 +23,41 @@ export default new Vuex.Store({
}, },
saveDbName (state, name) { saveDbName (state, name) {
state.dbName = name state.dbName = name
},
addTab (state, tab) {
state.tabs.push(tab)
},
updateTabName (state, { index, newName }) {
const tab = state.tabs[index]
tab.name = newName
Vue.set(state.tabs, index, tab)
},
updateTabState (state, { index, newValue }) {
console.log(index, newValue)
const tab = state.tabs[index]
tab.isUnsaved = newValue
Vue.set(state.tabs, index, tab)
},
deleteTab (state, index) {
if (state.tabs[index].id !== state.currentTabId) {
} else if (index < state.tabs.length - 1) {
state.currentTabId = state.tabs[index + 1].id
} else if (index > 0) {
state.currentTabId = state.tabs[index - 1].id
} else {
state.currentTabId = null
state.untitledLastIndex = 0
}
state.tabs.splice(index, 1)
},
setCurrentTabId (state, id) {
state.currentTabId = id
},
setCurrentTab (state, tab) {
state.currentTab = tab
},
updateUntitledLastIndex (state) {
state.untitledLastIndex += 1
} }
}, },
actions: { actions: {