1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2026-08-05 15:29:17 +08:00

Compare commits

..

8 Commits

Author SHA1 Message Date
lana-k
686a09a8ce fix import 2026-07-12 21:56:29 +02:00
lana-k
4e9d1a785c 0.30.1 2026-07-12 17:19:37 +02:00
lana-k
2b6de12dc5 fix: show custom chart settings in pivot #138 2026-07-12 17:19:07 +02:00
lana-k
39d1604959 fix: save chart settings wihtout data #137 2026-07-12 17:18:41 +02:00
lana-k
053b8fbecb wip 2026-07-08 20:39:43 +02:00
lana-k
1043a03c96 fix plotly patch file 2026-07-05 19:38:03 +02:00
lana-k
b6ec71338b fix plotly warnings see issue 7809 2026-07-05 19:12:24 +02:00
lana-k
4bb6676648 selection view wip 2026-05-25 17:50:57 +02:00
7 changed files with 222 additions and 34 deletions

6
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.29.0", "version": "0.30.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.29.0", "version": "0.30.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -24,7 +24,7 @@
"nanoid": "^3.1.12", "nanoid": "^3.1.12",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pivottable": "^2.23.0", "pivottable": "^2.23.0",
"plotly.js": "^2.35.2", "plotly.js": "^2.35.3",
"promise-worker": "^2.0.1", "promise-worker": "^2.0.1",
"react": "^16.14.0", "react": "^16.14.0",
"react-chart-editor": "^0.46.1", "react-chart-editor": "^0.46.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.30.0", "version": "0.30.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": true, "private": true,
"type": "module", "type": "module",
@@ -28,7 +28,7 @@
"nanoid": "^3.1.12", "nanoid": "^3.1.12",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pivottable": "^2.23.0", "pivottable": "^2.23.0",
"plotly.js": "^2.35.2", "plotly.js": "^2.35.3",
"promise-worker": "^2.0.1", "promise-worker": "^2.0.1",
"react": "^16.14.0", "react": "^16.14.0",
"react-chart-editor": "^0.46.1", "react-chart-editor": "^0.46.1",

View File

@@ -0,0 +1,26 @@
diff --git a/node_modules/plotly.js/dist/plotly.js b/node_modules/plotly.js/dist/plotly.js
index 28f5612..5b9956d 100644
--- a/node_modules/plotly.js/dist/plotly.js
+++ b/node_modules/plotly.js/dist/plotly.js
@@ -34384,6 +34384,9 @@ var layoutUIControlPatterns = [{
pattern: /^(shapes|annotations)/,
attr: 'editrevision'
}, {
+ pattern: /^selections/,
+ attr: 'selectionrevision'
+},{
pattern: /^title\.text$/,
attr: 'editrevision'
}];
diff --git a/node_modules/plotly.js/src/plot_api/plot_api.js b/node_modules/plotly.js/src/plot_api/plot_api.js
index 75e98f5..5c38053 100644
--- a/node_modules/plotly.js/src/plot_api/plot_api.js
+++ b/node_modules/plotly.js/src/plot_api/plot_api.js
@@ -2394,6 +2394,7 @@ var layoutUIControlPatterns = [
{pattern: /^legend\.(x|y)$/, attr: 'editrevision'},
{pattern: /^(shapes|annotations)/, attr: 'editrevision'},
+ {pattern: /^selections/, attr: 'selectionrevision'},
{pattern: /^title\.text$/, attr: 'editrevision'}
];

View File

