better newline support
This commit is contained in:
@@ -16,29 +16,24 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||||||
import "phoenix_html"
|
import 'phoenix_html';
|
||||||
// Establish Phoenix Socket and LiveView configuration.
|
// Establish Phoenix Socket and LiveView configuration.
|
||||||
import {Socket} from "phoenix"
|
import { Socket } from 'phoenix';
|
||||||
import {LiveSocket} from "phoenix_live_view"
|
import { LiveSocket } from 'phoenix_live_view';
|
||||||
import topbar from "../vendor/topbar"
|
import Hooks from './hooks';
|
||||||
|
|
||||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content');
|
||||||
let liveSocket = new LiveSocket("/live", Socket, {
|
let liveSocket = new LiveSocket('/live', Socket, {
|
||||||
|
hooks: Hooks,
|
||||||
longPollFallbackMs: 2500,
|
longPollFallbackMs: 2500,
|
||||||
params: {_csrf_token: csrfToken}
|
params: { _csrf_token: csrfToken },
|
||||||
})
|
});
|
||||||
|
|
||||||
// Show progress bar on live navigation and form submits
|
|
||||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
|
||||||
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
|
|
||||||
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
|
|
||||||
|
|
||||||
// connect if there are any LiveViews on the page
|
// connect if there are any LiveViews on the page
|
||||||
liveSocket.connect()
|
liveSocket.connect();
|
||||||
|
|
||||||
// expose liveSocket on window for web console debug logs and latency simulation:
|
// expose liveSocket on window for web console debug logs and latency simulation:
|
||||||
// >> liveSocket.enableDebug()
|
// >> liveSocket.enableDebug()
|
||||||
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
||||||
// >> liveSocket.disableLatencySim()
|
// >> liveSocket.disableLatencySim()
|
||||||
window.liveSocket = liveSocket
|
window.liveSocket = liveSocket;
|
||||||
|
|
||||||
|
|||||||
19
assets/js/hooks.js
Normal file
19
assets/js/hooks.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const Hooks = {};
|
||||||
|
|
||||||
|
Hooks.EnterToNewline = {
|
||||||
|
mounted() {
|
||||||
|
this.el.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const cursorPos = this.el.selectionStart;
|
||||||
|
const value = this.el.value;
|
||||||
|
|
||||||
|
this.el.value = value.slice(0, cursorPos) + '\\n' + value.slice(cursorPos);
|
||||||
|
this.el.selectionStart = this.el.selectionEnd = cursorPos + 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Hooks;
|
||||||
165
assets/vendor/topbar.js
vendored
165
assets/vendor/topbar.js
vendored
@@ -1,165 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license MIT
|
|
||||||
* topbar 2.0.0, 2023-02-04
|
|
||||||
* https://buunguyen.github.io/topbar
|
|
||||||
* Copyright (c) 2021 Buu Nguyen
|
|
||||||
*/
|
|
||||||
(function (window, document) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// https://gist.github.com/paulirish/1579671
|
|
||||||
(function () {
|
|
||||||
var lastTime = 0;
|
|
||||||
var vendors = ["ms", "moz", "webkit", "o"];
|
|
||||||
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
|
||||||
window.requestAnimationFrame =
|
|
||||||
window[vendors[x] + "RequestAnimationFrame"];
|
|
||||||
window.cancelAnimationFrame =
|
|
||||||
window[vendors[x] + "CancelAnimationFrame"] ||
|
|
||||||
window[vendors[x] + "CancelRequestAnimationFrame"];
|
|
||||||
}
|
|
||||||
if (!window.requestAnimationFrame)
|
|
||||||
window.requestAnimationFrame = function (callback, element) {
|
|
||||||
var currTime = new Date().getTime();
|
|
||||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
|
||||||
var id = window.setTimeout(function () {
|
|
||||||
callback(currTime + timeToCall);
|
|
||||||
}, timeToCall);
|
|
||||||
lastTime = currTime + timeToCall;
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
if (!window.cancelAnimationFrame)
|
|
||||||
window.cancelAnimationFrame = function (id) {
|
|
||||||
clearTimeout(id);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
var canvas,
|
|
||||||
currentProgress,
|
|
||||||
showing,
|
|
||||||
progressTimerId = null,
|
|
||||||
fadeTimerId = null,
|
|
||||||
delayTimerId = null,
|
|
||||||
addEvent = function (elem, type, handler) {
|
|
||||||
if (elem.addEventListener) elem.addEventListener(type, handler, false);
|
|
||||||
else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
|
|
||||||
else elem["on" + type] = handler;
|
|
||||||
},
|
|
||||||
options = {
|
|
||||||
autoRun: true,
|
|
||||||
barThickness: 3,
|
|
||||||
barColors: {
|
|
||||||
0: "rgba(26, 188, 156, .9)",
|
|
||||||
".25": "rgba(52, 152, 219, .9)",
|
|
||||||
".50": "rgba(241, 196, 15, .9)",
|
|
||||||
".75": "rgba(230, 126, 34, .9)",
|
|
||||||
"1.0": "rgba(211, 84, 0, .9)",
|
|
||||||
},
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowColor: "rgba(0, 0, 0, .6)",
|
|
||||||
className: null,
|
|
||||||
},
|
|
||||||
repaint = function () {
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = options.barThickness * 5; // need space for shadow
|
|
||||||
|
|
||||||
var ctx = canvas.getContext("2d");
|
|
||||||
ctx.shadowBlur = options.shadowBlur;
|
|
||||||
ctx.shadowColor = options.shadowColor;
|
|
||||||
|
|
||||||
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
|
||||||
for (var stop in options.barColors)
|
|
||||||
lineGradient.addColorStop(stop, options.barColors[stop]);
|
|
||||||
ctx.lineWidth = options.barThickness;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(0, options.barThickness / 2);
|
|
||||||
ctx.lineTo(
|
|
||||||
Math.ceil(currentProgress * canvas.width),
|
|
||||||
options.barThickness / 2
|
|
||||||
);
|
|
||||||
ctx.strokeStyle = lineGradient;
|
|
||||||
ctx.stroke();
|
|
||||||
},
|
|
||||||
createCanvas = function () {
|
|
||||||
canvas = document.createElement("canvas");
|
|
||||||
var style = canvas.style;
|
|
||||||
style.position = "fixed";
|
|
||||||
style.top = style.left = style.right = style.margin = style.padding = 0;
|
|
||||||
style.zIndex = 100001;
|
|
||||||
style.display = "none";
|
|
||||||
if (options.className) canvas.classList.add(options.className);
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
addEvent(window, "resize", repaint);
|
|
||||||
},
|
|
||||||
topbar = {
|
|
||||||
config: function (opts) {
|
|
||||||
for (var key in opts)
|
|
||||||
if (options.hasOwnProperty(key)) options[key] = opts[key];
|
|
||||||
},
|
|
||||||
show: function (delay) {
|
|
||||||
if (showing) return;
|
|
||||||
if (delay) {
|
|
||||||
if (delayTimerId) return;
|
|
||||||
delayTimerId = setTimeout(() => topbar.show(), delay);
|
|
||||||
} else {
|
|
||||||
showing = true;
|
|
||||||
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
|
|
||||||
if (!canvas) createCanvas();
|
|
||||||
canvas.style.opacity = 1;
|
|
||||||
canvas.style.display = "block";
|
|
||||||
topbar.progress(0);
|
|
||||||
if (options.autoRun) {
|
|
||||||
(function loop() {
|
|
||||||
progressTimerId = window.requestAnimationFrame(loop);
|
|
||||||
topbar.progress(
|
|
||||||
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
progress: function (to) {
|
|
||||||
if (typeof to === "undefined") return currentProgress;
|
|
||||||
if (typeof to === "string") {
|
|
||||||
to =
|
|
||||||
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
|
|
||||||
? currentProgress
|
|
||||||
: 0) + parseFloat(to);
|
|
||||||
}
|
|
||||||
currentProgress = to > 1 ? 1 : to;
|
|
||||||
repaint();
|
|
||||||
return currentProgress;
|
|
||||||
},
|
|
||||||
hide: function () {
|
|
||||||
clearTimeout(delayTimerId);
|
|
||||||
delayTimerId = null;
|
|
||||||
if (!showing) return;
|
|
||||||
showing = false;
|
|
||||||
if (progressTimerId != null) {
|
|
||||||
window.cancelAnimationFrame(progressTimerId);
|
|
||||||
progressTimerId = null;
|
|
||||||
}
|
|
||||||
(function loop() {
|
|
||||||
if (topbar.progress("+.1") >= 1) {
|
|
||||||
canvas.style.opacity -= 0.05;
|
|
||||||
if (canvas.style.opacity <= 0.05) {
|
|
||||||
canvas.style.display = "none";
|
|
||||||
fadeTimerId = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fadeTimerId = window.requestAnimationFrame(loop);
|
|
||||||
})();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof module === "object" && typeof module.exports === "object") {
|
|
||||||
module.exports = topbar;
|
|
||||||
} else if (typeof define === "function" && define.amd) {
|
|
||||||
define(function () {
|
|
||||||
return topbar;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.topbar = topbar;
|
|
||||||
}
|
|
||||||
}.call(this, window, document));
|
|
||||||
@@ -48,10 +48,10 @@ defmodule LabelmakerWeb.Home do
|
|||||||
|
|
||||||
preview_background =
|
preview_background =
|
||||||
case assigns.preview_bg do
|
case assigns.preview_bg do
|
||||||
"r" -> "bg-[linear-gradient(to_right,_black_25%,_white_75%)]"
|
"r" -> "bg-[linear-gradient(to_right,_black_33%,_white_67%)]"
|
||||||
"b" -> "bg-[linear-gradient(to_bottom,_black_25%,_white_75%)]"
|
"b" -> "bg-[linear-gradient(to_bottom,_black_33%,_white_67%)]"
|
||||||
"c" -> "bg-[#{assigns.color}]"
|
"c" -> "bg-[#{assigns.color}]"
|
||||||
_ -> "bg-[linear-gradient(to_right,_black_25%,_white_75%)]"
|
_ -> "bg-[linear-gradient(to_right,_black_33%,_white_67%)]"
|
||||||
end
|
end
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
@@ -103,6 +103,7 @@ defmodule LabelmakerWeb.Home do
|
|||||||
<div>
|
<div>
|
||||||
<label for="label" class="block text-sm font-medium">Label</label>
|
<label for="label" class="block text-sm font-medium">Label</label>
|
||||||
<input
|
<input
|
||||||
|
phx-hook="EnterToNewline"
|
||||||
type="text"
|
type="text"
|
||||||
id="label"
|
id="label"
|
||||||
name="label"
|
name="label"
|
||||||
@@ -110,6 +111,9 @@ defmodule LabelmakerWeb.Home do
|
|||||||
placeholder="Enter text"
|
placeholder="Enter text"
|
||||||
class={"mt-1 block w-full rounded border border-gray-300 px-3 py-2 text-fg-light dark:text-fg-dark dark:border-gray-600 focus:ring-primary dark:placeholder-gray-400/50 transition duration-300 #{if @label_too_long, do: "bg-danger", else: "bg-secondary-light dark:bg-secondary-dark"}"}
|
class={"mt-1 block w-full rounded border border-gray-300 px-3 py-2 text-fg-light dark:text-fg-dark dark:border-gray-600 focus:ring-primary dark:placeholder-gray-400/50 transition duration-300 #{if @label_too_long, do: "bg-danger", else: "bg-secondary-light dark:bg-secondary-dark"}"}
|
||||||
/>
|
/>
|
||||||
|
<p class="text-xs text-gray-600 dark:text-gray-400 m-0 ml-1">
|
||||||
|
<code>\n</code> or <Enter> for newlines
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
defmodule LabelmakerWeb.Label do
|
|
||||||
use LabelmakerWeb, :live_view
|
|
||||||
alias LabelmakerWeb.Constants
|
|
||||||
|
|
||||||
@label_dir Path.join(:code.priv_dir(:labelmaker), "static/labels")
|
|
||||||
File.mkdir_p!(@label_dir)
|
|
||||||
|
|
||||||
@defaults %{
|
|
||||||
"label" => "Labelmaker",
|
|
||||||
"font" => "Helvetica",
|
|
||||||
"color" => "black",
|
|
||||||
"size" => "24"
|
|
||||||
}
|
|
||||||
|
|
||||||
@permitted_keys Map.keys(@defaults)
|
|
||||||
|
|
||||||
def mount(params, _session, socket) do
|
|
||||||
options =
|
|
||||||
@defaults
|
|
||||||
|> Map.merge(params)
|
|
||||||
|> Map.take(@permitted_keys)
|
|
||||||
|
|
||||||
filename =
|
|
||||||
options
|
|
||||||
|> inspect()
|
|
||||||
|> (fn str -> :crypto.hash(:sha256, str) end).()
|
|
||||||
|> Base.encode16(case: :lower)
|
|
||||||
|
|
||||||
filename = filename <> ".png"
|
|
||||||
filepath = Path.join(@label_dir, filename)
|
|
||||||
|
|
||||||
unless File.exists?(filepath) do
|
|
||||||
generate_image(options, filepath)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok,
|
|
||||||
assign(socket,
|
|
||||||
label: options["label"],
|
|
||||||
image_path: ~p"/labels/#{filename}"
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render(assigns) do
|
|
||||||
IO.inspect(Constants.color_count())
|
|
||||||
IO.inspect(Constants.colors())
|
|
||||||
|
|
||||||
IO.inspect(Constants.font_count())
|
|
||||||
IO.inspect(Constants.fonts())
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<div>
|
|
||||||
<h1>Label: {@label}</h1>
|
|
||||||
<img src={@image_path} alt="" />
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp generate_image(options, filepath) do
|
|
||||||
args = [
|
|
||||||
"-background",
|
|
||||||
"none",
|
|
||||||
"-fill",
|
|
||||||
options["color"],
|
|
||||||
"-pointsize",
|
|
||||||
options["size"],
|
|
||||||
"-font",
|
|
||||||
options["font"],
|
|
||||||
"label:#{options["label"]}",
|
|
||||||
filepath
|
|
||||||
]
|
|
||||||
|
|
||||||
{_, 0} = System.cmd("magick", args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user