From 914138b2214b75e2fa9ad8ff30dbacce26d7f920 Mon Sep 17 00:00:00 2001 From: Gavin McDonald Date: Mon, 12 May 2025 16:22:10 -0400 Subject: [PATCH] better newline support --- assets/js/app.js | 29 +++--- assets/js/hooks.js | 19 ++++ assets/vendor/topbar.js | 165 ------------------------------- lib/labelmaker_web/live/home.ex | 10 +- lib/labelmaker_web/live/label.ex | 74 -------------- 5 files changed, 38 insertions(+), 259 deletions(-) create mode 100644 assets/js/hooks.js delete mode 100644 assets/vendor/topbar.js delete mode 100644 lib/labelmaker_web/live/label.ex diff --git a/assets/js/app.js b/assets/js/app.js index d5e278a..1308ab1 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -16,29 +16,24 @@ // // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. -import "phoenix_html" +import 'phoenix_html'; // Establish Phoenix Socket and LiveView configuration. -import {Socket} from "phoenix" -import {LiveSocket} from "phoenix_live_view" -import topbar from "../vendor/topbar" +import { Socket } from 'phoenix'; +import { LiveSocket } from 'phoenix_live_view'; +import Hooks from './hooks'; -let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, { - longPollFallbackMs: 2500, - 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()) +let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content'); +let liveSocket = new LiveSocket('/live', Socket, { + hooks: Hooks, + longPollFallbackMs: 2500, + params: { _csrf_token: csrfToken }, +}); // 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: // >> liveSocket.enableDebug() // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session // >> liveSocket.disableLatencySim() -window.liveSocket = liveSocket - +window.liveSocket = liveSocket; diff --git a/assets/js/hooks.js b/assets/js/hooks.js new file mode 100644 index 0000000..637618d --- /dev/null +++ b/assets/js/hooks.js @@ -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; diff --git a/assets/vendor/topbar.js b/assets/vendor/topbar.js deleted file mode 100644 index 4195727..0000000 --- a/assets/vendor/topbar.js +++ /dev/null @@ -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)); diff --git a/lib/labelmaker_web/live/home.ex b/lib/labelmaker_web/live/home.ex index df160c9..3ecaf58 100644 --- a/lib/labelmaker_web/live/home.ex +++ b/lib/labelmaker_web/live/home.ex @@ -48,10 +48,10 @@ defmodule LabelmakerWeb.Home do preview_background = case assigns.preview_bg do - "r" -> "bg-[linear-gradient(to_right,_black_25%,_white_75%)]" - "b" -> "bg-[linear-gradient(to_bottom,_black_25%,_white_75%)]" + "r" -> "bg-[linear-gradient(to_right,_black_33%,_white_67%)]" + "b" -> "bg-[linear-gradient(to_bottom,_black_33%,_white_67%)]" "c" -> "bg-[#{assigns.color}]" - _ -> "bg-[linear-gradient(to_right,_black_25%,_white_75%)]" + _ -> "bg-[linear-gradient(to_right,_black_33%,_white_67%)]" end ~H""" @@ -103,6 +103,7 @@ defmodule LabelmakerWeb.Home do
+

+ \n or <Enter> for newlines +

diff --git a/lib/labelmaker_web/live/label.ex b/lib/labelmaker_web/live/label.ex deleted file mode 100644 index 83f08af..0000000 --- a/lib/labelmaker_web/live/label.ex +++ /dev/null @@ -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""" -
-

Label: {@label}

- -
- """ - 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