change layout: add tool menu
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Simon Vieille 2024-04-14 19:39:47 +02:00
parent e34262f494
commit 47a2365c60
Signed by: deblan
GPG key ID: 579388D585F70417
2 changed files with 343 additions and 269 deletions

View file

@ -1,7 +1,7 @@
<template> <template>
<div class=""> <div class="">
<TopMenu /> <TopMenu />
<main class="py-6 grid grid-cols-1 gap-y-8 lg:py-12 lg:grid-cols-[1fr_2fr] lg:gap-x-20"> <main class="pt-5 grid grid-cols-1 lg:grid-cols-[1fr_2fr] lg:gap-x-20">
<RouterView /> <RouterView />
</main> </main>
</div> </div>

View file

@ -1,287 +1,330 @@
<template> <template>
<div class="md:grid md:grid-cols-12 md:col-span-12"> <div class="md:grid md:grid-cols-12 md:col-span-12">
<div class="col-span-3 flex flex-col gap-4 lg:gap-6"> <div
<div class="drop-shadow-md rounded-md md:border border-zinc-300 p-5"> class="col-span-3 lg:mr-4 tools"
<div> ref="tools"
<TextareaForm >
id="heading" <div class="mb-2">
v-model="heading" <button
label="Heading" class="p-3 mr-2 rounded"
/> :class="{'bg-slate-300': this.tool == 'heading'}"
<RangeForm @click="this.tool = 'heading'"
id="heading-size" >Heading</button>
v-model="headingSize"
min="1"
max="10"
step="0.1"
/>
</div>
<div class="py-6"> <button
<TextareaForm class="p-3 mr-2 rounded"
id="subheading" :class="{'bg-slate-300': this.tool == 'author'}"
v-model="subheading" @click="this.tool = 'author'"
label="Subheading" >Author</button>
/>
<RangeForm
id="subheading-size"
v-model="subheadingSize"
min="1"
max="10"
step="0.1"
/>
</div>
<div class="pb-6"> <button
<CheckboxForm class="p-3 mr-2 rounded"
id="allow-html" :class="{'bg-slate-300': this.tool == 'background'}"
v-model="allowHtml" @click="this.tool = 'background'"
label="Allow HTML" >Background</button>
/>
</div>
<div class="pb-6"> <button
<RangeForm class="p-3 rounded"
id="content-padding" :class="{'bg-slate-300': this.tool == 'size'}"
v-model="contentPadding" @click="this.tool = 'size'"
min="0" >Size</button>
max="50"
step="0.05"
label="Padding"
/>
</div>
<div class="pb-3">
<InputForm
id="text-color"
v-model="textColor"
label="Text color"
type="color"
/>
</div>
<div class="pb-6">
<SelectForm
id="font"
v-model="font"
:items="fonts"
label="Font"
/>
</div>
<div>
<div class="text-center stroke-gray-400">
<AlignForm v-model="headingAlign" />
</div>
</div>
</div> </div>
<div class="drop-shadow-md rounded-md md:border border-zinc-300 p-5"> <div
<div> :class="{hidden: this.tool !== 'heading'}"
<InputForm class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
id="author" >
v-model="author" <div class="p-5">
label="Author" <div>
/> <TextareaForm
</div> id="heading"
<div class="py-6"> v-model="heading"
<FileForm label="Heading"
id="avatar"
v-model="avatar"
label="Avatar"
/>
</div>
<div>
<FileForm
id="logo"
v-model="logo"
label="Logo"
/>
</div>
<div class="stroke-gray-400 py-6">
<RangeForm
id="author-margin-bottom"
v-model="authorMarginBottom"
min="0"
max="500"
step="0.1"
label="Position"
/>
<RangeForm
id="author-padding"
v-model="authorPadding"
min="0"
max="50"
step="0.05"
label="Padding"
/>
</div>
<div class="pb-3">
<InputForm
id="author-text-color"
v-model="authorTextColor"
label="Text color"
type="color"
/>
</div>
<div>
<InputForm
id="background-color"
v-model="authorBackgroundColor"
label="Background color"
type="color"
/>
<RangeForm
id="author-background-opacity"
v-model="authorBackgroundOpacity"
min="0"
max="1"
step="0.05"
/>
</div>
<div>
<div class="text-center stroke-gray-400 pt-5">
<AlignForm v-model="authorAlign" />
</div>
</div>
</div>
</div>
<div class="col-span-3 md:px-10 flex flex-col gap-4 lg:gap-6">
<div class="drop-shadow-md rounded-md md:border border-zinc-300 p-5">
<div class="py-6">
<FileForm
id="background"
v-model="background"
label="Background"
/>
</div>
<div>
<InputForm
id="background-hover"
v-model="backgroundHover"
label="Background hover"
type="color"
/>
<RangeForm
id="background-hover-opacity"
v-model="backgroundHoverOpacity"
min="0"
max="1"
step="0.05"
/>
</div>
</div>
<div class="drop-shadow-md rounded-md md:border border-zinc-300 p-5">
<div>
<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">
<InputForm
id="width"
v-model="width"
type="number"
/> />
<InputForm <RangeForm
id="height" id="heading-size"
v-model="height" v-model="headingSize"
type="number" min="1"
max="20"
step="0.1"
/> />
</div> </div>
<RangeForm <div class="py-6">
id="quality" <TextareaForm
v-model="quality" id="subheading"
min="0" v-model="subheading"
max="1" label="Subheading"
step="0.05" />
/> <RangeForm
id="subheading-size"
v-model="subheadingSize"
min="1"
max="20"
step="0.1"
/>
</div>
<div class="pb-6">
<CheckboxForm
id="allow-html"
v-model="allowHtml"
label="Allow HTML"
/>
</div>
<div class="pb-6">
<RangeForm
id="content-padding"
v-model="contentPadding"
min="0"
max="300"
step="0.05"
label="Padding"
/>
</div>
<div class="pb-3">
<InputForm
id="text-color"
v-model="textColor"
label="Text color"
type="color"
/>
</div>
<div class="pb-6">
<SelectForm
id="font"
v-model="font"
:items="fonts"
label="Font"
/>
</div>
<div>
<div class="text-center stroke-gray-400">
<AlignForm v-model="headingAlign" />
</div>
</div>
</div>
</div>
<div
:class="{hidden: this.tool !== 'author'}"
class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
>
<div class="p-5">
<div>
<InputForm
id="author"
v-model="author"
label="Author"
/>
</div>
<div class="py-6">
<FileForm
id="avatar"
v-model="avatar"
label="Avatar"
/>
</div>
<div>
<FileForm
id="logo"
v-model="logo"
label="Logo"
/>
</div>
<div class="stroke-gray-400 py-6">
<RangeForm
id="author-margin-bottom"
v-model="authorMarginBottom"
min="0"
max="500"
step="0.1"
label="Position"
/>
<RangeForm
id="author-padding"
v-model="authorPadding"
min="0"
max="50"
step="0.05"
label="Padding"
/>
</div>
<div class="pb-3">
<InputForm
id="author-text-color"
v-model="authorTextColor"
label="Text color"
type="color"
/>
</div>
<div>
<InputForm
id="background-color"
v-model="authorBackgroundColor"
label="Background color"
type="color"
/>
<RangeForm
id="author-background-opacity"
v-model="authorBackgroundOpacity"
min="0"
max="1"
step="0.05"
/>
</div>
<div>
<div class="text-center stroke-gray-400 pt-5">
<AlignForm v-model="authorAlign" />
</div>
</div>
</div>
</div>
<div
:class="{hidden: this.tool !== 'background'}"
class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
>
<div class="p-5">
<div class="pb-6">
<FileForm
id="background"
v-model="background"
label="Background"
/>
</div>
<div>
<InputForm
id="background-hover"
v-model="backgroundHover"
label="Background hover"
type="color"
/>
<RangeForm
id="background-hover-opacity"
v-model="backgroundHoverOpacity"
min="0"
max="1"
step="0.05"
/>
</div>
</div>
</div>
<div
:class="{hidden: this.tool !== 'size'}"
class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
>
<div class="p-5">
<div>
<label
class="block text-sm font-medium text-gray-700"
for="width"
>Size</label>
<div class="grid grid-cols-2 gap-x-4">
<InputForm
id="width"
v-model="width"
type="number"
/>
<InputForm
id="height"
v-model="height"
type="number"
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div <div
ref="ogwrapper" ref="ogwrapper"
class="col-span-6 space-y-6" class="og-wrapper col-span-9 lg:pl-10"
> >
<div <div class="og-wrapper-content">
ref="og"
class="og"
:style="ogStyle()"
>
<div <div
class="og-image" ref="og"
:style="ogImageStyle()" class="og"
:style="ogStyle()"
> >
<div <div
class="og-image-hover" class="og-image"
:style="ogImageHoverStyle()" ref="ogimage"
:style="ogImageStyle()"
> >
<div <div
class="h-full flex flex-col justify-between" class="og-image-hover"
:style="ogContentStyle()" :style="ogImageHoverStyle()"
> >
<div v-if="allowHtml">
<h1
class="leading-none"
:style="ogHeadingStyle()"
v-html="heading"
/>
<p
class="mt-10 mb-16 leading-tight"
:style="ogSubheadingStyle()"
v-html="subheading"
/>
</div>
<div v-else>
<h1
class="leading-none"
:style="ogHeadingStyle()"
v-text="heading"
/>
<p
class="mt-10 mb-16 leading-tight"
:style="ogSubheadingStyle()"
v-text="subheading"
/>
</div>
<div <div
class="w-full flex flex-row items-center og-image--footer" class="h-full flex flex-col justify-between"
:style="ogAuthorStyle()" :style="ogContentStyle()"
> >
<img <div v-if="allowHtml">
v-if="avatar" <h1
class="h-10 w-10 rounded-full" class="leading-none"
:src="avatar" :style="ogHeadingStyle()"
> v-html="heading"
/>
<p
class="mt-10 mb-16 leading-tight"
:style="ogSubheadingStyle()"
v-html="subheading"
/>
</div>
<div v-else>
<h1
class="leading-none"
:style="ogHeadingStyle()"
v-text="heading"
/>
<p
class="mt-10 mb-16 leading-tight"
:style="ogSubheadingStyle()"
v-text="subheading"
/>
</div>
<span
class="mx-4"
:style="ogAuthorTextStyle()"
v-text="author"
/>
<img <div
v-if="logo" class="w-full flex flex-row items-center og-image--footer"
class="h-10 w-10 rounded-full" :style="ogAuthorStyle()"
:src="logo"
> >
<img
v-if="avatar"
class="h-10 w-10 rounded-full"
:src="avatar"
>
<span
class="mx-4"
:style="ogAuthorTextStyle()"
v-text="author"
/>
<img
v-if="logo"
class="h-10 w-10 rounded-full"
:src="logo"
>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center mt-3">
<DownloadButton <DownloadButton
class="rounded-l-md" class="rounded-l-md"
label="PNG" label="PNG"
@ -294,6 +337,8 @@
/> />
</div> </div>
</div> </div>
<div id="bottom"></div>
</div> </div>
</template> </template>
@ -322,6 +367,7 @@ export default {
}, },
data() { data() {
return { return {
tool: localStorage.getItem('tool') ?? 'heading',
author: localStorage.getItem('author'), author: localStorage.getItem('author'),
authorPadding: localStorage.getItem('authorPadding') ?? 0, authorPadding: localStorage.getItem('authorPadding') ?? 0,
authorAlign: localStorage.getItem('authorAlign') ?? 'left', authorAlign: localStorage.getItem('authorAlign') ?? 'left',
@ -343,7 +389,6 @@ export default {
subheadingSize: localStorage.getItem('subheadingSize') ?? 2, subheadingSize: localStorage.getItem('subheadingSize') ?? 2,
width: localStorage.getItem('width') ?? 1920, width: localStorage.getItem('width') ?? 1920,
height: localStorage.getItem('height') ?? 1080, height: localStorage.getItem('height') ?? 1080,
quality: localStorage.getItem('quality') ?? 0.9,
contentPadding: localStorage.getItem('contentPadding') ?? 2.5, contentPadding: localStorage.getItem('contentPadding') ?? 2.5,
allowHtml: localStorage.getItem('allowHtml'), allowHtml: localStorage.getItem('allowHtml'),
ogMaxWidth: 100, ogMaxWidth: 100,
@ -363,6 +408,9 @@ export default {
} }
}, },
watch: { watch: {
tool(value) {
localStorage.setItem('tool', value)
},
author(value) { author(value) {
localStorage.setItem('author', value) localStorage.setItem('author', value)
}, },
@ -423,9 +471,6 @@ export default {
height(value) { height(value) {
localStorage.setItem('height', value) localStorage.setItem('height', value)
}, },
quality(value) {
localStorage.setItem('quality', value)
},
contentPadding(value) { contentPadding(value) {
localStorage.setItem('contentPadding', value) localStorage.setItem('contentPadding', value)
}, },
@ -444,18 +489,25 @@ export default {
ogStyle() { ogStyle() {
return { return {
color: this.textColor, color: this.textColor,
width: `${this.width}px`,
maxWidth: `${this.ogMaxWidth}px`,
fontFamily: this.font, fontFamily: this.font,
maxHeight: `${this.ogMaxHeight}px`,
maxWidth: `${this.ogMaxWidth}px !important`,
aspectRatio: `${this.width}/${this.height}`,
} }
}, },
ogImageStyle() { ogImageStyle() {
if (this.background) { let value = {
return { maxHeight: `${this.ogMaxHeight}px`,
background: `url(${this.background}) center`, maxWidth: `${this.ogMaxWidth}px`,
backgroundSize: 'cover', aspectRatio: `${this.width}/${this.height}`,
}
} }
if (this.background) {
value.background = `url(${this.background}) center`
value.backgroundSize = 'cover'
}
return value
}, },
ogHeadingStyle() { ogHeadingStyle() {
return { return {
@ -513,7 +565,9 @@ export default {
return { return {
background, background,
aspectRatio: `${this.width}/${this.height}` aspectRatio: `${this.width}/${this.height}`,
maxHeight: `${this.ogMaxHeight}px`,
maxWidth: `${this.ogMaxWidth}px`,
} }
}, },
ogContentStyle() { ogContentStyle() {
@ -530,12 +584,12 @@ export default {
return this.download(toJpeg, 'image.jpg') return this.download(toJpeg, 'image.jpg')
}, },
download(callback, filename) { download(callback, filename) {
const element = this.$refs.og const element = this.$refs.ogimage
callback(this.$refs.og, { callback(element, {
canvasWidth: this.width, canvasWidth: this.width,
canvasHeight: this.height, canvasHeight: this.height,
quality: this.quality, quality: 1,
}) })
.then((dataUrl) => { .then((dataUrl) => {
const link = document.createElement('a') const link = document.createElement('a')
@ -552,7 +606,8 @@ export default {
}, },
updateOgMaxWidth() { updateOgMaxWidth() {
this.$refs.og.style.display = 'none' this.$refs.og.style.display = 'none'
this.ogMaxWidth = this.$refs.ogwrapper.offsetWidth this.ogMaxWidth = this.$refs.ogwrapper.offsetWidth - 30
this.ogMaxHeight = this.$refs.tools.offsetHeight - 50
this.$refs.og.style.display = 'block' this.$refs.og.style.display = 'block'
}, },
} }
@ -560,12 +615,31 @@ export default {
</script> </script>
<style> <style>
#bottom {
position: fixed;
bottom: 0;
}
.tool {
max-height: calc(100vh - 250px);
overflow: auto;
}
.tools {
height: calc(100vh - 250px);
}
.og { .og {
background: #cecece;
max-width: 10px;
margin: auto; margin: auto;
} }
.og-wrapper-content {
background-color: #c1c1c1;
background-image: linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%), linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
}
.og-heading { .og-heading {
font-size: 5em; font-size: 5em;
} }