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

link to docs, disable some settings, check result set

This commit is contained in:
lana-k
2025-11-01 15:49:34 +01:00
parent 3d6479be7a
commit 3d1e822cdc
13 changed files with 173 additions and 56 deletions

View File

@@ -4,3 +4,10 @@
font-size: 13px; font-size: 13px;
padding: 0 24px; 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> <template>
<Field label="Adjust sizes"> <Field label="Scaling ratio">
<NumericInput
:value="modelValue.scalingRatio"
@update="update('scalingRatio', $event)"
/>
</Field>
<Field label="Prevent overlapping">
<RadioBlocks <RadioBlocks
:options="booleanOptions" :options="booleanOptions"
:activeOption="modelValue.adjustSizes" :activeOption="modelValue.adjustSizes"
@@ -22,13 +29,6 @@
/> />
</Field> </Field>
<Field label="Gravity">
<NumericInput
:value="modelValue.gravity"
@update="update('gravity', $event)"
/>
</Field>
<Field label="Strong gravity mode"> <Field label="Strong gravity mode">
<RadioBlocks <RadioBlocks
:options="booleanOptions" :options="booleanOptions"

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,39 @@ const TYPE_NODE = 0
const TYPE_EDGE = 1 const TYPE_EDGE = 1
const DEFAULT_SCALE = COLOR_PICKER_CONSTANTS.DEFAULT_SCALE 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) { export function buildNodes(graph, dataSources, options) {
const docColumn = Object.keys(dataSources)[0] || 'doc' const docColumn = Object.keys(dataSources)[0]
const { objectType, nodeId } = options.structure const { objectType, nodeId } = options.structure
if (objectType && nodeId) { if (objectType && nodeId) {
@@ -110,10 +141,23 @@ function getUpdateSizeMethod(graph, sizeSettings) {
if (type === 'constant') { if (type === 'constant') {
return attributes => (attributes.size = value) return attributes => (attributes.size = value)
} else if (type === 'variable') { } else if (type === 'variable') {
return getVariabledSizeMethod(mode, source, scale, min) return attributes => {
attributes.size = getVariabledSize(
mode,
attributes.data[source],
scale,
min
)
}
} else { } else {
return (attributes, nodeId) => return (attributes, nodeId) => {
(attributes.size = Math.max(graph[method](nodeId) * scale, min)) attributes.size = getVariabledSize(
mode,
graph[method](nodeId),
scale,
min
)
}
} }
} }
@@ -184,22 +228,13 @@ function getUpdateEdgeColorMethod(graph, colorSettings) {
} }
} }
function getVariabledSizeMethod(mode, source, scale, min) { function getVariabledSize(mode, value, scale, min) {
if (mode === 'diameter') { if (mode === 'diameter') {
return attributes => return Math.max((value / 2) * scale, min / 2)
(attributes.size = Math.max(
(attributes.data[source] / 2) * scale,
min / 2
))
} else if (mode === 'area') { } else if (mode === 'area') {
return attributes => return Math.max(Math.sqrt((value / 2) * scale), min / 2)
(attributes.size = Math.max(
Math.sqrt((attributes.data[source] / 2) * scale),
min / 2
))
} else { } else {
return attributes => return Math.max(value * scale, min)
(attributes.size = Math.max(attributes.data[source] * scale, min))
} }
} }

View File

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

View File

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

View File

@@ -7,7 +7,9 @@
<Field> <Field>
Map your result set records to node and edge properties required Map your result set records to node and edge properties required
to build a graph. Learn more about result set requirements in the 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>
<Field label="Object type"> <Field label="Object type">
<Dropdown <Dropdown
@@ -291,7 +293,7 @@ export default {
nodes: { nodes: {
size: { size: {
type: 'constant', type: 'constant',
value: 4 value: 10
}, },
color: { color: {
type: 'constant', type: 'constant',

View File

@@ -1,13 +1,20 @@
<template> <template>
<div ref="graphContainer" class="graph-container"> <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 There is no data to build a graph. Run your SQL query and make sure the
result is not empty. result is not empty.
</div> </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 <div
class="graph" class="graph"
:style="{ :style="{
height: !dataSources ? 'calc(100% - 40px)' : '100%' height:
!dataSources || !dataSourceIsValid ? 'calc(100% - 40px)' : '100%'
}" }"
> >
<GraphEditor <GraphEditor
@@ -25,6 +32,7 @@
import 'react-chart-editor/lib/react-chart-editor.css' import 'react-chart-editor/lib/react-chart-editor.css'
import events from '@/lib/utils/events' import events from '@/lib/utils/events'
import GraphEditor from './GraphEditor.vue' import GraphEditor from './GraphEditor.vue'
import { dataSourceIsValid } from '@/lib/graphHelper'
export default { export default {
name: 'Graph', name: 'Graph',
@@ -48,7 +56,6 @@ export default {
resizeObserver: null resizeObserver: null
} }
}, },
created() { created() {
this.$emit('update:exportToSvgEnabled', false) this.$emit('update:exportToSvgEnabled', false)
this.$emit('update:exportToHtmlEnabled', false) this.$emit('update:exportToHtmlEnabled', false)
@@ -66,6 +73,11 @@ export default {
this.handleResize() this.handleResize()
} }
}, },
computed: {
dataSourceIsValid() {
return !this.dataSources || dataSourceIsValid(this.dataSources)
}
},
methods: { methods: {
getOptionsForSave() { getOptionsForSave() {
return this.$refs.graphEditor.settings return this.$refs.graphEditor.settings
@@ -93,13 +105,6 @@ export default {
height: 100%; height: 100%;
} }
.chart-warning {
height: 40px;
line-height: 40px;
border-bottom: 1px solid var(--color-border);
box-sizing: border-box;
}
.graph { .graph {
min-height: 242px; min-height: 242px;
} }

View File

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

View File

@@ -42,8 +42,8 @@ export default {
) { ) {
const stmt = [ const stmt = [
'/*', '/*',
' * Your database is empty. In order to start building charts', ' * Your database is empty. In order to start building data visualisations',
' * you should create a table and insert data into it.', ' * you should create tables and insert data into them.',
' */', ' */',
'CREATE TABLE house', 'CREATE TABLE house',
'(', '(',
@@ -54,7 +54,20 @@ export default {
"('Gryffindor', 100),", "('Gryffindor', 100),",
"('Hufflepuff', 90),", "('Hufflepuff', 90),",
"('Ravenclaw', 95),", "('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') ].join('\n')
const tabId = await this.$store.dispatch('addTab', { query: stmt }) const tabId = await this.$store.dispatch('addTab', { query: stmt })