transform form with components
This commit is contained in:
parent
90dd03882f
commit
0d4a468014
|
@ -3,75 +3,53 @@
|
|||
<div class="col-span-3 flex flex-col gap-4 lg:gap-6">
|
||||
<div class="drop-shadow-md rounded-md border border-zinc-300 p-5">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="heading">Heading</label>
|
||||
<input type="text" class="mt-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="heading" id="heading">
|
||||
<input type="range" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" min="1" max="10" step="0.1" v-model="headingSize" id="heading-size">
|
||||
<InputForm id="heading" label="Heading" v-model="heading" />
|
||||
<RangeForm id="heading-size" min="1" max="10" step="0.1" v-model="headingSize" />
|
||||
</div>
|
||||
|
||||
<div class="py-6">
|
||||
<label class="block text-sm font-medium text-gray-700" for="subheading">Subheading</label>
|
||||
<textarea class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="subheading" id="subheading"></textarea>
|
||||
<input type="range" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" min="1" max="10" step="0.1" v-model="subheadingSize" id="subheading-size">
|
||||
<TextareaForm id="subheading" label="Subheading" v-model="subheading" />
|
||||
<RangeForm id="subheading-size" min="1" max="10" step="0.1" v-model="subheadingSize" />
|
||||
</div>
|
||||
|
||||
<div class="pb-6">
|
||||
<label class="block text-sm font-medium text-gray-700" for="subheading">Font</label>
|
||||
|
||||
<select v-model="font" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<option v-for="font in fonts" :key="font.value" :value="font.value" v-text="font.label"></option>
|
||||
</select>
|
||||
<SelectForm id="font" :items="fonts" label="Font" v-model="font" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-center stroke-gray-400">
|
||||
<button v-on:click="headingAlign = 'left'">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: headingAlign === 'left'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m4.498 10.5h5.997"/><path d="m4.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
|
||||
<button v-on:click="headingAlign = 'center'">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: headingAlign === 'center'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m7.498 10.5h5.997"/><path d="m5.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
|
||||
<button v-on:click="headingAlign = 'right'">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: headingAlign === 'right'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m10.498 10.5h5.997"/><path d="m6.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
<AlignForm v-model="headingAlign" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drop-shadow-md rounded-md border border-zinc-300 p-5">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="author">Author</label>
|
||||
<input type="text" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="author" id="author">
|
||||
<InputForm id="author" label="Author" v-model="author" />
|
||||
</div>
|
||||
|
||||
<div class="py-6">
|
||||
<label class="block text-sm font-medium text-gray-700" for="avatar">Avatar</label>
|
||||
<input type="file" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-on:change="updateAvatar" id="avatar">
|
||||
<FileForm id="avatar" label="Avatar" v-model="avatar" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="logo">Logo</label>
|
||||
<input type="file" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-on:change="updateLogo" id="logo">
|
||||
<FileForm id="logo" label="Logo" v-model="logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-3 px-10 flex flex-col gap-4 lg:gap-6">
|
||||
<div class="drop-shadow-md rounded-md border border-zinc-300 p-5">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="text-color">Text color</label>
|
||||
<input type="color" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="textColor" id="text-color">
|
||||
<InputForm id="text-color" label="Text color" type="color" v-model="textColor" />
|
||||
</div>
|
||||
|
||||
<div class="py-6">
|
||||
<label class="block text-sm font-medium text-gray-700" for="background">Background</label>
|
||||
<input type="file" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-on:change="updateBackground" id="background">
|
||||
<FileForm id="background" label="Background" v-model="background" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="background-hover">Background hover</label>
|
||||
<input type="color" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="backgroundHover" id="background-hover">
|
||||
<input type="range" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" min="0" max="1" step="0.1" v-model="backgroundHoverOpacity" id="background-hover-opacity">
|
||||
<InputForm id="background-hover" label="Background hover" type="color" v-model="backgroundHover" />
|
||||
<RangeForm id="background-hover-opacity" min="0" max="1" step="0.05" v-model="backgroundHoverOpacity" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="drop-shadow-md rounded-md border border-zinc-300 p-5">
|
||||
|
@ -79,17 +57,15 @@
|
|||
<label class="block text-sm font-medium text-gray-700" for="width">Size and quality</label>
|
||||
|
||||
<div class="grid grid-cols-2 gap-x-4">
|
||||
<input type="number" class="mt-1 inline rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="width" id="width">
|
||||
<input type="number" class="mt-1 inline rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" v-model="height">
|
||||
<InputForm id="width" type="number" v-model="width" />
|
||||
<InputForm id="height" type="number" v-model="height" />
|
||||
</div>
|
||||
|
||||
<input type="range" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" min="0" max="1" step="0.1" v-model="quality">
|
||||
<RangeForm id="quality" min="0" max="1" step="0.05" v-model="quality" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700" for="width">Box padding</label>
|
||||
|
||||
<input type="range" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" min="2.5" max="10" step="0.1" v-model="contentPadding">
|
||||
<RangeForm id="content-padding" min="2.5" max="10" step="0.05" v-model="contentPadding" label="Box padding" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,13 +91,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md text-center" role="group">
|
||||
<button type="button" class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-l-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-blue-500 dark:focus:text-white" v-on:click="downloadAsPng">
|
||||
PNG
|
||||
</button>
|
||||
<button type="button" class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-r-md hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-blue-500 dark:focus:text-white" v-on:click="downloadAsJpeg">
|
||||
JPEG
|
||||
</button>
|
||||
<div class="text-center">
|
||||
<DownloadButton v-on:click="downloadAsPng" class="rounded-l-md" label="PNG"/>
|
||||
<DownloadButton v-on:click="downloadAsJpeg" class="rounded-r-md" label="JPEG"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -153,10 +125,25 @@
|
|||
|
||||
<script>
|
||||
import { hexToRgb } from '../util/color'
|
||||
import { toBase64 } from '../util/file'
|
||||
import { toPng, toJpeg } from 'html-to-image'
|
||||
import InputForm from '../ui/InputForm'
|
||||
import RangeForm from '../ui/RangeForm'
|
||||
import TextareaForm from '../ui/TextareaForm'
|
||||
import FileForm from '../ui/FileForm'
|
||||
import SelectForm from '../ui/SelectForm'
|
||||
import AlignForm from '../ui/AlignForm'
|
||||
import DownloadButton from '../ui/DownloadButton'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InputForm,
|
||||
RangeForm,
|
||||
TextareaForm,
|
||||
FileForm,
|
||||
SelectForm,
|
||||
AlignForm,
|
||||
DownloadButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
author: localStorage.getItem('author'),
|
||||
|
@ -239,15 +226,6 @@ export default {
|
|||
padding: `${this.contentPadding}em`,
|
||||
}
|
||||
},
|
||||
async updateBackground(event) {
|
||||
this.background = await this.convertFileToBase64(event.target.files)
|
||||
},
|
||||
async updateLogo(event) {
|
||||
this.logo = await this.convertFileToBase64(event.target.files)
|
||||
},
|
||||
async updateAvatar(event) {
|
||||
this.avatar = await this.convertFileToBase64(event.target.files)
|
||||
},
|
||||
downloadAsPng() {
|
||||
return this.download(toPng, 'image.png')
|
||||
},
|
||||
|
@ -275,13 +253,6 @@ export default {
|
|||
console.error('oops, something went wrong!', error);
|
||||
})
|
||||
},
|
||||
async convertFileToBase64(files) {
|
||||
if (files.length) {
|
||||
return await toBase64(files[0])
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
updateOgMaxWidth() {
|
||||
this.$refs.og.style.display = 'none'
|
||||
this.ogMaxWidth = this.$refs.ogwrapper.offsetWidth
|
||||
|
|
32
src/ui/AlignForm.vue
Normal file
32
src/ui/AlignForm.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<button v-on:click="update('left')">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: modelValue === 'left'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m4.498 10.5h5.997"/><path d="m4.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
|
||||
<button v-on:click="update('center')">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: modelValue === 'center'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m7.498 10.5h5.997"/><path d="m5.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
|
||||
<button v-on:click="update('right')">
|
||||
<svg class="inline border-gray-400 rounded-md" :class="{border: modelValue === 'right'}" height="40" viewBox="0 0 21 21" width="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 6.5h12"/><path d="m10.498 10.5h5.997"/><path d="m6.5 14.5h9.995"/></g></svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
methods: {
|
||||
update(value) {
|
||||
this.$emit('update:modelValue', value)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
24
src/ui/DownloadButton.vue
Normal file
24
src/ui/DownloadButton.vue
Normal file
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-blue-500 dark:focus:text-white" v-on:click="downloadAsJpeg"
|
||||
:class="class"
|
||||
v-text="label"
|
||||
>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
43
src/ui/FileForm.vue
Normal file
43
src/ui/FileForm.vue
Normal file
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<label class="block text-sm font-medium text-gray-700" :for="id" v-if="label" v-text="label"></label>
|
||||
<input
|
||||
class="mt-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||||
type="file"
|
||||
:id="id"
|
||||
:class="class"
|
||||
@change="convertFileToBase64($event.target.files)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toBase64 } from '../util/file'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
methods: {
|
||||
async convertFileToBase64(files) {
|
||||
if (files.length) {
|
||||
const value = await toBase64(files[0])
|
||||
|
||||
this.$emit('update:modelValue', value)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
39
src/ui/InputForm.vue
Normal file
39
src/ui/InputForm.vue
Normal file
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<label class="block text-sm font-medium text-gray-700" :for="id" v-if="label" v-text="label"></label>
|
||||
<input
|
||||
class="mt-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||||
:type="type"
|
||||
:id="id"
|
||||
:value="modelValue"
|
||||
:class="class"
|
||||
@change="$emit('change', $event)"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'text'
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
"class": {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
}
|
||||
</script>
|
51
src/ui/RangeForm.vue
Normal file
51
src/ui/RangeForm.vue
Normal file
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<label class="block text-sm font-medium text-gray-700" :for="id" v-if="label" v-text="label"></label>
|
||||
<input
|
||||
class="mt-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||||
type="range"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
:id="id"
|
||||
:class="class"
|
||||
:value="modelValue"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 100,
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0.1,
|
||||
},
|
||||
"class": {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
}
|
||||
</script>
|
38
src/ui/SelectForm.vue
Normal file
38
src/ui/SelectForm.vue
Normal file
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<label class="block text-sm font-medium text-gray-700" :for="id" v-if="label" v-text="label"></label>
|
||||
<select
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||||
:id="id"
|
||||
:class="class"
|
||||
:value="modelValue"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
>
|
||||
<option v-for="item in items" :key="item.value" :value="item.value" v-text="item.label"></option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
"class": {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
}
|
||||
</script>
|
37
src/ui/TextareaForm.vue
Normal file
37
src/ui/TextareaForm.vue
Normal file
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<label class="block text-sm font-medium text-gray-700" :for="id" v-if="label" v-text="label"></label>
|
||||
<textarea
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||||
:id="id"
|
||||
:class="class"
|
||||
:value="modelValue"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
></textarea>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
defaut: 'text'
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
"class": {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
}
|
||||
</script>
|
Loading…
Reference in a new issue