Simon Vieille
431f661740
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
remove log
697 lines
19 KiB
Vue
697 lines
19 KiB
Vue
<template>
|
|
<div class="flex">
|
|
<div
|
|
class="lg:mr-4 tools"
|
|
ref="tools"
|
|
>
|
|
<div class="mb-2 flex flex-row">
|
|
<button
|
|
v-for="(name, value) in tools"
|
|
class="p-3 mr-2 rounded"
|
|
:class="{'bg-slate-300': this.tool == value}"
|
|
@click="this.tool = value"
|
|
v-text="name"
|
|
></button>
|
|
</div>
|
|
|
|
<div
|
|
:class="{hidden: this.tool !== 'heading'}"
|
|
class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
|
|
>
|
|
<div class="p-5">
|
|
<div>
|
|
<TextareaForm
|
|
id="heading"
|
|
v-model="heading"
|
|
label="Heading"
|
|
/>
|
|
<RangeForm
|
|
id="heading-size"
|
|
v-model="headingSize"
|
|
min="1"
|
|
max="20"
|
|
step="0.1"
|
|
/>
|
|
</div>
|
|
|
|
<div class="py-6">
|
|
<TextareaForm
|
|
id="subheading"
|
|
v-model="subheading"
|
|
label="Subheading"
|
|
/>
|
|
<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="heading-spacing"
|
|
v-model="headingSpacing"
|
|
min="0"
|
|
max="300"
|
|
step="0.05"
|
|
label="Spacing"
|
|
/>
|
|
</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>
|
|
<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 !== 'font'}"
|
|
class="drop-shadow-md rounded-md md:border border-zinc-300 tool"
|
|
>
|
|
<div class="p-5">
|
|
<SelectForm
|
|
id="font"
|
|
v-model="font"
|
|
:items="fonts"
|
|
label="Font"
|
|
/>
|
|
</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 !== 'sizes'}"
|
|
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"
|
|
>Sizes</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>
|
|
|
|
<h3 class="mt-4 block text-sm font-medium text-gray-700">Recommendations</h3>
|
|
|
|
<button
|
|
v-for="(sizes, name) in sizesRecommendations"
|
|
class="bg-lime-300 rounded px-3 py-2 mt-1 mr-1"
|
|
@click="this.setSizes(sizes[0], sizes[1])"
|
|
v-text="name"
|
|
></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
ref="ogwrapper"
|
|
class="og-wrapper lg:pl-10"
|
|
>
|
|
<svg data-marpit-svg :viewBox="[0, 0, width, height].join(' ')">
|
|
<foreignObject
|
|
:width="width"
|
|
:height="height"
|
|
:style="ogSvgStyle()"
|
|
>
|
|
<div class="og-wrapper-content">
|
|
<div
|
|
ref="og"
|
|
class="og"
|
|
:style="ogStyle()"
|
|
>
|
|
<div
|
|
class="og-image"
|
|
ref="ogimage"
|
|
:style="ogImageStyle()"
|
|
>
|
|
<div
|
|
class="og-image-hover"
|
|
:style="ogImageHoverStyle()"
|
|
>
|
|
<div
|
|
class="h-full flex flex-col justify-between"
|
|
:style="ogContentStyle()"
|
|
>
|
|
<div v-if="allowHtml">
|
|
<h1
|
|
class="leading-none"
|
|
:style="ogHeadingStyle()"
|
|
v-html="heading"
|
|
/>
|
|
<p
|
|
class="leading-tight"
|
|
:style="ogSubheadingStyle()"
|
|
v-html="subheading"
|
|
/>
|
|
</div>
|
|
<div v-else>
|
|
<h1
|
|
class="leading-none"
|
|
:style="ogHeadingStyle()"
|
|
v-text="heading"
|
|
/>
|
|
<p
|
|
class="leading-tight"
|
|
:style="ogSubheadingStyle()"
|
|
v-text="subheading"
|
|
/>
|
|
</div>
|
|
|
|
|
|
<div
|
|
class="w-full flex flex-row items-center og-image--footer"
|
|
:style="ogAuthorStyle()"
|
|
>
|
|
<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>
|
|
</foreignObject>
|
|
</svg>
|
|
<div class="text-center mt-3">
|
|
<DownloadButton
|
|
class="rounded-l-md"
|
|
label="PNG"
|
|
@click="downloadAsPng"
|
|
/>
|
|
<DownloadButton
|
|
class="rounded-r-md"
|
|
label="JPEG"
|
|
@click="downloadAsJpeg"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { hexToRgb } from '../util/color'
|
|
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 CheckboxForm from '../ui/CheckboxForm'
|
|
import AlignForm from '../ui/AlignForm'
|
|
import DownloadButton from '../ui/DownloadButton'
|
|
|
|
export default {
|
|
components: {
|
|
InputForm,
|
|
RangeForm,
|
|
TextareaForm,
|
|
FileForm,
|
|
SelectForm,
|
|
CheckboxForm,
|
|
AlignForm,
|
|
DownloadButton,
|
|
},
|
|
data() {
|
|
return {
|
|
tool: localStorage.getItem('tool') ?? 'heading',
|
|
author: localStorage.getItem('author') ?? 'Author name',
|
|
authorPadding: localStorage.getItem('authorPadding') ?? 10,
|
|
authorAlign: localStorage.getItem('authorAlign') ?? 'left',
|
|
authorTextColor: localStorage.getItem('authorTextColor') ?? '#fff9e6',
|
|
authorMarginBottom: localStorage.getItem('authorMarginBottom') ?? 200,
|
|
authorBackgroundColor: localStorage.getItem('authorBackgroundColor') ?? '#fc5705',
|
|
authorBackgroundOpacity: localStorage.getItem('authorBackgroundOpacity') ?? 1,
|
|
heading: localStorage.getItem('heading') ?? 'Heading',
|
|
subheading: localStorage.getItem('subheading') ?? 'Subheading',
|
|
headingSpacing: localStorage.getItem('headingSpacing') ?? 10,
|
|
headingAlign: localStorage.getItem('headingAlign') ?? 'left',
|
|
font: localStorage.getItem('font') ?? 'Trebuchet MS',
|
|
avatar: localStorage.getItem('avatar'),
|
|
logo: localStorage.getItem('logo'),
|
|
textColor: localStorage.getItem('textColor') ?? '#fc5705',
|
|
background: localStorage.getItem('background'),
|
|
backgroundHover: localStorage.getItem('backgroundHover') ?? '#fff9e6',
|
|
backgroundHoverOpacity: localStorage.getItem('backgroundHoverOpacity') ?? 1,
|
|
headingSize: localStorage.getItem('headingSize') ?? 4,
|
|
subheadingSize: localStorage.getItem('subheadingSize') ?? 2,
|
|
width: localStorage.getItem('width') ?? 1200,
|
|
height: localStorage.getItem('height') ?? 600,
|
|
contentPadding: localStorage.getItem('contentPadding') ?? 20,
|
|
allowHtml: localStorage.getItem('allowHtml'),
|
|
ogMaxWidth: 100,
|
|
tools: {
|
|
heading: 'Heading',
|
|
author: 'Author',
|
|
font: 'Font',
|
|
background: 'Background',
|
|
sizes: 'Sizes',
|
|
},
|
|
sizesRecommendations: {
|
|
Mastodon: [1200, 600],
|
|
LinkedIn: [1200, 627],
|
|
Instagram: [1200, 630],
|
|
X: [1200, 675],
|
|
},
|
|
fonts: [
|
|
{label: 'Arial (sans-serif)', value : 'Arial'},
|
|
{label: 'Brush Script MT (cursive)', value : 'Brush Script MT'},
|
|
{label: 'Calibri (sans-serif)', value : 'Calibri'},
|
|
{label: 'Courier New (monospace)', value : 'Courier New'},
|
|
{label: 'Garamond (serif)', value : 'Garamond'},
|
|
{label: 'Georgia (serif)', value : 'Georgia'},
|
|
{label: 'Tahoma (sans-serif)', value : 'Tahoma'},
|
|
{label: 'Times New Roman (serif)', value : 'Times New Roman'},
|
|
{label: 'Trebuchet MS (sans-serif)', value : 'Trebuchet MS'},
|
|
{label: 'Verdana (sans-serif)', value : 'Verdana'},
|
|
],
|
|
}
|
|
},
|
|
watch: {
|
|
tool(value) {
|
|
localStorage.setItem('tool', value)
|
|
},
|
|
author(value) {
|
|
localStorage.setItem('author', value)
|
|
},
|
|
authorAlign(value) {
|
|
localStorage.setItem('authorAlign', value)
|
|
},
|
|
authorPadding(value) {
|
|
localStorage.setItem('authorPadding', value)
|
|
},
|
|
authorBackgroundColor(value) {
|
|
localStorage.setItem('authorBackgroundColor', value)
|
|
},
|
|
authorBackgroundOpacity(value) {
|
|
localStorage.setItem('authorBackgroundOpacity', value)
|
|
},
|
|
authorTextColor(value) {
|
|
localStorage.setItem('authorTextColor', value)
|
|
},
|
|
authorMarginBottom(value) {
|
|
localStorage.setItem('authorMarginBottom', value)
|
|
},
|
|
heading(value) {
|
|
localStorage.setItem('heading', value)
|
|
},
|
|
subheading(value) {
|
|
localStorage.setItem('subheading', value)
|
|
},
|
|
headingSpacing(value) {
|
|
localStorage.setItem('headingSpacing', value)
|
|
},
|
|
avatar(value) {
|
|
localStorage.setItem('avatar', value)
|
|
},
|
|
logo(value) {
|
|
localStorage.setItem('logo', value)
|
|
},
|
|
textColor(value) {
|
|
localStorage.setItem('textColor', value)
|
|
},
|
|
background(value) {
|
|
localStorage.setItem('background', value)
|
|
},
|
|
backgroundHover(value) {
|
|
localStorage.setItem('backgroundHover', value)
|
|
},
|
|
backgroundHoverOpacity(value) {
|
|
localStorage.setItem('backgroundHoverOpacity', value)
|
|
},
|
|
headingSize(value) {
|
|
localStorage.setItem('headingSize', value)
|
|
},
|
|
subheadingSize(value) {
|
|
localStorage.setItem('subheadingSize', value)
|
|
},
|
|
headingAlign(value) {
|
|
localStorage.setItem('headingAlign', value)
|
|
},
|
|
width(value) {
|
|
localStorage.setItem('width', value)
|
|
},
|
|
height(value) {
|
|
localStorage.setItem('height', value)
|
|
},
|
|
contentPadding(value) {
|
|
localStorage.setItem('contentPadding', value)
|
|
},
|
|
font(value) {
|
|
localStorage.setItem('font', value)
|
|
},
|
|
allowHtml(value) {
|
|
localStorage.setItem('allowHtml', value)
|
|
},
|
|
},
|
|
mounted() {
|
|
this.updateOgMaxWidth()
|
|
window.addEventListener('resize', this.updateOgMaxWidth, false)
|
|
},
|
|
methods: {
|
|
ogSvgStyle() {
|
|
return {
|
|
maxHeight: `${this.ogMaxHeight}px`,
|
|
maxWidth: `${this.ogMaxWidth}px !important`,
|
|
aspectRatio: `${this.width}/${this.height}`,
|
|
}
|
|
},
|
|
ogStyle() {
|
|
return {
|
|
color: this.textColor,
|
|
fontFamily: this.font,
|
|
maxHeight: `${this.ogMaxHeight}px`,
|
|
maxWidth: `${this.ogMaxWidth}px !important`,
|
|
aspectRatio: `${this.width}/${this.height}`,
|
|
}
|
|
},
|
|
ogImageStyle() {
|
|
let value = {
|
|
aspectRatio: `${this.width}/${this.height}`,
|
|
}
|
|
|
|
if (this.background) {
|
|
value.background = `url(${this.background}) center`
|
|
value.backgroundSize = 'cover'
|
|
}
|
|
|
|
return value
|
|
},
|
|
ogHeadingStyle() {
|
|
return {
|
|
fontSize: `${this.headingSize}em`,
|
|
textAlign: this.headingAlign,
|
|
}
|
|
},
|
|
ogSubheadingStyle() {
|
|
return {
|
|
fontSize: `${this.subheadingSize}em`,
|
|
textAlign: this.headingAlign,
|
|
marginTop: `${this.headingSpacing}px`,
|
|
}
|
|
},
|
|
ogAuthorStyle() {
|
|
let background = 'none'
|
|
|
|
if (this.backgroundHover) {
|
|
const colors = hexToRgb(this.authorBackgroundColor)
|
|
background = `rgba(${colors.red}, ${colors.green}, ${colors.blue}, ${this.authorBackgroundOpacity})`
|
|
}
|
|
|
|
return {
|
|
textAlign: this.authorAlign,
|
|
color: this.authorTextColor,
|
|
marginBottom: `${this.authorMarginBottom}px`,
|
|
padding: `${this.authorPadding}px`,
|
|
marginLeft: `-${this.contentPadding}px`,
|
|
width: `calc(${this.contentPadding * 2}px + 100%)`,
|
|
background,
|
|
}
|
|
},
|
|
ogAuthorTextStyle() {
|
|
let value = {}
|
|
|
|
if (this.authorAlign === 'left') {
|
|
value.marginRight = 'auto !important'
|
|
} else if (this.authorAlign === 'center') {
|
|
value.marginLeft = 'auto !important'
|
|
value.marginRight = 'auto !important'
|
|
} else if (this.authorAlign === 'right') {
|
|
value.marginLeft = 'auto !important'
|
|
}
|
|
|
|
return value
|
|
},
|
|
ogImageHoverStyle() {
|
|
let background = 'none'
|
|
|
|
if (this.backgroundHover) {
|
|
const colors = hexToRgb(this.backgroundHover)
|
|
background = `rgba(${colors.red}, ${colors.green}, ${colors.blue}, ${this.backgroundHoverOpacity})`
|
|
}
|
|
|
|
return {
|
|
background,
|
|
aspectRatio: `${this.width}/${this.height}`,
|
|
maxHeight: `${this.ogMaxHeight}px`,
|
|
maxWidth: `${this.ogMaxWidth}px`,
|
|
}
|
|
},
|
|
ogContentStyle() {
|
|
return {
|
|
paddingTop: `${this.contentPadding}px`,
|
|
paddingRight: `${this.contentPadding}px`,
|
|
paddingLeft: `${this.contentPadding}px`,
|
|
}
|
|
},
|
|
setSizes(width, height) {
|
|
this.width = width
|
|
this.height = height
|
|
},
|
|
downloadAsPng() {
|
|
return this.download(toPng, 'image.png')
|
|
},
|
|
downloadAsJpeg() {
|
|
return this.download(toJpeg, 'image.jpg')
|
|
},
|
|
download(callback, filename) {
|
|
const element = this.$refs.ogimage
|
|
|
|
callback(element, {
|
|
canvasWidth: this.width,
|
|
canvasHeight: this.height,
|
|
quality: 1,
|
|
})
|
|
.then((dataUrl) => {
|
|
const link = document.createElement('a')
|
|
|
|
link.setAttribute('href', dataUrl)
|
|
link.setAttribute('download', filename)
|
|
link.click()
|
|
})
|
|
.catch((error) => {
|
|
element.classList.toggle('og--scale', false)
|
|
|
|
console.error('oops, something went wrong!', error);
|
|
})
|
|
},
|
|
updateOgMaxWidth() {
|
|
this.$refs.og.style.display = 'none'
|
|
this.ogMaxWidth = this.$refs.ogwrapper.offsetWidth - 30
|
|
this.ogMaxHeight = this.$refs.tools.offsetHeight - 50
|
|
this.$refs.og.style.display = 'block'
|
|
},
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.tool {
|
|
max-height: calc(100vh - 250px);
|
|
overflow: auto;
|
|
}
|
|
|
|
.tools {
|
|
height: calc(100vh - 250px);
|
|
width: 443px;
|
|
}
|
|
|
|
.og {
|
|
margin: auto;
|
|
}
|
|
|
|
.og-wrapper {
|
|
width: calc(100vw - 540px);
|
|
}
|
|
|
|
.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 {
|
|
font-size: 5em;
|
|
}
|
|
|
|
.og-subheading {
|
|
font-size: 3em;
|
|
}
|
|
|
|
.og-image--footer {
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.og-image-hover {
|
|
overflow: hidden;
|
|
}
|
|
</style>
|