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

5 Commits

Author SHA1 Message Date
lana-k
3c456ef135 fix sql hint: read properties of undefined #99 2022-07-29 15:27:01 +02:00
lana-k
c713c713b7 fix paths #97 2022-07-20 23:15:14 +02:00
lana-k
babf0074c0 add artifact with source map #97 2022-07-20 22:49:26 +02:00
lana-k
e71e6700c1 improve events 2022-07-20 22:47:40 +02:00
lana-k
84e66b8167 Update README.md 2022-07-10 23:18:52 +02:00
19 changed files with 101 additions and 79 deletions

View File

@@ -24,10 +24,11 @@ jobs:
npm install
npm run build
- name: Create archive
- name: Create archives
run: |
cd dist
zip -9 -r dist.zip . -x "js/*.map" -x "/*.map"
zip -9 -r ../dist.zip . -x "js/*.map" -x "/*.map"
zip -9 -r ../dist_map.zip .
- name: Create Release Notes
run: |
@@ -39,6 +40,6 @@ jobs:
- name: Create release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/dist.zip"
artifacts: "dist.zip,dist_map.zip"
token: ${{ secrets.GITHUB_TOKEN }}
bodyFile: "CHANGELOG.md"

View File

@@ -21,7 +21,7 @@ https://user-images.githubusercontent.com/24638357/128249848-f8fab0f5-9add-46e0-
The latest release of sqliteviz is deployed on [sqliteviz.com/app][6].
## Wiki
For user documentation, check out sqliteviz [Wiki][7].
For user documentation, check out sqliteviz [documentation][7].
## Motivation
It's a kind of middleground between [Plotly Falcon][1] and [Redash][2].
@@ -35,7 +35,7 @@ It is built on top of [react-chart-editor][3], [PivotTable.js][12], [sql.js][4]
[4]: https://github.com/sql-js/sql.js
[5]: https://github.com/vuejs/vue
[6]: https://sqliteviz.com/app/
[7]: https://github.com/lana-k/sqliteviz/wiki
[7]: https://sqliteviz.com/docs
[8]: https://github.com/surmon-china/vue-codemirror#readme
[9]: https://www.papaparse.com/
[10]: https://github.com/lana-k/sqliteviz/wiki/Predefined-queries

View File

@@ -1,6 +1,6 @@
{
"name": "sqliteviz",
"version": "0.20.0",
"version": "0.21.1",
"license": "Apache-2.0",
"private": true,
"scripts": {

View File

@@ -112,7 +112,7 @@ import SqlTable from '@/components/SqlTable'
import Logs from '@/components/Logs'
import time from '@/lib/utils/time'
import fIo from '@/lib/utils/fileIo'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'CsvImport',
@@ -336,7 +336,7 @@ export default {
this.$store.commit('setCurrentTabId', tabId)
this.importCsvCompleted = false
this.$emit('finish')
send('inquiry.create', undefined, { auto: true })
events.send('inquiry.create', null, { auto: true })
}
}
}

View File