@@ -1,13 +1,18 @@
<template> <template>
<div ref="chartContainer" class="chart-container"> <div class="chart-container">
<div v-show="!dataSources" class="warning data-view-warning"> <div v-show="!dataSources" class="warning data-view-warning">
There is no data to build a chart. Run your SQL query and make sure the There is no data to build a chart. Run your SQL query and make sure the
result is not empty. result is not empty.
</div> </div>
<div <splitpanes
:before="{ size: 70, max: 100 }"
:after="{ size: 30, max: 50, hidden: !showValueViewer }"
:default="{ before: 70, after: 30 }"
class="chart" class="chart"
:style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }" :style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }"
> >
<template #left-pane>
<div ref="chartContainer" :style="{ height: '100%' }">
<PlotlyEditor <PlotlyEditor
ref="plotlyEditor" ref="plotlyEditor"
:data="state.data" :data="state.data"
@@ -25,6 +30,16 @@
@render="onRender" @render="onRender"
/> />
</div> </div>
</template>
<template v-if="showValueViewer" #right-pane>
<value-viewer
:empty="!selectedItem"
emptyMessage="No points selected to view"
:value="JSON.stringify(selectedItem)"
defaultFormat="json"
/>
</template>
</splitpanes>
</div> </div>
</template> </template>
@@ -37,11 +52,16 @@ import chartHelper from '@/lib/chartHelper'
import * as dereference from 'react-chart-editor/lib/lib/dereference' import * as dereference from 'react-chart-editor/lib/lib/dereference'
import fIo from '@/lib/utils/fileIo' import fIo from '@/lib/utils/fileIo'
import events from '@/lib/utils/events' import events from '@/lib/utils/events'
import Splitpanes from '@/components/Common/Splitpanes'
import ValueViewer from '@/components/ValueViewer.vue'
import { nextTick } from 'vue'
export default { export default {
name: 'Chart', name: 'Chart',
components: { components: {
PlotlyEditor: applyPureReactInVue(ReactPlotlyEditorWithPlotRef) PlotlyEditor: applyPureReactInVue(ReactPlotlyEditorWithPlotRef),
Splitpanes,
ValueViewer
}, },
props: { props: {
dataSources: Object, dataSources: Object,
@@ -49,7 +69,8 @@ export default {
exportToPngEnabled: Boolean, exportToPngEnabled: Boolean,
exportToSvgEnabled: Boolean, exportToSvgEnabled: Boolean,
forPivot: Boolean, forPivot: Boolean,
showViewSettings: Boolean showViewSettings: Boolean,
showValueViewer: Boolean
}, },
emits: [ emits: [
'update:exportToSvgEnabled', 'update:exportToSvgEnabled',
@@ -60,7 +81,9 @@ export default {
data() { data() {
return { return {
plotly, plotly,
state: this.initOptions || { state: this.initOptions
? JSON.parse(JSON.stringify(this.initOptions))
: {
data: [], data: [],
layout: { autosize: true }, layout: { autosize: true },
frames: [] frames: []
@@ -71,7 +94,8 @@ export default {
modeBarButtonsToRemove: ['toImage'] modeBarButtonsToRemove: ['toImage']
}, },
resizeObserver: null, resizeObserver: null,
useResizeHandler: this.$store.state.isWorkspaceVisible useResizeHandler: this.$store.state.isWorkspaceVisible,
selectedItem: null
} }
}, },
computed: { computed: {
@@ -115,13 +139,24 @@ export default {
this.$emit('update:exportToSvgEnabled', true) this.$emit('update:exportToSvgEnabled', true)
this.$emit('update:exportToHtmlEnabled', true) this.$emit('update:exportToHtmlEnabled', true)
}, },
mounted() { async mounted() {
this.resizeObserver = new ResizeObserver(this.handleResize) this.resizeObserver = new ResizeObserver(this.handleResize)
this.resizeObserver.observe(this.$refs.chartContainer) this.resizeObserver.observe(this.$refs.chartContainer)
if (this.dataSources) { if (this.dataSources) {
dereference.default(this.state.data, this.dataSources) dereference.default(this.state.data, this.dataSources)
} }
this.handleResize() this.handleResize()
await nextTick()
const plotlyDiv =
this.$refs.plotlyEditor?.$el?.querySelector('.js-plotly-plot')
plotlyDiv?.on('plotly_selected', selectionEvent => {
if (selectionEvent) {
this.selectedItem = 1
}
})
plotlyDiv?.on('plotly_deselect', () => {
this.selectedItem = null
})
}, },
activated() { activated() {
this.useResizeHandler = true this.useResizeHandler = true
@@ -144,6 +179,10 @@ export default {
// TODO: check changes and enable Save button if needed // TODO: check changes and enable Save button if needed
}, },
update(data, layout, frames) { update(data, layout, frames) {
if (layout?.selections?.length > 0) {
layout.selections = []
data.forEach(dataItem => delete dataItem.selectedpoints)
}
this.state = { data, layout, frames } this.state = { data, layout, frames }
this.$emit('update') this.$emit('update')
}, },

View File

@@ -60,7 +60,7 @@
<icon-button <icon-button
v-if="mode === 'graph'" v-if="mode === 'graph'"
ref="viewNodeOrEdgeBtn" ref="viewNodeOrEdgeBtn"
tooltip="View node or edge details" tooltip="View details"
tooltipPosition="top-left" tooltipPosition="top-left"
:active="viewValuePanelVisible" :active="viewValuePanelVisible"
@click="viewValuePanelVisible = !viewValuePanelVisible" @click="viewValuePanelVisible = !viewValuePanelVisible"

View File

@@ -19,6 +19,7 @@
<chart <chart
ref="customChart" ref="customChart"
v-bind="customChartComponentProps" v-bind="customChartComponentProps"
:showViewSettings="showViewSettings"
@update="$emit('update')" @update="$emit('update')"
@loading-image-completed="$emit('loadingImageCompleted')" @loading-image-completed="$emit('loadingImageCompleted')"
/> />

View File

@@ -0,0 +1,122 @@
import { expect } from 'chai'
import { mount, flushPromises } from '@vue/test-utils'
import actions from '@/store/actions'
import mutations from '@/store/mutations'
import { createStore } from 'vuex'
import MainView from '@/views/MainView.vue'
import { routes } from '@/router'
import { createRouter, createWebHistory } from 'vue-router'
describe('MainView.vue', () => {
it('Saves inquiry without data', async () => {
const state = {
db: {},
tabs: [],
inquiries: []
}
const store = createStore({ state, actions, mutations })
const router = createRouter({
history: createWebHistory(),
routes: routes
})
router.push('/workspace')
await router.isReady()
const wrapper = mount(MainView, {
global: {
stubs: {
'router-link': true,
teleport: false,
'app-diagnostic-info': true,
transition: false,
schema: true,
'run-result': true
},
plugins: [store, router]
},
attachTo: document.body
})
await wrapper.find('#create-btn').trigger('click')
store.state.tabs[1].result = {
columns: ['id', 'name'],
values: {
id: [1, 2],
name: ['Harry', 'Drako']
}
}
await flushPromises()
// Add trace
await wrapper
.findAllComponents({ name: 'Chart' })[1]
.find('button.js-add-button')
.wrapperElement.click()
await flushPromises()
// Select data for axis x
await wrapper
.findAll('.field .dropdown-container .Select__indicator')[0]
.wrapperElement.dispatchEvent(
new MouseEvent('mousedown', { bubbles: true })
)
await wrapper.findAll('.Select__menu .Select__option')[0].trigger('click')
// Select data for axis y
await wrapper
.findAll('.field .dropdown-container .Select__indicator')[2]
.wrapperElement.dispatchEvent(
new MouseEvent('mousedown', { bubbles: true })
)
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
// Click Save in the top menu
await wrapper.find('#save-btn').trigger('click')
// Input inquiry name
document.querySelector('.dialog input').value = 'test'
document.querySelector('.dialog input').dispatchEvent(
new InputEvent('input', {
data: 'test'
})
)
// Click Save in the dialog
document
.querySelector('.dialog .dialog-buttons-container button.primary')
.click()
const inquiriesInStore = store.state.inquiries
expect(inquiriesInStore.length).to.equal(1)
expect(inquiriesInStore[0].createdAt).to.not.be.null
expect(inquiriesInStore[0].createdAt).to.not.be.undefined
expect(inquiriesInStore[0].updateAt).to.not.be.null
expect(inquiriesInStore[0].updatedAt).to.not.be.undefined
expect(inquiriesInStore[0].id).to.not.be.null
expect(inquiriesInStore[0].id).to.not.be.undefined
expect(inquiriesInStore[0].name).to.equal('test')
expect(inquiriesInStore[0].viewType).to.equal('chart')
expect(inquiriesInStore[0].viewOptions.data).to.eql([
{
type: 'scatter',
mode: 'markers',
x: null,
xsrc: 'id',
meta: {
columnNames: {
x: 'id',
y: 'name'
}
},
y: null,
ysrc: 'name'
}
])
wrapper.unmount()
})
})