transform form with components

This commit is contained in:
Simon Vieille 2023-02-24 21:40:50 +01:00
parent 90dd03882f
commit 0d4a468014
Signed by: deblan
GPG key ID: 579388D585F70417
8 changed files with 300 additions and 65 deletions

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>