Skip to content
Snippets Groups Projects
Verified Commit d67dd610 authored by Alexa Valentová's avatar Alexa Valentová
Browse files

run/add new hooks, add new template

parent 880a9bc0
Branches
No related tags found
No related merge requests found
Showing
with 790 additions and 812 deletions
...@@ -2,7 +2,6 @@ stages: ...@@ -2,7 +2,6 @@ stages:
- build - build
- test_deploy - test_deploy
image: docker:24.0.1 image: docker:24.0.1
variables: variables:
......
...@@ -13,3 +13,7 @@ repos: ...@@ -13,3 +13,7 @@ repos:
args: [--fix=lf] args: [--fix=lf]
- id: detect-private-key - id: detect-private-key
- id: check-merge-conflict - id: check-merge-conflict
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "" # Use the sha or tag you want to point at
hooks:
- id: prettier
/* eslint-env node */ /* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution') require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = { module.exports = {
root: true, root: true,
'extends': [ extends: [
'plugin:vue/vue3-essential', "plugin:vue/vue3-essential",
'eslint:recommended', "eslint:recommended",
'@vue/eslint-config-prettier/skip-formatting' "@vue/eslint-config-prettier/skip-formatting",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 'latest' ecmaVersion: "latest",
} },
} };
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<link rel="icon" href="/static/favicon.ico"> <link rel="icon" href="/static/favicon.ico" />
<link rel="stylesheet" href="https://styleguide.pirati.cz/2.12.x/css/styles.css"> <link
<meta name="viewport" content="width=device-width, initial-scale=1.0"> rel="stylesheet"
href="https://styleguide.pirati.cz/2.12.x/css/styles.css"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script> <script>
;(function () { (function () {
var src = '//cdn.jsdelivr.net/npm/eruda'; var src = "//cdn.jsdelivr.net/npm/eruda";
if (!/eruda=true/.test(window.location) && localStorage.getItem('active-eruda') != 'true') return; if (
document.write('<scr' + 'ipt src="' + src + '"></scr' + 'ipt>'); !/eruda=true/.test(window.location) &&
document.write('<scr' + 'ipt>eruda.init();</scr' + 'ipt>'); localStorage.getItem("active-eruda") != "true"
)
return;
document.write("<scr" + 'ipt src="' + src + '"></scr' + "ipt>");
document.write("<scr" + "ipt>eruda.init();</scr" + "ipt>");
})(); })();
</script> </script>
<title>Generátor grafiky</title> <title>Generátor grafiky</title>
......
...@@ -3,4 +3,4 @@ module.exports = { ...@@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} };
<script setup> <script setup>
import { RouterView } from 'vue-router' import { RouterView } from "vue-router";
</script> </script>
<template> <template>
......
/* glegoo-regular - latin_latin-ext */ /* glegoo-regular - latin_latin-ext */
@font-face { @font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Glegoo'; font-family: "Glegoo";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url('./glegoo-v14-latin_latin-ext-regular.woff2') format('woff2'), /* Chrome 36+, Opera 23+, Firefox 39+ */ src:
url('./glegoo-v14-latin_latin-ext-regular.woff') format('woff'); /* Chrome 5+, Firefox 3.6+, IE 9+, Safari 5.1+ */ url("./glegoo-v14-latin_latin-ext-regular.woff2") format("woff2"),
/* Chrome 36+, Opera 23+, Firefox 39+ */
url("./glegoo-v14-latin_latin-ext-regular.woff") format("woff"); /* Chrome 5+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* glegoo-700 - latin_latin-ext */ /* glegoo-700 - latin_latin-ext */
@font-face { @font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Glegoo'; font-family: "Glegoo";
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: url('./glegoo-v14-latin_latin-ext-700.woff2') format('woff2'), /* Chrome 36+, Opera 23+, Firefox 39+ */ src:
url('./glegoo-v14-latin_latin-ext-700.woff') format('woff'); /* Chrome 5+, Firefox 3.6+, IE 9+, Safari 5.1+ */ url("./glegoo-v14-latin_latin-ext-700.woff2") format("woff2"),
/* Chrome 36+, Opera 23+, Firefox 39+ */
url("./glegoo-v14-latin_latin-ext-700.woff") format("woff"); /* Chrome 5+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
frontend/src/assets/previews/base_event.png

5.1 KiB

frontend/src/assets/template/base_event/bg_black.png

20 KiB

frontend/src/assets/template/base_event/bg_white.png

21.5 KiB

const COLORS = { const COLORS = {
black: { black: {
name: 'Černá', name: "Černá",
value: '#000000' value: "#000000",
}, },
white: { white: {
name: 'Bílá', name: "Bílá",
value: '#ffffff' value: "#ffffff",
}, },
yellow1: { yellow1: {
name: 'Žlutá 1', name: "Žlutá 1",
value: '#fec900' value: "#fec900",
}, },
gray1: { gray1: {
name: 'Šedá 1', name: "Šedá 1",
value: '#b2b2b2' value: "#b2b2b2",
}, },
gray2: { gray2: {
name: 'Šedá 2', name: "Šedá 2",
value: '#888888' value: "#888888",
}, },
gray3: { gray3: {
name: 'Šedá 3', name: "Šedá 3",
value: '#404040' value: "#404040",
} },
} };
export default COLORS export default COLORS;
<template> <template>
<div class="container container--default mt-12 flex flex-col gap-8 md:grid md:gap-5 md:grid-cols-2"> <div
class="container container--default mt-12 flex flex-col gap-8 md:grid md:gap-5 md:grid-cols-2"
>
<div class="md:col-span-1"> <div class="md:col-span-1">
<slot name="left"></slot> <slot name="left"></slot>
</div> </div>
......
<script setup> <script setup>
import TEMPLATES from '../templates' import TEMPLATES from "../templates";
import v2Image from '../assets/v2.png' import v2Image from "../assets/v2.png";
import VueSelect from 'vue-select' import VueSelect from "vue-select";
import { RouterLink } from 'vue-router' import { RouterLink } from "vue-router";
</script> </script>
<script> <script>
export default { export default {
components: { VueSelect }, components: { VueSelect },
props: ['defaultTemplate'], props: ["defaultTemplate"],
data() { data() {
return { return {
currentTemplate: this.defaultTemplate currentTemplate: this.defaultTemplate,
} };
}, },
watch: { watch: {
currentTemplate(value) { currentTemplate(value) {
console.info("Switching template: ", value) console.info("Switching template: ", value);
this.$router.push(value.path) this.$router.push(value.path);
} },
} },
} };
</script> </script>
<template> <template>
<nav class="bg-grey-600 py-7"> <nav class="bg-grey-600 py-7">
<div class="container container--default flex justify-between flex-col lg:flex-row"> <div
class="container container--default flex justify-between flex-col lg:flex-row"
>
<div class="mb-5 flex items-center lg:pr-8 lg:mb-0"> <div class="mb-5 flex items-center lg:pr-8 lg:mb-0">
<RouterLink to="/"> <RouterLink to="/">
<img src="https://styleguide.pirati.cz/2.12.x/images/logo-round-white.svg" class="w-8"> <img
src="https://styleguide.pirati.cz/2.12.x/images/logo-round-white.svg"
class="w-8"
/>
</RouterLink> </RouterLink>
<RouterLink <RouterLink
to="/" to="/"
class="text-white pl-4 font-bold text-xl hover:no-underline lg:border-r lg:border-grey-300 lg:pr-8 flex gap-2 items-center" class="text-white pl-4 font-bold text-xl hover:no-underline lg:border-r lg:border-grey-300 lg:pr-8 flex gap-2 items-center"
> >
Generátor grafiky Generátor grafiky
<img <img :src="v2Image" class="h-5" alt="Verze 2.0" />
:src="v2Image"
class="h-5"
alt="Verze 2.0"
/>
</RouterLink> </RouterLink>
</div> </div>
<div> <div>
...@@ -55,11 +56,7 @@ export default { ...@@ -55,11 +56,7 @@ export default {
> >
<!-- BEGIN Hide search (TODO) --> <!-- BEGIN Hide search (TODO) -->
<template v-slot:search="{ attributes, events }"> <template v-slot:search="{ attributes, events }">
<input <input class="h-0 w-0" v-bind="attributes" v-on="events" />
class="h-0 w-0"
v-bind="attributes"
v-on="events"
/>
</template> </template>
<!-- END Hide search --> <!-- END Hide search -->
...@@ -67,7 +64,7 @@ export default { ...@@ -67,7 +64,7 @@ export default {
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<div class="w-12 h-12 flex justify-end shrink-0"> <div class="w-12 h-12 flex justify-end shrink-0">
<img <img
class="h-12" class="h-12 mx-auto"
alt="Náhled šablony" alt="Náhled šablony"
:src="option.image" :src="option.image"
/> />
......
<script> <script>
import { fabric } from 'fabric'; import { fabric } from "fabric";
export default { export default {
props: [ props: ["width", "height", "redrawFunction"],
'width',
'height',
'redrawFunction',
],
mounted() { mounted() {
fabric.Object.prototype.set({ fabric.Object.prototype.set({
transparentCorners: false, transparentCorners: false,
borderColor: '#ff00ff', borderColor: "#ff00ff",
cornerColor: '#ff0000' cornerColor: "#ff0000",
}); });
this.canvas = new fabric.Canvas(this.$refs.canvas); this.canvas = new fabric.Canvas(this.$refs.canvas);
}, },
data() { data() {
return { return {
redrawing: false redrawing: false,
} };
}, },
methods: { methods: {
async redraw(options) { async redraw(options) {
if (this.redrawing) { if (this.redrawing) {
console.info("Already drawing, skipping redraw.") console.info("Already drawing, skipping redraw.");
return return;
} }
this.redrawing = true this.redrawing = true;
console.info(`Redrawing canvas with options: `, options) console.info(`Redrawing canvas with options: `, options);
try { try {
await this.redrawFunction(this.canvas, options) await this.redrawFunction(this.canvas, options);
} catch (exception) { } catch (exception) {
console.error("Error redrawing: ", exception) console.error("Error redrawing: ", exception);
} }
this.redrawing = false this.redrawing = false;
}, },
downloadImage() { downloadImage() {
this.canvas.discardActiveObject().renderAll() this.canvas.discardActiveObject().renderAll();
let link = document.createElement('a') let link = document.createElement("a");
link.download = "Vyhrajem.png" link.download = "Vyhrajem.png";
link.href = this.$refs.canvas.toDataURL() link.href = this.$refs.canvas.toDataURL();
link.click() link.click();
} },
} },
} };
</script> </script>
<template> <template>
...@@ -62,10 +58,7 @@ export default { ...@@ -62,10 +58,7 @@ export default {
<h2>Náhled</h2> <h2>Náhled</h2>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button class="btn btn--icon" @click="downloadImage">
class="btn btn--icon"
@click="downloadImage"
>
<div class="btn__body-wrap"> <div class="btn__body-wrap">
<div class="btn__body">Stáhnout</div> <div class="btn__body">Stáhnout</div>
<div class="btn__icon"> <div class="btn__icon">
...@@ -80,7 +73,7 @@ export default { ...@@ -80,7 +73,7 @@ export default {
id="canvas" id="canvas"
ref="canvas" ref="canvas"
class="w-full border border-gray-300 drop-shadow-md duration-150" class="w-full border border-gray-300 drop-shadow-md duration-150"
:class="{'blur': redrawing}" :class="{ blur: redrawing }"
:width="width" :width="width"
:height="height" :height="height"
></canvas> ></canvas>
......
import { fabric } from 'fabric' import { fabric } from "fabric";
class PaddedHighlightingTextbox extends fabric.Textbox { class PaddedHighlightingTextbox extends fabric.Textbox {
_renderTextLinesBackground(ctx) { _renderTextLinesBackground(ctx) {
if (!this.textBackgroundColor && !this.styleHas('textBackgroundColor')) { if (!this.textBackgroundColor && !this.styleHas("textBackgroundColor")) {
return; return;
} }
var heightOfLine, var heightOfLine,
lineLeftOffset, originalFill = ctx.fillStyle, lineLeftOffset,
line, lastColor, originalFill = ctx.fillStyle,
line,
lastColor,
leftOffset = this._getLeftOffset(), leftOffset = this._getLeftOffset(),
lineTopOffset = this._getTopOffset(), lineTopOffset = this._getTopOffset(),
boxStart = 0, boxWidth = 0, charBox, currentColor, path = this.path, boxStart = 0,
boxWidth = 0,
charBox,
currentColor,
path = this.path,
drawStart; drawStart;
const highlightPadding = ( const highlightPadding =
(this.highlightPadding !== undefined) ? this.highlightPadding !== undefined ? this.highlightPadding : 1;
this.highlightPadding : 1
)
for (var i = 0, len = this._textLines.length; i < len; i++) { for (var i = 0, len = this._textLines.length; i < len; i++) {
heightOfLine = this.getHeightOfLine(i); heightOfLine = this.getHeightOfLine(i);
if (!this.textBackgroundColor && !this.styleHas('textBackgroundColor', i)) { if (
!this.textBackgroundColor &&
!this.styleHas("textBackgroundColor", i)
) {
lineTopOffset += heightOfLine; lineTopOffset += heightOfLine;
continue; continue;
} }
...@@ -32,37 +38,40 @@ class PaddedHighlightingTextbox extends fabric.Textbox { ...@@ -32,37 +38,40 @@ class PaddedHighlightingTextbox extends fabric.Textbox {
lineLeftOffset = this._getLineLeftOffset(i); lineLeftOffset = this._getLineLeftOffset(i);
boxWidth = 0; boxWidth = 0;
boxStart = 0; boxStart = 0;
lastColor = this.getValueOfPropertyAt(i, 0, 'textBackgroundColor'); lastColor = this.getValueOfPropertyAt(i, 0, "textBackgroundColor");
for (var j = 0, jlen = line.length; j < jlen; j++) { for (var j = 0, jlen = line.length; j < jlen; j++) {
charBox = this.__charBounds[i][j]; charBox = this.__charBounds[i][j];
currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor'); currentColor = this.getValueOfPropertyAt(i, j, "textBackgroundColor");
if (path) { if (path) {
ctx.save(); ctx.save();
ctx.translate(charBox.renderLeft, charBox.renderTop); ctx.translate(charBox.renderLeft, charBox.renderTop);
ctx.rotate(charBox.angle); ctx.rotate(charBox.angle);
ctx.fillStyle = currentColor; ctx.fillStyle = currentColor;
currentColor && ctx.fillRect( currentColor &&
ctx.fillRect(
-charBox.width / 2, -charBox.width / 2,
(-heightOfLine / this.lineHeight * (1 - this._fontSizeFraction)) - highlightPadding * 2, (-heightOfLine / this.lineHeight) * (1 - this._fontSizeFraction) -
highlightPadding * 2,
charBox.width, charBox.width,
(heightOfLine / this.lineHeight) + highlightPadding heightOfLine / this.lineHeight + highlightPadding,
); );
ctx.restore(); ctx.restore();
} else if (currentColor !== lastColor) { } else if (currentColor !== lastColor) {
drawStart = leftOffset + lineLeftOffset + boxStart; drawStart = leftOffset + lineLeftOffset + boxStart;
if (this.direction === 'rtl') { if (this.direction === "rtl") {
drawStart = this.width - drawStart - boxWidth; drawStart = this.width - drawStart - boxWidth;
} }
ctx.fillStyle = lastColor; ctx.fillStyle = lastColor;
lastColor && ctx.fillRect( lastColor &&
ctx.fillRect(
drawStart, drawStart,
lineTopOffset - highlightPadding, lineTopOffset - highlightPadding,
boxWidth, boxWidth,
(heightOfLine / this.lineHeight) + highlightPadding * 2 heightOfLine / this.lineHeight + highlightPadding * 2,
); );
boxStart = charBox.left; boxStart = charBox.left;
boxWidth = charBox.width; boxWidth = charBox.width;
...@@ -75,7 +84,7 @@ class PaddedHighlightingTextbox extends fabric.Textbox { ...@@ -75,7 +84,7 @@ class PaddedHighlightingTextbox extends fabric.Textbox {
if (currentColor && !path) { if (currentColor && !path) {
drawStart = leftOffset + lineLeftOffset + boxStart; drawStart = leftOffset + lineLeftOffset + boxStart;
if (this.direction === 'rtl') { if (this.direction === "rtl") {
drawStart = this.width - drawStart - boxWidth; drawStart = this.width - drawStart - boxWidth;
} }
...@@ -84,7 +93,7 @@ class PaddedHighlightingTextbox extends fabric.Textbox { ...@@ -84,7 +93,7 @@ class PaddedHighlightingTextbox extends fabric.Textbox {
drawStart, drawStart,
lineTopOffset - highlightPadding, lineTopOffset - highlightPadding,
boxWidth, boxWidth,
(heightOfLine / this.lineHeight) + highlightPadding * 2 heightOfLine / this.lineHeight + highlightPadding * 2,
); );
} }
...@@ -98,5 +107,4 @@ class PaddedHighlightingTextbox extends fabric.Textbox { ...@@ -98,5 +107,4 @@ class PaddedHighlightingTextbox extends fabric.Textbox {
} }
} }
export { PaddedHighlightingTextbox };
export { PaddedHighlightingTextbox }
import alertifyjs from "alertifyjs" import alertifyjs from "alertifyjs";
import "alertifyjs/build/css/alertify.css" import "alertifyjs/build/css/alertify.css";
import { fabric } from 'fabric'
import { fabric } from "fabric";
const setCharAt = (str, index, chr) => { const setCharAt = (str, index, chr) => {
if (index > str.length - 1) return str if (index > str.length - 1) return str;
return str.substring(0, index) + chr + str.substring(index + 1) return str.substring(0, index) + chr + str.substring(index + 1);
} };
const clearObjects = (clearableItems, canvas) => { const clearObjects = (clearableItems, canvas) => {
for (const clearableItem of clearableItems) { for (const clearableItem of clearableItems) {
...@@ -16,153 +15,124 @@ const clearObjects = (clearableItems, canvas) => { ...@@ -16,153 +15,124 @@ const clearObjects = (clearableItems, canvas) => {
canvas.remove(clearableItem); canvas.remove(clearableItem);
} }
} }
} };
const sortObjects = (canvas) => { const sortObjects = (canvas) => {
canvas._objects.sort((a, b) => (a.zIndex > b.zIndex) ? 1 : -1) canvas._objects.sort((a, b) => (a.zIndex > b.zIndex ? 1 : -1));
canvas.renderAll() canvas.renderAll();
} };
const transformTextLineBreaks = ( const transformTextLineBreaks = (
text, text,
fontSize, fontSize,
fontFamily, fontFamily,
maxWidth, maxWidth,
options options,
) => { ) => {
if (options === undefined) { if (options === undefined) {
options = {} options = {};
} }
text = text.replace(/[^\S\r\n]+/g, ' ') text = text.replace(/[^\S\r\n]+/g, " ");
text = text.replace(/\r\n/g, '\n') text = text.replace(/\r\n/g, "\n");
let positionWithinString = -1 let positionWithinString = -1;
let currentWidth = 0 let currentWidth = 0;
const splitWords = text.split(" ") const splitWords = text.split(" ");
let wordIndexes = {} let wordIndexes = {};
const spaceText = new fabric.Text( const spaceText = new fabric.Text(" ", {
" ",
{
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: fontSize fontSize: fontSize,
} });
)
for (let wordPosition = 0; wordPosition < splitWords.length; wordPosition++) { for (let wordPosition = 0; wordPosition < splitWords.length; wordPosition++) {
let currentWord = splitWords[wordPosition] let currentWord = splitWords[wordPosition];
let skipNewLineGeneration = false let skipNewLineGeneration = false;
if (currentWord.includes("\n")) { if (currentWord.includes("\n")) {
skipNewLineGeneration = true skipNewLineGeneration = true;
const breakSplitWord = currentWord.split("\n") const breakSplitWord = currentWord.split("\n");
const firstLineWord = breakSplitWord[0] const firstLineWord = breakSplitWord[0];
const secondLineWord = breakSplitWord[1] const secondLineWord = breakSplitWord[1];
// Word + \n // Word + \n
positionWithinString += firstLineWord.length + 1 positionWithinString += firstLineWord.length + 1;
currentWord = secondLineWord currentWord = secondLineWord;
currentWidth = 0 currentWidth = 0;
} }
const wordIsLast = (wordPosition === splitWords.length - 1) const wordIsLast = wordPosition === splitWords.length - 1;
positionWithinString += currentWord.length + ( positionWithinString += currentWord.length + (!wordIsLast ? 1 : 0);
(!wordIsLast) ?
1 : 0
)
const wordText = new fabric.Text( const wordText = new fabric.Text(currentWord, {
currentWord,
{
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: fontSize fontSize: fontSize,
} });
);
// This is really ugly, I have no idea why Chromium thinks the text is shorter than it really is. // This is really ugly, I have no idea why Chromium thinks the text is shorter than it really is.
// (Or why Firefox thinks it's longer.) // (Or why Firefox thinks it's longer.)
// But, it works. // But, it works.
currentWidth += wordText.width * ( currentWidth += wordText.width * (!!window.chrome ? 1.183 : 1);
(!!window.chrome)
? 1.183
: 1
)
if (!skipNewLineGeneration && currentWidth > maxWidth) { if (!skipNewLineGeneration && currentWidth > maxWidth) {
if ( if (
["a", "i", "o", "u", "s", "se", "v", "z"]. ["a", "i", "o", "u", "s", "se", "v", "z"].includes(
includes(splitWords[ splitWords[
(wordPosition !== 0) wordPosition !== 0 ? wordPosition - 1 : wordPosition
? (wordPosition - 1) ].replace("*", ""),
: (wordPosition)
].replace("*", ""))
) { // Previous word is not a, i, o, u, s, ...
const lineBreakPosition = (
positionWithinString
- (
(!wordIsLast) ?
1 : 0
)
- currentWord.length
- 1
- splitWords[
(wordPosition !== 0)
? (wordPosition - 1)
: (wordPosition)
].length
) )
) {
text = setCharAt(text, lineBreakPosition, "\n") // Previous word is not a, i, o, u, s, ...
const lineBreakPosition =
positionWithinString -
(!wordIsLast ? 1 : 0) -
currentWord.length -
1 -
splitWords[wordPosition !== 0 ? wordPosition - 1 : wordPosition]
.length;
text = setCharAt(text, lineBreakPosition, "\n");
} else { } else {
text = setCharAt( text = setCharAt(
text, text,
( positionWithinString - (!wordIsLast ? 1 : 0) - currentWord.length,
positionWithinString "\n",
- ( );
(!wordIsLast) ?
1 : 0
)
- currentWord.length
),
"\n"
)
} }
currentWidth = wordText.width currentWidth = wordText.width;
} else if (!wordIsLast) { } else if (!wordIsLast) {
currentWidth += spaceText.width currentWidth += spaceText.width;
} }
} }
if (options.prependLinesWithSpace) { if (options.prependLinesWithSpace) {
let prependedText = "" let prependedText = "";
let splitLines = text.split("\n") let splitLines = text.split("\n");
let linePosition = 0; let linePosition = 0;
for (let line of splitLines) { for (let line of splitLines) {
if (linePosition + 1 !== splitLines.length) { if (linePosition + 1 !== splitLines.length) {
if (line[0] === "*") { if (line[0] === "*") {
line = `${line}\n` line = `${line}\n`;
} else { } else {
line = ` ${line}\n` line = ` ${line}\n`;
}
} }
prependedText += line
} }
text = prependedText prependedText += line;
} }
return text text = prependedText;
} }
return text;
};
const transformHighlightedText = ( const transformHighlightedText = (
text, text,
...@@ -171,66 +141,62 @@ const transformHighlightedText = ( ...@@ -171,66 +141,62 @@ const transformHighlightedText = (
fontFamily, fontFamily,
highlightColor, highlightColor,
highlightedTextColor, highlightedTextColor,
options options,
) => { ) => {
if (options === undefined) { if (options === undefined) {
options = {} options = {};
} }
text = transformTextLineBreaks( text = transformTextLineBreaks(text, fontSize, fontFamily, maxWidth, options);
text,
fontSize,
fontFamily,
maxWidth,
options
)
let positionWithinString = 0 let positionWithinString = 0;
const textContainsDiacritics = ( const textContainsDiacritics = /[áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚÝŽ]+/.test(text);
/[áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚÝŽ]+/.test(text)
)
const textContainsHighlight = ( const textContainsHighlight = /\*/.test(text);
/\*/.test(text)
)
let positionWithinLine = 0 let positionWithinLine = 0;
let linePosition = 0 let linePosition = 0;
let highlightIsActive = false let highlightIsActive = false;
let styles = { let styles = {
0: {} 0: {},
} };
let prependWithSpace = false; let prependWithSpace = false;
for (const character of text) { for (const character of text) {
const characterIsStar = (character === "*") const characterIsStar = character === "*";
if (characterIsStar) { if (characterIsStar) {
highlightIsActive = !highlightIsActive highlightIsActive = !highlightIsActive;
text = setCharAt(text, positionWithinString, " ") text = setCharAt(text, positionWithinString, " ");
} }
let style = {} let style = {};
if (highlightIsActive || characterIsStar) { if (highlightIsActive || characterIsStar) {
style.textBackgroundColor = highlightColor style.textBackgroundColor = highlightColor;
style.fill = highlightedTextColor style.fill = highlightedTextColor;
} }
if (options.padWhenDiacritics && textContainsHighlight && textContainsDiacritics) { if (
style.deltaY = Math.ceil(fontSize * ( options.padWhenDiacritics &&
(options.diacriticsDeltaYOffset !== undefined) ? textContainsHighlight &&
options.diacriticsDeltaYOffset : 0.1 textContainsDiacritics
)) ) {
style.deltaY = Math.ceil(
fontSize *
(options.diacriticsDeltaYOffset !== undefined
? options.diacriticsDeltaYOffset
: 0.1),
);
} }
styles[linePosition][positionWithinLine] = style styles[linePosition][positionWithinLine] = style;
positionWithinLine++ positionWithinLine++;
positionWithinString++ positionWithinString++;
// TODO // TODO
// //
...@@ -239,20 +205,26 @@ const transformHighlightedText = ( ...@@ -239,20 +205,26 @@ const transformHighlightedText = (
// } // }
if (character === "\n") { if (character === "\n") {
styles[linePosition + 1] = {} styles[linePosition + 1] = {};
linePosition++ linePosition++;
if (highlightIsActive) { if (highlightIsActive) {
text = text.slice(0, positionWithinString - 1) + " " + text.slice(positionWithinString - 1) text =
positionWithinString++ text.slice(0, positionWithinString - 1) +
" " +
text = text.slice(0, positionWithinString) + " " + text.slice(positionWithinString) text.slice(positionWithinString - 1);
positionWithinString++ positionWithinString++;
positionWithinLine = 1 text =
styles[linePosition][0] = style text.slice(0, positionWithinString) +
" " +
text.slice(positionWithinString);
positionWithinString++;
positionWithinLine = 1;
styles[linePosition][0] = style;
} else { } else {
positionWithinLine = 0 positionWithinLine = 0;
} }
} }
} }
...@@ -260,34 +232,44 @@ const transformHighlightedText = ( ...@@ -260,34 +232,44 @@ const transformHighlightedText = (
return { return {
text: text, text: text,
styles: styles, styles: styles,
paddingBottom: ( paddingBottom:
(options.padWhenDiacritics && textContainsHighlight && textContainsDiacritics) ? options.padWhenDiacritics &&
Math.ceil(fontSize * ( textContainsHighlight &&
(options.diacriticsDeltaYOffset !== undefined) ? textContainsDiacritics
options.diacriticsDeltaYOffset : 0.1 ? Math.ceil(
)) : 0 fontSize *
(options.diacriticsDeltaYOffset !== undefined
? options.diacriticsDeltaYOffset
: 0.1),
) )
} : 0,
} };
};
const checkTextBoxHeight = (textBox, maxLines) => { const checkTextBoxHeight = (textBox, maxLines) => {
if (textBox.textLines.length > maxLines) { if (textBox.textLines.length > maxLines) {
if (!window.showingMaxLinesWarning) { if (!window.showingMaxLinesWarning) {
window.showingMaxLinesWarning = true window.showingMaxLinesWarning = true;
const errorMessage = alertifyjs.error( const errorMessage = alertifyjs.error(
`Text je moc dlouhý a nevejde se do ${maxLines} řádků. Prosím, zkrať ho.`, `Text je moc dlouhý a nevejde se do ${maxLines} řádků. Prosím, zkrať ho.`,
) );
errorMessage.callback = () => { errorMessage.callback = () => {
window.showingMaxLinesWarning = false window.showingMaxLinesWarning = false;
} };
} }
let textLines = [...textBox.textLines] let textLines = [...textBox.textLines];
textLines.splice(maxLines) textLines.splice(maxLines);
textBox.set("text", textLines.join('\n')) textBox.set("text", textLines.join("\n"));
} }
} };
export { clearObjects, sortObjects, transformHighlightedText, transformTextLineBreaks, checkTextBoxHeight } export {
clearObjects,
sortObjects,
transformHighlightedText,
transformTextLineBreaks,
checkTextBoxHeight,
};
<script setup> <script setup>
import VueSelect from 'vue-select' import VueSelect from "vue-select";
import InputHeading from "./InputHeading.vue" import InputHeading from "./InputHeading.vue";
import likeImage from '../../assets/reactions/like.png' import likeImage from "../../assets/reactions/like.png";
import laughImage from '../../assets/reactions/laugh.png' import laughImage from "../../assets/reactions/laugh.png";
import heartImage from '../../assets/reactions/heart.png' import heartImage from "../../assets/reactions/heart.png";
import angryImage from '../../assets/reactions/angry.png' import angryImage from "../../assets/reactions/angry.png";
import sadImage from '../../assets/reactions/sad.png' import sadImage from "../../assets/reactions/sad.png";
import surprisedImage from '../../assets/reactions/surprised.png' import surprisedImage from "../../assets/reactions/surprised.png";
import careImage from '../../assets/reactions/care.png' import careImage from "../../assets/reactions/care.png";
</script> </script>
<script> <script>
export default { export default {
components: { InputHeading, VueSelect }, components: { InputHeading, VueSelect },
props: ['name', 'important', 'zIndex', 'modelValue'], props: ["name", "important", "zIndex", "modelValue"],
emits: ['update:modelValue'], emits: ["update:modelValue"],
data() { data() {
return { return {
emojiOptions: [ emojiOptions: [
{ {
title: '👍 Like', title: "👍 Like",
url: likeImage url: likeImage,
}, },
{ {
title: '😂 Smích', title: "😂 Smích",
url: laughImage url: laughImage,
}, },
{ {
title: '♥️ Srdce', title: "♥️ Srdce",
url: heartImage url: heartImage,
}, },
{ {
title: '😡 Naštvaný', title: "😡 Naštvaný",
url: angryImage url: angryImage,
}, },
{ {
title: '☹️ Smutný', title: "☹️ Smutný",
url: sadImage url: sadImage,
}, },
{ {
title: '😲 Překvapený', title: "😲 Překvapený",
url: surprisedImage url: surprisedImage,
}, },
{ {
title: '🥰 Péče', title: "🥰 Péče",
url: careImage url: careImage,
}, },
], ],
selectedEmoji: this.modelValue selectedEmoji: this.modelValue,
} };
}, },
watch: { watch: {
selectedEmoji(value) { selectedEmoji(value) {
const image = new Image() const image = new Image();
if (value === null || value === undefined) { if (value === null || value === undefined) {
this.$emit('update:modelValue', null) this.$emit("update:modelValue", null);
return return;
} }
image.onload = () => { image.onload = () => {
this.$emit('update:modelValue', image) this.$emit("update:modelValue", image);
} };
image.src = value.url image.src = value.url;
} },
} },
} };
</script> </script>
<template> <template>
......
<script setup> <script setup>
import InputHeading from "./InputHeading.vue" import InputHeading from "./InputHeading.vue";
import VueSelect from 'vue-select' import VueSelect from "vue-select";
</script> </script>
<script> <script>
export default { export default {
components: { InputHeading, VueSelect }, components: { InputHeading, VueSelect },
props: [ props: [
'name', "name",
'important', "important",
'zIndex', "zIndex",
'modelValue', "modelValue",
'predefinedImages', "predefinedImages",
'mustSelectPredefinedImage', "mustSelectPredefinedImage",
'disableImageInput' "disableImageInput",
], ],
emits: ['update:modelValue'], emits: ["update:modelValue"],
data() { data() {
let data = { let data = {
hasFile: false, hasFile: false,
selectedImage: null selectedImage: null,
} };
if (this.predefinedImages) { if (this.predefinedImages) {
for (const image of this.predefinedImages) { for (const image of this.predefinedImages) {
if (!image.defaultSelected) { if (!image.defaultSelected) {
continue continue;
} }
data.selectedImage = image data.selectedImage = image;
} }
} }
return data return data;
}, },
methods: { methods: {
handleFileInput(event) { handleFileInput(event) {
if (event.target.files.length !== 0) { if (event.target.files.length !== 0) {
this.loadImageFromFile(event.target.files[0]) this.loadImageFromFile(event.target.files[0]);
} else { } else {
this.hasFile = false this.hasFile = false;
} }
}, },
loadImageFromFile(file) { loadImageFromFile(file) {
this.hasFile = true this.hasFile = true;
const image = new Image() const image = new Image();
image.onload = () => { image.onload = () => {
this.$emit('update:modelValue', image) this.$emit("update:modelValue", image);
} };
const reader = new FileReader() const reader = new FileReader();
reader.onloadend = () => { reader.onloadend = () => {
image.src = reader.result image.src = reader.result;
} };
reader.readAsDataURL(file) reader.readAsDataURL(file);
}, },
setSelectedImage(value) { setSelectedImage(value) {
this.selectedImage = value this.selectedImage = value;
if (value !== null) { if (value !== null) {
const image = new Image() const image = new Image();
image.onload = () => { image.onload = () => {
this.$emit('update:modelValue', image) this.$emit("update:modelValue", image);
} };
image.src = value.src image.src = value.src;
} else { } else {
if (this.hasFile) { if (this.hasFile) {
this.loadImageFromFile(this.$refs.fileInput.files[0]) this.loadImageFromFile(this.$refs.fileInput.files[0]);
} else { } else {
this.$emit('update:modelValue', null) this.$emit("update:modelValue", null);
} }
} }
}, },
clearFileInput(event) { clearFileInput(event) {
this.hasFile = false this.hasFile = false;
this.$refs.fileInput.value = '' this.$refs.fileInput.value = "";
if (this.selectedImage) { if (this.selectedImage) {
this.setSelectedImage(this.selectedImage) this.setSelectedImage(this.selectedImage);
} else { } else {
this.$emit('update:modelValue', null) this.$emit("update:modelValue", null);
}
} }
}, },
},
mounted() { mounted() {
if (this.selectedImage && !this.modelValue) { if (this.selectedImage && !this.modelValue) {
this.setSelectedImage(this.selectedImage) this.setSelectedImage(this.selectedImage);
}
}
} }
},
};
</script> </script>
<template> <template>
...@@ -108,7 +108,7 @@ export default { ...@@ -108,7 +108,7 @@ export default {
icon="file-picture" icon="file-picture"
></InputHeading> ></InputHeading>
<hr class="hr--unstyled border-t-gray-300"> <hr class="hr--unstyled border-t-gray-300" />
<div class="flex justify-between gap-2" v-if="!disableImageInput"> <div class="flex justify-between gap-2" v-if="!disableImageInput">
<input <input
...@@ -116,7 +116,7 @@ export default { ...@@ -116,7 +116,7 @@ export default {
type="file" type="file"
accept="image/*" accept="image/*"
@change="handleFileInput" @change="handleFileInput"
> />
<button <button
aria-label="Odstranit obrázek" aria-label="Odstranit obrázek"
class="shrink-0 bg-black text-xs text-white duration-150 w-8 hover:bg-grey-800" class="shrink-0 bg-black text-xs text-white duration-150 w-8 hover:bg-grey-800"
...@@ -141,28 +141,20 @@ export default { ...@@ -141,28 +141,20 @@ export default {
:options="predefinedImages" :options="predefinedImages"
:clearable="!mustSelectPredefinedImage" :clearable="!mustSelectPredefinedImage"
:searchable="false" :searchable="false"
:modelValue="(selectedImage) ? selectedImage : null" :modelValue="selectedImage ? selectedImage : null"
@update:modelValue="setSelectedImage" @update:modelValue="setSelectedImage"
label="name" label="name"
> >
<!-- BEGIN Hide search (TODO) --> <!-- BEGIN Hide search (TODO) -->
<template v-slot:search="{ attributes, events }"> <template v-slot:search="{ attributes, events }">
<input <input class="h-0 w-0" v-bind="attributes" v-on="events" />
class="h-0 w-0"
v-bind="attributes"
v-on="events"
/>
</template> </template>
<!-- END Hide search --> <!-- END Hide search -->
<template v-slot:option="option"> <template v-slot:option="option">
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<div class="p-2 bg-gray-200 rounded-md"> <div class="p-2 bg-gray-200 rounded-md">
<img <img class="h-8" alt="Náhled možnosti" :src="option.src" />
class="h-8"
alt="Náhled možnosti"
:src="option.src"
/>
</div> </div>
<div> <div>
{{ option.name }} {{ option.name }}
......
<script> <script>
export default { export default {
props: ['name', 'important', 'zIndex', 'icon'], props: ["name", "important", "zIndex", "icon"],
methods: { methods: {
getClass() { getClass() {
return { return {
'font-bold text-xl': this.important, "font-bold text-xl": this.important,
'text-lg': !this.important "text-lg": !this.important,
} };
} },
} },
} };
</script> </script>
<template> <template>
<div <div class="flex gap-2 items-center" :style="{ 'z-index': zIndex }">
class="flex gap-2 items-center" <i :class="'ico--' + icon" class="text-lg" v-if="icon"></i>
:style="{'z-index': zIndex}" <h2 class="font-condensed" :class="getClass()">{{ this.name }}</h2>
>
<i
:class="'ico--' + icon"
class="text-lg"
v-if="icon"
></i>
<h2
class="font-condensed"
:class="getClass()"
>{{ this.name }}</h2>
</div> </div>
</template> </template>
<template> <template>
<hr class="hr--unstyled my-5 border-t-gray-300"> <hr class="hr--unstyled my-5 border-t-gray-300" />
</template> </template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment