1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-07 02:28:54 +08:00

3 Commits

Author SHA1 Message Date
lana-k
0a2af0bba3 events 2025-11-01 21:25:56 +01:00
lana-k
e4b35bac0a skip node if there is no node id 2025-11-01 19:48:22 +01:00
lana-k
3d1e822cdc link to docs, disable some settings, check result set 2025-11-01 15:49:34 +01:00
15 changed files with 191 additions and 104 deletions

View File

@@ -33,48 +33,6 @@ export default {
</script>
<style>
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Regular.woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-SemiBold.woff2');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Bold.woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Italic.woff2');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-SemiBoldItalic.woff2');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-BoldItalic.woff2');
font-weight: 700;
font-style: italic;
}
#app,
.dialog,
input,

View File

@@ -4,3 +4,10 @@
font-size: 13px;
padding: 0 24px;
}
.data-view-warning {
height: 40px;
line-height: 40px;
border-bottom: 1px solid var(--color-border);
box-sizing: border-box;
}

View File

@@ -0,0 +1,45 @@
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Regular.woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-SemiBold.woff2');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Bold.woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-Italic.woff2');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-SemiBoldItalic.woff2');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src: url('@/assets/fonts/OpenSans-BoldItalic.woff2');
font-weight: 700;
font-style: italic;
}
a {
color: var(--color-accent-shade);
}

View File

@@ -1,5 +1,12 @@
<template>
<Field label="Adjust sizes">
<Field label="Scaling ratio">
<NumericInput
:value="modelValue.scalingRatio"
@update="update('scalingRatio', $event)"
/>
</Field>
<Field label="Prevent overlapping">
<RadioBlocks
:options="booleanOptions"
:activeOption="modelValue.adjustSizes"
@@ -22,13 +29,6 @@
/>
</Field>
<Field label="Gravity">
<NumericInput
:value="modelValue.gravity"
@update="update('gravity', $event)"
/>
</Field>
<Field label="Strong gravity mode">
<RadioBlocks
:options="booleanOptions"

View File

@@ -39,7 +39,10 @@
</template>
</Field>
<Field v-if="modelValue.type !== 'constant'" label="Color as">
<Field
v-if="modelValue.type !== 'constant' && modelValue.sourceUsage === 'map_to'"
label="Color as"
>
<RadioBlocks
:options="сolorAsOptions"
:activeOption="modelValue.mode"
@@ -47,7 +50,10 @@
/>
</Field>
<Field v-if="modelValue.type !== 'constant'" label="Colorscale direction">
<Field
v-if="modelValue.type !== 'constant' && modelValue.sourceUsage === 'map_to'"
label="Colorscale direction"
>
<RadioBlocks
:options="сolorscaleDirections"
:activeOption="modelValue.colorscaleDirection"

View File

@@ -7,10 +7,10 @@
/>
</Field>
<Field label="Scaling ratio">
<Field label="Gravity">
<NumericInput
:value="modelValue.scalingRatio"
@update="update('scalingRatio', $event)"
:value="modelValue.gravity"
@update="update('gravity', $event)"
/>
</Field>
</template>

View File

@@ -27,7 +27,7 @@
/>
</Field>
<Field>
<Field v-if="modelValue.type === 'variable'">
<RadioBlocks
:options="colorSourceUsageOptions"
:activeOption="modelValue.sourceUsage"
@@ -45,7 +45,10 @@
</template>
</Field>
<Field v-if="modelValue.type !== 'constant'" label="Color as">
<Field
v-if="modelValue.type !== 'constant' && modelValue.sourceUsage === 'map_to'"
label="Color as"
>
<RadioBlocks
:options="сolorAsOptions"
:activeOption="modelValue.mode"
@@ -53,7 +56,10 @@
/>
</Field>
<Field v-if="modelValue.type !== 'constant'" label="Colorscale direction">
<Field
v-if="modelValue.type !== 'constant' && modelValue.sourceUsage === 'map_to'"
label="Colorscale direction"
>
<RadioBlocks
:options="сolorscaleDirections"
:activeOption="modelValue.colorscaleDirection"

View File