@@ -58,7 +58,7 @@ import fIo from '@/lib/utils/fileIo'
import ChangeDbIcon from '@/components/svg/changeDb'
import database from '@/lib/database'
import CsvImport from '@/components/CsvImport'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'DbUploader',
@@ -128,7 +128,7 @@ export default {
if (fIo.isDatabase(file)) {
this.loadDb(file)
} else {
send('database.import', file.size, {
events.send('database.import', file.size, {
from: 'csv',
new_db: true
})

View File

@@ -7,7 +7,7 @@ import Worker from './_worker.js'
// https://github.com/nolanlawson/promise-worker
import PromiseWorker from 'promise-worker'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
function getNewDatabase () {
const worker = new Worker()
@@ -79,7 +79,7 @@ class Database {
this.dbName = file ? fu.getFileName(file) : 'database'
this.refreshSchema()
send('database.import', file ? file.size : 0, {
events.send('database.import', file ? file.size : 0, {
from: file ? 'sqlite' : 'none',
new_db: true
})
@@ -121,7 +121,7 @@ class Database {
throw new Error(data.error)
}
fu.exportToFile(data, fileName)
send('database.export', data.byteLength, { to: 'sqlite' })
events.send('database.export', data.byteLength, { to: 'sqlite' })
}
async validateTableName (name) {

View File

@@ -1,6 +1,6 @@
import { nanoid } from 'nanoid'
import fu from '@/lib/utils/fileIo'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
import migration from './_migrations'
const migrate = migration._migrate
@@ -106,7 +106,7 @@ export default {
.then(str => {
const inquires = this.deserialiseInquiries(str)
send('inquiry.import', inquires.length)
events.send('inquiry.import', inquires.length)
return inquires
})
@@ -115,7 +115,7 @@ export default {
const jsonStr = this.serialiseInquiries(inquiryList)
fu.exportToFile(jsonStr, fileName)
send('inquiry.export', inquiryList.length)
events.send('inquiry.export', inquiryList.length)
},
async readPredefinedInquiries () {

View File

@@ -1,10 +1,12 @@
export function send (name, value, labels) {
const event = new CustomEvent('sqliteviz-app-event', {
detail: {
name,
value,
labels
}
})
window.dispatchEvent(event)
export default {
send (name, value, labels) {
const event = new CustomEvent('sqliteviz-app-event', {
detail: {
name,
value,
labels: labels || {}
}
})
window.dispatchEvent(event)
}
}

View File

@@ -1,4 +1,4 @@
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
let refresh = false
function invokeServiceWorkerUpdateFlow (registration) {
@@ -44,6 +44,6 @@ if ('serviceWorker' in navigator) {
})
window.addEventListener('appinstalled', () => {
send('pwa.install')
events.send('pwa.install')
})
}

View File

@@ -60,7 +60,7 @@ import TextField from '@/components/TextField'
import CloseIcon from '@/components/svg/close'
import storedInquiries from '@/lib/storedInquiries'
import AppDiagnosticInfo from './AppDiagnosticInfo'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'MainMenu',
@@ -115,7 +115,7 @@ export default {
}
})
send('inquiry.create', undefined, { auto: false })
events.send('inquiry.create', null, { auto: false })
},
cancelSave () {
this.$modal.hide('save')
@@ -169,7 +169,7 @@ export default {
// Signal about saving
this.$root.$emit('inquirySaved')
send('inquiry.save')
events.send('inquiry.save')
},
_keyListener (e) {
if (this.$route.path === '/workspace') {

View File

@@ -33,7 +33,7 @@
<script>
import fIo from '@/lib/utils/fileIo'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
import TableDescription from './TableDescription'
import TextField from '@/components/TextField'
import TreeChevron from '@/components/svg/treeChevron'
@@ -88,7 +88,7 @@ export default {
await csvImport.previewCsv()
csvImport.open()
send('database.import', this.file.size, {
events.send('database.import', this.file.size, {
from: 'csv',
new_db: false
})

View File

@@ -31,7 +31,7 @@ import PlotlyEditor from 'react-chart-editor'
import chartHelper from '@/lib/chartHelper'
import dereference from 'react-chart-editor/lib/lib/dereference'
import fIo from '@/lib/utils/fileIo'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'Chart',
@@ -66,11 +66,10 @@ export default {
notifyOnLogging: 1
})
this.$watch(
() => JSON.stringify(
this.state.data.map(trace => `${trace.type}-${trace.mode}`)
),
() => this.state.data.map(trace => `${trace.type}-${trace.mode}`)
.join(','),
(value) => {
send('viz_plotly.render', undefined, {
events.send('viz_plotly.render', null, {
type: value,
pivot: !!this.forPivot
})

View File

@@ -23,7 +23,7 @@ import pivotHelper from './pivotHelper'
import Chart from '@/views/Main/Workspace/Tabs/Tab/DataView/Chart'
import chartHelper from '@/lib/chartHelper'
import Vue from 'vue'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
const ChartClass = Vue.extend(Chart)
export default {
@@ -96,7 +96,7 @@ export default {
'update:importToSvgEnabled',
this.viewStandartChart || this.viewCustomChart
)
send('viz_pivot.render', undefined, {
events.send('viz_pivot.render', null, {
type: this.pivotOptions.rendererName
})
}

View File

@@ -95,7 +95,7 @@ import ClipboardIcon from '@/components/svg/clipboard'
import cIo from '@/lib/utils/clipboardIo'
import loadingDialog from '@/components/LoadingDialog'
import time from '@/lib/utils/time'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'DataView',
@@ -207,11 +207,11 @@ export default {
eventLabels.pivot = this.plotlyInPivot
}
send(
events.send(
this.mode === 'chart' || this.plotlyInPivot
? 'viz_plotly.export'
: 'viz_pivot.export',
undefined,
null,
eventLabels
)
}

View File

@@ -72,7 +72,7 @@ import fIo from '@/lib/utils/fileIo'
import cIo from '@/lib/utils/clipboardIo'
import time from '@/lib/utils/time'
import loadingDialog from '@/components/LoadingDialog'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'RunResult',
@@ -119,7 +119,7 @@ export default {
exportToCsv () {
if (this.result && this.result.values) {
send('resultset.export',
events.send('resultset.export',
this.result.values[this.result.columns[0]].length,
{ to: 'csv' }
)
@@ -130,7 +130,7 @@ export default {
async prepareCopy () {
if (this.result && this.result.values) {
send('resultset.export',
events.send('resultset.export',
this.result.values[this.result.columns[0]].length,
{ to: 'clipboard' }
)

View File

@@ -3,14 +3,20 @@ import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/sql-hint.js'
import store from '@/store'
function _getHintText (hint) {
return typeof hint === 'string' ? hint : hint.text
}
export function getHints (cm, options) {
const token = cm.getTokenAt(cm.getCursor()).string.toUpperCase()
const result = CM.hint.sql(cm, options)
// Don't show the hint if there is only one option
// and the token is already completed with this option
if (result.list.length === 1 && result.list[0].text.toUpperCase() === token) {
// and the replacingText is already equals to this option
const replacedText = cm.getRange(result.from, result.to).toUpperCase()
if (result.list.length === 1 &&
_getHintText(result.list[0]).toUpperCase() === replacedText) {
result.list = []
}
return result
}

View File

@@ -56,7 +56,7 @@ import DataView from './DataView'
import RunResult from './RunResult'
import time from '@/lib/utils/time'
import Teleport from 'vue2-teleport'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'Tab',
@@ -110,7 +110,7 @@ export default {
this.layout[from] = this.layout[to]
this.layout[to] = fromPosition
send('inquiry.panel', undefined, { panel: to })
events.send('inquiry.panel', null, { panel: to })
},
onDataViewUpdate () {
this.$store.commit('updateTab', { index: this.tabIndex, isSaved: false })
@@ -126,19 +126,19 @@ export default {
this.time = time.getPeriod(start, new Date())
if (this.result && this.result.values) {
send('resultset.create',
events.send('resultset.create',
this.result.values[this.result.columns[0]].length
)
}
send('query.run', parseFloat(this.time), { status: 'success' })
events.send('query.run', parseFloat(this.time), { status: 'success' })
} catch (err) {
this.error = {
type: 'error',
message: err
}
send('query.run', 0, { status: 'error' })
events.send('query.run', 0, { status: 'error' })
}
state.db.refreshSchema()
this.isGettingResults = false

View File

@@ -19,7 +19,7 @@
import Splitpanes from '@/components/Splitpanes'
import Schema from './Schema'
import Tabs from './Tabs'
import { send } from '@/lib/utils/events'
import events from '@/lib/utils/events'
export default {
name: 'Workspace',
@@ -51,7 +51,7 @@ export default {
const tabId = await this.$store.dispatch('addTab', { query: stmt })
this.$store.commit('setCurrentTabId', tabId)
send('inquiry.create', undefined, { auto: true })
events.send('inquiry.create', null, { auto: true })
}
}
}

View File

@@ -138,15 +138,37 @@ describe('hint.js', () => {
'getHints returns [ ] if there is only one option and token is completed with this option',
() => {
// mock CM.hint.sql and editor
sinon.stub(CM.hint, 'sql').returns({ list: [{ text: 'SELECT' }] })
sinon.stub(CM.hint, 'sql').returns({
list: [{ text: 'SELECT' }],
from: null, // from/to doesn't metter because getRange is mocked
to: null
})
const editor = {
getTokenAt () {
return {
string: 'select',
type: 'keyword'
}
},
getCursor: sinon.stub()
getRange () {
return 'select'
}
}
const hints = getHints(editor, {})
expect(hints.list).to.eql([])
}
)
it(
'getHints returns [ ] if there is only one string option and token ' +
'is completed with this option',
() => {
// mock CM.hint.sql and editor
sinon.stub(CM.hint, 'sql').returns({
list: ['house.name'],
from: null, // from/to doesn't metter because getRange is mocked
to: null
})
const editor = {
getRange () {
return 'house.name'
}
}
const hints = getHints(editor, {})
@@ -160,15 +182,11 @@ describe('hint.js', () => {
{ text: 'SELECT' },
{ text: 'ST' }
]
sinon.stub(CM.hint, 'sql').returns({ list })
sinon.stub(CM.hint, 'sql').returns({ list, from: null, to: null })
const editor = {
getTokenAt () {
return {
string: 'se',
type: 'keyword'
}
},
getCursor: sinon.stub()
getRange () {
return 'se'
}
}
const hints = getHints(editor, {})
@@ -182,15 +200,11 @@ describe('hint.js', () => {
() => {
// mock CM.hint.sql and editor
const list = [{ text: 'SELECT' }]
sinon.stub(CM.hint, 'sql').returns({ list })
sinon.stub(CM.hint, 'sql').returns({ list, from: null, to: null })
const editor = {
getTokenAt () {
return {
string: 'sele',
type: 'keyword'
}
},
getCursor: sinon.stub()
getRange () {
return 'sele'
}
}
const hints = getHints(editor, {})