mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 18:18:53 +08:00
show dialog for keeping user activity #50
This commit is contained in:
83
src/components/LoadingDialog.vue
Normal file
83
src/components/LoadingDialog.vue
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<modal
|
||||||
|
:name="name"
|
||||||
|
classes="dialog"
|
||||||
|
height="auto"
|
||||||
|
:clickToClose="false"
|
||||||
|
>
|
||||||
|
<div class="dialog-header">
|
||||||
|
{{ title }}
|
||||||
|
<close-icon @click="$emit('cancel')" :disabled="loading"/>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<div v-if="loading" class="loading-dialog-body">
|
||||||
|
<loading-indicator :size="30" class="state-icon"/>
|
||||||
|
{{ loadingMsg }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="loading-dialog-body">
|
||||||
|
<img :src="require('@/assets/images/success.svg')" class="success-icon state-icon" />
|
||||||
|
{{ successMsg }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-buttons-container">
|
||||||
|
<button
|
||||||
|
class="secondary"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="$emit('cancel')"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="primary"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="$emit('action')"
|
||||||
|
>
|
||||||
|
{{ actionBtnName }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import LoadingIndicator from '@/components/LoadingIndicator'
|
||||||
|
import CloseIcon from '@/components/svg/close'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'loadingDialog',
|
||||||
|
props: {
|
||||||
|
loadingMsg: String,
|
||||||
|
successMsg: String,
|
||||||
|
actionBtnName: String,
|
||||||
|
name: String,
|
||||||
|
title: String,
|
||||||
|
loading: Boolean
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
loading () {
|
||||||
|
if (this.loading) {
|
||||||
|
this.$modal.show(this.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: { LoadingIndicator, CloseIcon },
|
||||||
|
methods: {
|
||||||
|
cancel () {
|
||||||
|
this.$emit('cancel')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.loading-dialog-body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.success-icon {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -9,22 +9,31 @@ async function _copyBlob (blob) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function _copyFromDataUrl (url) {
|
||||||
|
const blob = dataUrlToBlob(url)
|
||||||
|
await _copyBlob(blob)
|
||||||
|
Lib.notifier('Image copied to clipboard successfully', 'long')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _copyCanvas (canvas) {
|
||||||
|
canvas.toBlob(async (blob) => {
|
||||||
|
await _copyBlob(blob)
|
||||||
|
Lib.notifier('Image copied to clipboard successfully', 'long')
|
||||||
|
}, 'image/png', 1)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async copyCsv (str) {
|
async copyCsv (str) {
|
||||||
await navigator.clipboard.writeText(str)
|
await navigator.clipboard.writeText(str)
|
||||||
Lib.notifier('CSV copied to clipboard successfully', 'long')
|
Lib.notifier('CSV copied to clipboard successfully', 'long')
|
||||||
},
|
},
|
||||||
|
|
||||||
async copyCanvas (canvas, type) {
|
async copyImage (source) {
|
||||||
canvas.toBlob(async (blob) => {
|
if (source instanceof HTMLCanvasElement) {
|
||||||
await _copyBlob(blob)
|
console.log('canvas')
|
||||||
Lib.notifier('Image copied to clipboard successfully', 'long')
|
return _copyCanvas(source)
|
||||||
}, 'image/png', 1)
|
} else {
|
||||||
},
|
return _copyFromDataUrl(source)
|
||||||
|
}
|
||||||
async copyFromDataUrl (url) {
|
|
||||||
const blob = dataUrlToBlob(url)
|
|
||||||
await _copyBlob(blob)
|
|
||||||
Lib.notifier('Image copied to clipboard successfully', 'long')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import PlotlyEditor from 'react-chart-editor'
|
|||||||
import chartHelper from '@/lib/chartHelper'
|
import chartHelper from '@/lib/chartHelper'
|
||||||
import dereference from 'react-chart-editor/lib/lib/dereference'
|
import dereference from 'react-chart-editor/lib/lib/dereference'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Chart',
|
name: 'Chart',
|
||||||
@@ -90,13 +89,12 @@ export default {
|
|||||||
return chartHelper.getOptionsForSave(this.state, this.dataSources)
|
return chartHelper.getOptionsForSave(this.state, this.dataSources)
|
||||||
},
|
},
|
||||||
async saveAsPng () {
|
async saveAsPng () {
|
||||||
const url = await chartHelper.getImageDataUrl(this.$refs.plotlyEditor.$el, 'png')
|
const url = await this.prepareCopy()
|
||||||
this.$emit('loadingImageCompleted')
|
this.$emit('loadingImageCompleted')
|
||||||
fIo.downloadFromUrl(url, 'chart')
|
fIo.downloadFromUrl(url, 'chart')
|
||||||
},
|
},
|
||||||
async copyPngToClipboard () {
|
async prepareCopy () {
|
||||||
const url = await chartHelper.getImageDataUrl(this.$refs.plotlyEditor.$el, 'png')
|
return await chartHelper.getImageDataUrl(this.$refs.plotlyEditor.$el, 'png')
|
||||||
cIo.copyFromDataUrl(url)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import 'pivottable'
|
import 'pivottable'
|
||||||
import 'pivottable/dist/pivot.css'
|
import 'pivottable/dist/pivot.css'
|
||||||
@@ -166,15 +165,14 @@ export default {
|
|||||||
fIo.downloadFromUrl(canvas.toDataURL('image/png'), 'pivot', 'image/png')
|
fIo.downloadFromUrl(canvas.toDataURL('image/png'), 'pivot', 'image/png')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async copyPngToClipboard () {
|
|
||||||
|
async prepareCopy () {
|
||||||
if (this.pivotOptions.rendererName === 'Custom chart') {
|
if (this.pivotOptions.rendererName === 'Custom chart') {
|
||||||
this.pivotOptions.rendererOptions.customChartComponent.copyPngToClipboard()
|
return await this.pivotOptions.rendererOptions.customChartComponent.prepareCopy()
|
||||||
} else if (this.pivotOptions.rendererName in $.pivotUtilities.plotly_renderers) {
|
} else if (this.pivotOptions.rendererName in $.pivotUtilities.plotly_renderers) {
|
||||||
const url = await chartHelper.getImageDataUrl(this.$refs.pivotOutput, 'png')
|
return await chartHelper.getImageDataUrl(this.$refs.pivotOutput, 'png')
|
||||||
cIo.copyFromDataUrl(url)
|
|
||||||
} else {
|
} else {
|
||||||
const canvas = await getPivotCanvas(this.$refs.pivotOutput)
|
return await getPivotCanvas(this.$refs.pivotOutput)
|
||||||
cIo.copyCanvas(canvas)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,13 +42,25 @@
|
|||||||
</icon-button>
|
</icon-button>
|
||||||
|
|
||||||
<icon-button
|
<icon-button
|
||||||
|
:loading="copyingImage"
|
||||||
tooltip="Copy visualisation to clipboard"
|
tooltip="Copy visualisation to clipboard"
|
||||||
tooltip-position="top-left"
|
tooltip-position="top-left"
|
||||||
@click="copyToClipboard"
|
@click="prepareCopy"
|
||||||
>
|
>
|
||||||
<clipboard-icon/>
|
<clipboard-icon/>
|
||||||
</icon-button>
|
</icon-button>
|
||||||
</side-tool-bar>
|
</side-tool-bar>
|
||||||
|
|
||||||
|
<loading-dialog
|
||||||
|
loadingMsg="Rendering the visualisation..."
|
||||||
|
successMsg="Image is ready"
|
||||||
|
actionBtnName="Copy"
|
||||||
|
name="prepareCopy"
|
||||||
|
title="Copy to clipboard"
|
||||||
|
:loading="preparingCopy"
|
||||||
|
@action="copyToClipboard"
|
||||||
|
@cancel="cancelCopy"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -61,6 +73,8 @@ import ChartIcon from '@/components/svg/chart'
|
|||||||
import PivotIcon from '@/components/svg/pivot'
|
import PivotIcon from '@/components/svg/pivot'
|
||||||
import PngIcon from '@/components/svg/png'
|
import PngIcon from '@/components/svg/png'
|
||||||
import ClipboardIcon from '@/components/svg/clipboard'
|
import ClipboardIcon from '@/components/svg/clipboard'
|
||||||
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
|
import loadingDialog from '@/components/LoadingDialog'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DataView',
|
name: 'DataView',
|
||||||
@@ -73,13 +87,17 @@ export default {
|
|||||||
ChartIcon,
|
ChartIcon,
|
||||||
PivotIcon,
|
PivotIcon,
|
||||||
PngIcon,
|
PngIcon,
|
||||||
ClipboardIcon
|
ClipboardIcon,
|
||||||
|
loadingDialog
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
mode: this.initMode || 'chart',
|
mode: this.initMode || 'chart',
|
||||||
importToPngEnabled: true,
|
importToPngEnabled: true,
|
||||||
loadingImage: false
|
loadingImage: false,
|
||||||
|
copyingImage: false,
|
||||||
|
preparingCopy: false,
|
||||||
|
dataToCopy: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -106,12 +124,33 @@ export default {
|
|||||||
getOptionsForSave () {
|
getOptionsForSave () {
|
||||||
return this.$refs.viewComponent.getOptionsForSave()
|
return this.$refs.viewComponent.getOptionsForSave()
|
||||||
},
|
},
|
||||||
copyToClipboard () {
|
async prepareCopy () {
|
||||||
if ('ClipboardItem' in window) {
|
if ('ClipboardItem' in window) {
|
||||||
this.$refs.viewComponent.copyPngToClipboard()
|
this.preparingCopy = true
|
||||||
|
this.$modal.show('prepareCopy')
|
||||||
|
const t0 = performance.now()
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
this.dataToCopy = await this.$refs.viewComponent.prepareCopy()
|
||||||
|
const t1 = performance.now()
|
||||||
|
if ((t1 - t0) < 950) {
|
||||||
|
this.$modal.hide('prepareCopy')
|
||||||
|
this.copyToClipboard()
|
||||||
|
} else {
|
||||||
|
this.preparingCopy = false
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
} else {
|
} else {
|
||||||
alert("Your browser doesn't support copying images into the clipboard. If you use Firefox you can enable it by setting dom.events.asyncClipboard.clipboardItem to true.")
|
alert("Your browser doesn't support copying images into the clipboard. If you use Firefox you can enable it by setting dom.events.asyncClipboard.clipboardItem to true.")
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async copyToClipboard () {
|
||||||
|
cIo.copyImage(this.dataToCopy)
|
||||||
|
this.$modal.hide('prepareCopy')
|
||||||
|
},
|
||||||
|
cancelCopy () {
|
||||||
|
this.dataToCopy = null
|
||||||
|
this.$modal.hide('prepareCopy')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,4 +171,19 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
>>>.vm--container {
|
||||||
|
animation: show-modal 1s linear 0s 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes show-modal {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user