mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Create sharable modal component. Implemented initial image export
This commit is contained in:
@@ -45,9 +45,25 @@
|
||||
@click="requestDocumentClose()"
|
||||
></button>
|
||||
</li>
|
||||
<li><button v-t="'loadDocument'"></button></li>
|
||||
<li><button v-t="'saveDocument'"></button></li>
|
||||
<li><button v-t="'exportImage'"></button></li>
|
||||
<li>
|
||||
<button v-t="'loadDocument'"
|
||||
type="button"
|
||||
disabled
|
||||
></button>
|
||||
</li>
|
||||
<li>
|
||||
<button v-t="'saveDocument'"
|
||||
type="button"
|
||||
:disabled="true/*!documents.length*/"
|
||||
></button>
|
||||
</li>
|
||||
<li>
|
||||
<button v-t="'exportImage'"
|
||||
type="button"
|
||||
:disabled="!documents.length"
|
||||
@click="requestImageExport()"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
@@ -55,6 +71,7 @@
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<button v-t="'resizeDocument'"
|
||||
type="button"
|
||||
:disabled="!documents.length"
|
||||
@click="requestDocumentResize()"
|
||||
></button>
|
||||
@@ -86,7 +103,7 @@
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
|
||||
import { RESIZE_DOCUMENT } from "@/definitions/modal-windows";
|
||||
import { RESIZE_DOCUMENT, EXPORT_IMAGE } from "@/definitions/modal-windows";
|
||||
import { supportsFullscreen, setToggleButton } from "@/utils/environment-util";
|
||||
import messages from "./messages.json";
|
||||
|
||||
@@ -125,6 +142,9 @@ export default {
|
||||
...mapActions([
|
||||
"requestDocumentClose",
|
||||
]),
|
||||
requestImageExport() {
|
||||
this.openModal( EXPORT_IMAGE );
|
||||
},
|
||||
requestDocumentResize() {
|
||||
this.openModal( RESIZE_DOCUMENT );
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters, mapActions } from "vuex";
|
||||
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
|
||||
import ZoomableCanvas from "@/components/ui/zcanvas/zoomable-canvas";
|
||||
import DrawableLayer from "@/components/ui/zcanvas/drawable-layer";
|
||||
import { scaleToRatio } from "@/utils/image-math";
|
||||
@@ -52,7 +52,7 @@ import {
|
||||
|
||||
/* internal non-reactive properties */
|
||||
|
||||
let lastDocument, zCanvas, drawableLayer;
|
||||
let lastDocument, drawableLayer;
|
||||
// scale of the on-screen canvas relative to the document
|
||||
let xScale = 1, yScale = 1, zoom = 1, containerSize;
|
||||
|
||||
@@ -63,6 +63,7 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
...mapState([
|
||||
"zCanvas",
|
||||
"windowSize"
|
||||
]),
|
||||
...mapGetters([
|
||||
@@ -80,16 +81,16 @@ export default {
|
||||
deep: true,
|
||||
handler( document, oldValue = null ) {
|
||||
if ( !document?.layers ) {
|
||||
if ( zCanvas ) {
|
||||
zCanvas.dispose();
|
||||
zCanvas = null;
|
||||
if ( this.zCanvas ) {
|
||||
this.zCanvas.dispose();
|
||||
this.setZCanvas( null );
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ( !zCanvas ) {
|
||||
if ( !this.zCanvas ) {
|
||||
this.createCanvas();
|
||||
this.$nextTick(() => {
|
||||
zCanvas.insertInPage( this.$refs.canvasContainer );
|
||||
this.zCanvas.insertInPage( this.$refs.canvasContainer );
|
||||
this.cacheContainerSize();
|
||||
this.scaleCanvas();
|
||||
});
|
||||
@@ -99,7 +100,7 @@ export default {
|
||||
lastDocument = name;
|
||||
flushCache(); // switching between documents
|
||||
}
|
||||
if ( zCanvas.width !== width || zCanvas.height !== height ) {
|
||||
if ( this.zCanvas.width !== width || this.zCanvas.height !== height ) {
|
||||
this.scaleCanvas();
|
||||
}
|
||||
document.layers.forEach( layer => {
|
||||
@@ -108,7 +109,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
layer.graphics.forEach( graphic => {
|
||||
const sprite = createSpriteForGraphic( zCanvas, graphic );
|
||||
const sprite = createSpriteForGraphic( this.zCanvas, graphic );
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -124,7 +125,7 @@ export default {
|
||||
case "brush":
|
||||
if ( !drawableLayer ) {
|
||||
drawableLayer = new DrawableLayer( this.activeDocument );
|
||||
zCanvas.addChild( drawableLayer );
|
||||
this.zCanvas.addChild( drawableLayer );
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -153,18 +154,21 @@ export default {
|
||||
this.cacheContainerSize();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
"setZCanvas",
|
||||
]),
|
||||
...mapActions([
|
||||
"requestDocumentClose",
|
||||
]),
|
||||
createCanvas() {
|
||||
zCanvas = new ZoomableCanvas({
|
||||
this.setZCanvas( new ZoomableCanvas({
|
||||
width: 160,
|
||||
height: 90,
|
||||
animate: false,
|
||||
smoothing: true,
|
||||
backgroundColor: "red",
|
||||
stretchToFit: false
|
||||
});
|
||||
}));
|
||||
},
|
||||
cacheContainerSize() {
|
||||
containerSize = this.$el.parentNode?.getBoundingClientRect();
|
||||
@@ -180,12 +184,12 @@ export default {
|
||||
let { width, height } = this.activeDocument;
|
||||
({ width, height } = scaleToRatio( width, height, containerSize.width, containerSize.height ));
|
||||
this.wrapperHeight = `${window.innerHeight - containerSize.top - 20}px`;
|
||||
zCanvas.setDimensions( width * zoom, height * zoom, true, true ); // replace to not multiply by zoom
|
||||
this.zCanvas.setDimensions( width * zoom, height * zoom, true, true ); // replace to not multiply by zoom
|
||||
xScale = width / this.activeDocument.width;
|
||||
yScale = height / this.activeDocument.height;
|
||||
zCanvas.setZoomFactor( xScale * zoom, yScale * zoom ); // replace with zCanvas.setZoom()
|
||||
this.zCanvas.setZoomFactor( xScale * zoom, yScale * zoom ); // replace with zCanvas.setZoom()
|
||||
|
||||
this.centerCanvas = zCanvas.getWidth() < containerSize.width || zCanvas.getHeight() < containerSize.height ;
|
||||
this.centerCanvas = this.zCanvas.getWidth() < containerSize.width || this.zCanvas.getHeight() < containerSize.height ;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -67,9 +67,8 @@ import ImageToDocumentManager from "@/mixins/image-to-document-manag
|
||||
import { listFolder, downloadFileAsBlob } from "@/services/dropbox-service";
|
||||
import DropboxImagePreview from "./dropbox-image-preview";
|
||||
import { truncate } from "@/utils/string-util";
|
||||
import messages from "./messages.json";
|
||||
|
||||
const ACCEPTED_FILE_EXTENSIONS = [ ".jpg", ".jpeg", "gif", "png" ];
|
||||
import { ACCEPTED_FILE_EXTENSIONS } from "@/definitions/image-types";
|
||||
import messages from "./messages.json";
|
||||
|
||||
function mapEntry( entry, children = [], parent = null ) {
|
||||
return {
|
||||
@@ -145,7 +144,7 @@ export default {
|
||||
return this.leaf.children.filter( entry => {
|
||||
// only show folders and image files
|
||||
if ( entry.type === "file" ) {
|
||||
return ACCEPTED_FILE_EXTENSIONS.some( ext => entry.name.includes( ext ));
|
||||
return ACCEPTED_FILE_EXTENSIONS.some( ext => entry.name.includes( `.${ext}` ));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -1,52 +1,86 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2019-2020 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
<template>
|
||||
<div class="resize-image-modal">
|
||||
<h2 v-t="'resizeDocument'"></h2>
|
||||
<div class="content form">
|
||||
<div class="wrapper input">
|
||||
<label v-t="'width'"></label>
|
||||
<input
|
||||
ref="first"
|
||||
v-model="width"
|
||||
type="number"
|
||||
name="width"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'maintainAspectRatio'"></label>
|
||||
<input
|
||||
v-model="maintainRatio"
|
||||
type="checkbox"
|
||||
name="ratio"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'height'"></label>
|
||||
<input
|
||||
v-model="height"
|
||||
type="number"
|
||||
name="height"
|
||||
/>
|
||||
<modal>
|
||||
<template #header>
|
||||
<h2 v-t="'resizeDocument'"></h2>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="wrapper input">
|
||||
<label v-t="'width'"></label>
|
||||
<input
|
||||
ref="first"
|
||||
v-model="width"
|
||||
type="number"
|
||||
name="width"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'maintainAspectRatio'"></label>
|
||||
<input
|
||||
v-model="maintainRatio"
|
||||
type="checkbox"
|
||||
name="ratio"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'height'"></label>
|
||||
<input
|
||||
v-model="height"
|
||||
type="number"
|
||||
name="height"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<button
|
||||
v-t="'save'"
|
||||
type="button"
|
||||
class="button"
|
||||
@click="save()"
|
||||
></button>
|
||||
<button
|
||||
v-t="'cancel'"
|
||||
type="button"
|
||||
class="button"
|
||||
@click="close()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from "vuex";
|
||||
import Modal from "@/components/modal/modal";
|
||||
import messages from "./messages.json";
|
||||
|
||||
export default {
|
||||
i18n: { messages },
|
||||
components: {
|
||||
Modal,
|
||||
},
|
||||
data: () => ({
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -101,26 +135,3 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/component";
|
||||
|
||||
.resize-image-modal {
|
||||
@include overlay();
|
||||
@include component();
|
||||
|
||||
h2 {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
@include large() {
|
||||
$width: 480px;
|
||||
$height: 300px;
|
||||
|
||||
width: $width;
|
||||
height: $height;
|
||||
left: calc(50% - #{$width / 2});
|
||||
top: calc(50% - #{$height / 2});
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
131
src/components/file-menu/export-image/export-image.vue
Normal file
131
src/components/file-menu/export-image/export-image.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2019-2020 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
<template>
|
||||
<modal>
|
||||
<template #header>
|
||||
<h2 v-t="'exportImage'"></h2>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="wrapper input">
|
||||
<label v-t="'imageType'"></label>
|
||||
<select-box :options="fileTypes"
|
||||
v-model="type"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="hasQualityOptions"
|
||||
class="wrapper input"
|
||||
>
|
||||
<label v-t="'imageQuality'"></label>
|
||||
<slider
|
||||
v-model="quality"
|
||||
:min="0"
|
||||
:max="100"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'fileName'"></label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<button
|
||||
v-t="'export'"
|
||||
type="button"
|
||||
class="button"
|
||||
@click="exportImage()"
|
||||
></button>
|
||||
<button
|
||||
v-t="'cancel'"
|
||||
type="button"
|
||||
class="button"
|
||||
@click="close()"
|
||||
></button>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import Modal from "@/components/modal/modal";
|
||||
import SelectBox from '@/components/ui/select-box/select-box';
|
||||
import Slider from "@/components/ui/slider/slider";
|
||||
import { mapSelectOptions } from "@/utils/search-select-util";
|
||||
import { ACCEPTED_FILE_TYPES, typeToExt, isCompressableFileType } from "@/definitions/image-types";
|
||||
import { createCanvas } from "@/utils/canvas-util";
|
||||
import { saveBlobAsFile } from "@/utils/file-util";
|
||||
import messages from "./messages.json";
|
||||
|
||||
export default {
|
||||
i18n: { messages },
|
||||
components: {
|
||||
Modal,
|
||||
SelectBox,
|
||||
Slider,
|
||||
},
|
||||
data: () => ({
|
||||
name: "",
|
||||
type: ACCEPTED_FILE_TYPES[ 0 ],
|
||||
quality: 95,
|
||||
}),
|
||||
computed: {
|
||||
...mapState([
|
||||
"zCanvas",
|
||||
]),
|
||||
...mapGetters([
|
||||
"activeDocument",
|
||||
]),
|
||||
fileTypes() {
|
||||
return mapSelectOptions( ACCEPTED_FILE_TYPES );
|
||||
},
|
||||
hasQualityOptions() {
|
||||
return isCompressableFileType( this.type );
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.name = this.activeDocument.name.split( "." )[ 0 ];
|
||||
},
|
||||
methods: {
|
||||
async exportImage() {
|
||||
// TODO: here we clone the current zCanvas state to a new canvas
|
||||
// of the document dimensions. This will result in a loss of quality if
|
||||
// the zCanvas is zoomed out. We need to re-render all document properties
|
||||
// into the new canvas at its own scale.
|
||||
const { cvs, ctx } = createCanvas( this.activeDocument.width, this.activeDocument.height );
|
||||
ctx.drawImage( this.zCanvas.getElement(), 0, 0, cvs.width, cvs.height );
|
||||
const base64 = await fetch( cvs.toDataURL( this.type, this.quality / 100 ));
|
||||
const blob = await base64.blob();
|
||||
saveBlobAsFile( blob, `${this.name}.${typeToExt(this.type)}` );
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
this.$emit( "close" );
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
10
src/components/file-menu/export-image/messages.json
Normal file
10
src/components/file-menu/export-image/messages.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"en-US": {
|
||||
"exportImage": "Export image",
|
||||
"imageType": "Image type",
|
||||
"imageQuality": "Image quality",
|
||||
"fileName": "File name",
|
||||
"export": "Export",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
}
|
||||
76
src/components/modal/modal.vue
Normal file
76
src/components/modal/modal.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2019-2020 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
<template>
|
||||
<div class="modal">
|
||||
<slot name="header" />
|
||||
<div class="content">
|
||||
<slot name="content" />
|
||||
<div class="actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/component";
|
||||
|
||||
.modal {
|
||||
@include overlay();
|
||||
@include component();
|
||||
|
||||
h2 {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
padding: $spacing-small $spacing-medium;
|
||||
box-sizing: border-box;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
margin: $spacing-small;
|
||||
}
|
||||
}
|
||||
|
||||
@include large() {
|
||||
$width: 480px;
|
||||
$height: 300px;
|
||||
|
||||
width: $width;
|
||||
height: $height;
|
||||
left: calc(50% - #{$width / 2});
|
||||
top: calc(50% - #{$height / 2});
|
||||
}
|
||||
}
|
||||
</style>
|
||||
43
src/definitions/image-types.js
Normal file
43
src/definitions/image-types.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2019-2020 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
const JPEG = "image/jpg";
|
||||
const PNG = "image/png";
|
||||
const GIF = "image/gif";
|
||||
|
||||
export const ACCEPTED_FILE_TYPES = [ JPEG, PNG, GIF ];
|
||||
export const ACCEPTED_FILE_EXTENSIONS = [ "jpg", "jpeg", "png", "gif" ];
|
||||
|
||||
export const isCompressableFileType = type => type === JPEG;
|
||||
|
||||
export const typeToExt = type => {
|
||||
switch ( type ) {
|
||||
default:
|
||||
throw new Error( `Unsupported type ${type} provided` );
|
||||
case JPEG:
|
||||
return ACCEPTED_FILE_EXTENSIONS[ 0 ];
|
||||
case PNG:
|
||||
return ACCEPTED_FILE_EXTENSIONS[ 2 ];
|
||||
case GIF:
|
||||
return ACCEPTED_FILE_EXTENSIONS[ 3 ];
|
||||
}
|
||||
};
|
||||
@@ -21,4 +21,5 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
export const RESIZE_DOCUMENT = 1;
|
||||
export const DROPBOX_FILE_SELECTOR = 2;
|
||||
export const EXPORT_IMAGE = 2;
|
||||
export const DROPBOX_FILE_SELECTOR = 3;
|
||||
|
||||
@@ -75,7 +75,7 @@ import { isMobile } from "@/utils/environment-util";
|
||||
import store from "./store";
|
||||
import messages from "./messages.json";
|
||||
import {
|
||||
RESIZE_DOCUMENT, DROPBOX_FILE_SELECTOR
|
||||
RESIZE_DOCUMENT, EXPORT_IMAGE, DROPBOX_FILE_SELECTOR
|
||||
} from "@/definitions/modal-windows";
|
||||
|
||||
Vue.use( Vuex );
|
||||
@@ -115,6 +115,8 @@ export default {
|
||||
return null;
|
||||
case RESIZE_DOCUMENT:
|
||||
return () => import( "@/components/edit-menu/resize-document/resize-document" );
|
||||
case EXPORT_IMAGE:
|
||||
return () => import( "@/components/file-menu/export-image/export-image" );
|
||||
case DROPBOX_FILE_SELECTOR:
|
||||
return () => import( "@/components/dropbox-file-selector/dropbox-file-selector" );
|
||||
}
|
||||
|
||||
@@ -20,10 +20,14 @@ export default {
|
||||
toolboxOpened: false,
|
||||
optionsPanelOpened: true,
|
||||
blindActive: false,
|
||||
dialog: null,
|
||||
modal: null,
|
||||
notifications: [],
|
||||
windowSize: { width: window.innerWidth, height: window.innerHeight },
|
||||
dialog: null, // currently opened dialog
|
||||
modal: null, // currently opened modal
|
||||
notifications: [], // notification message queue
|
||||
zCanvas: null, // zCanvas instance
|
||||
windowSize: {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@@ -71,6 +75,9 @@ export default {
|
||||
clearNotifications( state ) {
|
||||
state.notifications = [];
|
||||
},
|
||||
setZCanvas( state, zCanvas ) {
|
||||
state.zCanvas = zCanvas;
|
||||
},
|
||||
/**
|
||||
* cache the resize in the store so components can react to these values
|
||||
* instead of maintaining multiple listeners at the expense of DOM trashing/performance hits
|
||||
|
||||
@@ -3,11 +3,23 @@
|
||||
.form {
|
||||
.wrapper {
|
||||
margin: $spacing-small 0;
|
||||
|
||||
|
||||
label,
|
||||
input, textarea, .select {
|
||||
input,
|
||||
textarea,
|
||||
.select,
|
||||
.vue-slider {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
width: 50% !important;
|
||||
}
|
||||
input, textarea {
|
||||
box-sizing: border-box;
|
||||
border-radius: $spacing-small;
|
||||
border: none;
|
||||
padding: #{$spacing-small + $spacing-xsmall} $spacing-medium;
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
src/utils/file-util.js
Normal file
40
src/utils/file-util.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2019-2020 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
const { URL } = window;
|
||||
|
||||
export const saveBlobAsFile = ( blob, fileName ) => {
|
||||
const blobURL = URL.createObjectURL( blob );
|
||||
const anchor = document.createElement( "a" );
|
||||
anchor.style.display = "none";
|
||||
anchor.href = blobURL;
|
||||
anchor.setAttribute( "download", fileName );
|
||||
|
||||
// Safari has no download attribute
|
||||
if ( typeof anchor.download === "undefined" ) {
|
||||
anchor.setAttribute( "target", "_blank" );
|
||||
}
|
||||
document.body.appendChild( anchor );
|
||||
anchor.click();
|
||||
document.body.removeChild( anchor );
|
||||
URL.revokeObjectURL( blobURL );
|
||||
};
|
||||
@@ -111,6 +111,13 @@ describe( "Vuex store", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it( "should be able to set the current zCanvas instance", () => {
|
||||
const state = { zCanvas: null };
|
||||
const canvas = { foo: "bar" };
|
||||
mutations.setZCanvas( state, canvas );
|
||||
expect( state.zCanvas ).toEqual( canvas );
|
||||
});
|
||||
|
||||
it( "should be able to set the window size", () => {
|
||||
const state = { windowSize: { width: 0, height: 0 }};
|
||||
const width = 500;
|
||||
|
||||
Reference in New Issue
Block a user