@@ -5,8 +5,39 @@ const TYPE_NODE = 0
const TYPE_EDGE = 1
const DEFAULT_SCALE = COLOR_PICKER_CONSTANTS.DEFAULT_SCALE
export function dataSourceIsValid(dataSources) {
const docColumn = Object.keys(dataSources)[0]
if (!docColumn) {
return false
}
try {
const records = dataSources[docColumn].slice(0, 10)
records.forEach(record => {
const parsedRec = JSON.parse(record)
if (Object.keys(parsedRec).length < 2) {
throw new Error('The records must have at least 2 keys')
}
})
const firstRecord = JSON.parse(records[0])
if (
!Object.keys(firstRecord).some(key => {
return records
.map(record => JSON.parse(record)[key])
.every(value => value === 0 || value === 1)
})
) {
throw new Error(
'There must be a common key used as object type: 0 - node, 1 - edge'
)
}
return true
} catch (err) {
return false
}
}
export function buildNodes(graph, dataSources, options) {
const docColumn = Object.keys(dataSources)[0] || 'doc'
const docColumn = Object.keys(dataSources)[0]
const { objectType, nodeId } = options.structure
if (objectType && nodeId) {
@@ -14,10 +45,12 @@ export function buildNodes(graph, dataSources, options) {
.map(json => JSON.parse(json))
.filter(item => item[objectType] === TYPE_NODE)
nodes.forEach(node => {
if (node[nodeId]) {
graph.addNode(node[nodeId], {
data: node,
labelColor: options.style.nodes.label.color
})
}
})
}
}
@@ -110,10 +143,23 @@ function getUpdateSizeMethod(graph, sizeSettings) {
if (type === 'constant') {
return attributes => (attributes.size = value)
} else if (type === 'variable') {
return getVariabledSizeMethod(mode, source, scale, min)
return attributes => {
attributes.size = getVariabledSize(
mode,
attributes.data[source],
scale,
min
)
}
} else {
return (attributes, nodeId) =>
(attributes.size = Math.max(graph[method](nodeId) * scale, min))
return (attributes, nodeId) => {
attributes.size = getVariabledSize(
mode,
graph[method](nodeId),
scale,
min
)
}
}
}
@@ -184,22 +230,13 @@ function getUpdateEdgeColorMethod(graph, colorSettings) {
}
}
function getVariabledSizeMethod(mode, source, scale, min) {
function getVariabledSize(mode, value, scale, min) {
if (mode === 'diameter') {
return attributes =>
(attributes.size = Math.max(
(attributes.data[source] / 2) * scale,
min / 2
))
return Math.max((value / 2) * scale, min / 2)
} else if (mode === 'area') {
return attributes =>
(attributes.size = Math.max(
Math.sqrt((attributes.data[source] / 2) * scale),
min / 2
))
return Math.max(Math.sqrt((value / 2) * scale), min / 2)
} else {
return attributes =>
(attributes.size = Math.max(attributes.data[source] * scale, min))
return Math.max(value * scale, min)
}
}

View File

@@ -5,6 +5,7 @@ import store from '@/store'
import { createVfm, VueFinalModal, useVfm } from 'vue-final-modal'
import '@/assets/styles/variables.css'
import '@/assets/styles/typography.css'
import '@/assets/styles/buttons.css'
import '@/assets/styles/tables.css'
import '@/assets/styles/dialogs.css'

View File

@@ -1,6 +1,6 @@
<template>
<div ref="chartContainer" class="chart-container">
<div v-show="!dataSources" class="warning chart-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
result is not empty.
</div>
@@ -184,13 +184,6 @@ export default {
height: 100%;
}
.chart-warning {
height: 40px;
line-height: 40px;
border-bottom: 1px solid var(--color-border);
box-sizing: border-box;
}
.chart {
min-height: 242px;
}

View File

@@ -7,7 +7,9 @@
<Field>
Map your result set records to node and edge properties required
to build a graph. Learn more about result set requirements in the
documentation.
<a href="https://sqliteviz.com/docs/graph/" target="_blank">
documentation</a
>.
</Field>
<Field label="Object type">
<Dropdown
@@ -224,6 +226,7 @@ import NodeColorSettings from '@/components/Graph/NodeColorSettings.vue'
import NodeSizeSettings from '@/components/Graph/NodeSizeSettings.vue'
import EdgeSizeSettings from '@/components/Graph/EdgeSizeSettings.vue'
import EdgeColorSettings from '@/components/Graph/EdgeColorSettings.vue'
import events from '@/lib/utils/events'
export default {
components: {
@@ -291,7 +294,7 @@ export default {
nodes: {
size: {
type: 'constant',
value: 4
value: 10
},
color: {
type: 'constant',
@@ -375,6 +378,14 @@ export default {
this.buildGraph()
}
},
'settings.layout.type': {
immediate: true,
handler() {
events.send('viz_graph.render', null, {
layout: this.settings.layout.type
})
}
},
tabLayout: {
deep: true,
handler() {

View File

@@ -1,13 +1,20 @@
<template>
<div ref="graphContainer" class="graph-container">
<div v-show="!dataSources" class="warning chart-warning">
<div v-show="!dataSources" class="warning data-view-warning">
There is no data to build a graph. Run your SQL query and make sure the
result is not empty.
</div>
<div v-show="!dataSourceIsValid" class="warning data-view-warning">
Result set is invalid for graph visualisation. Learn more in
<a href="https://sqliteviz.com/docs/graph/" target="_blank">
documentation</a
>.
</div>
<div
class="graph"
:style="{
height: !dataSources ? 'calc(100% - 40px)' : '100%'
height:
!dataSources || !dataSourceIsValid ? 'calc(100% - 40px)' : '100%'
}"
>
<GraphEditor
@@ -23,8 +30,8 @@
<script>
import 'react-chart-editor/lib/react-chart-editor.css'
import events from '@/lib/utils/events'
import GraphEditor from './GraphEditor.vue'
import { dataSourceIsValid } from '@/lib/graphHelper'
export default {
name: 'Graph',
@@ -48,7 +55,6 @@ export default {
resizeObserver: null
}
},
created() {
this.$emit('update:exportToSvgEnabled', false)
this.$emit('update:exportToHtmlEnabled', false)
@@ -66,6 +72,11 @@ export default {
this.handleResize()
}
},
computed: {
dataSourceIsValid() {
return !this.dataSources || dataSourceIsValid(this.dataSources)
}
},
methods: {
getOptionsForSave() {
return this.$refs.graphEditor.settings
@@ -93,13 +104,6 @@ export default {
height: 100%;
}
.chart-warning {
height: 40px;
line-height: 40px;
border-bottom: 1px solid var(--color-border);
box-sizing: border-box;
}
.graph {
min-height: 242px;
}

View File

@@ -326,4 +326,8 @@ export default {
.pivot-output:empty {
flex-grow: 0;
}
:deep(.js-plotly-plot) {
height: 100%;
}
</style>

View File

@@ -254,6 +254,8 @@ export default {
events.send(
this.mode === 'chart' || this.plotlyInPivot
? 'viz_plotly.export'
: this.mode === 'graph'
? 'viz_graph.export'
: 'viz_pivot.export',
null,
eventLabels

View File

@@ -42,8 +42,8 @@ export default {
) {
const stmt = [
'/*',
' * Your database is empty. In order to start building charts',
' * you should create a table and insert data into it.',
' * Your database is empty. In order to start building data visualisations',
' * you should create tables and insert data into them.',
' */',
'CREATE TABLE house',
'(',
@@ -54,7 +54,20 @@ export default {
"('Gryffindor', 100),",
"('Hufflepuff', 90),",
"('Ravenclaw', 95),",
"('Slytherin', 80);"
"('Slytherin', 80);",
'',
'CREATE TABLE student',
'(',
' id INTEGER,',
' name TEXT,',
' house TEXT',
');',
'INSERT INTO student VALUES',
"(1, 'Harry Potter', 'Gryffindor'),",
"(2, 'Ron Weasley', 'Gryffindor'),",
"(3, 'Draco Malfoy', 'Slytherin'),",
"(4, 'Luna Lovegood', 'Ravenclaw'),",
"(5, 'Cedric Diggory', 'Hufflepuff');"
].join('\n')
const tabId = await this.$store.dispatch('addTab', { query: stmt })