Created an Elixir PHoenix web framework application project
This commit is contained in:
		
							
								
								
									
										5
									
								
								assets/css/app.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								assets/css/app.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
@import "tailwindcss/base";
 | 
			
		||||
@import "tailwindcss/components";
 | 
			
		||||
@import "tailwindcss/utilities";
 | 
			
		||||
 | 
			
		||||
/* This file is for your main application CSS */
 | 
			
		||||
							
								
								
									
										44
									
								
								assets/js/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								assets/js/app.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
 | 
			
		||||
// to get started and then uncomment the line below.
 | 
			
		||||
// import "./user_socket.js"
 | 
			
		||||
 | 
			
		||||
// You can include dependencies in two ways.
 | 
			
		||||
//
 | 
			
		||||
// The simplest option is to put them in assets/vendor and
 | 
			
		||||
// import them using relative paths:
 | 
			
		||||
//
 | 
			
		||||
//     import "../vendor/some-package.js"
 | 
			
		||||
//
 | 
			
		||||
// Alternatively, you can `npm install some-package --prefix assets` and import
 | 
			
		||||
// them using a path starting with the package name:
 | 
			
		||||
//
 | 
			
		||||
//     import "some-package"
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
 | 
			
		||||
import "phoenix_html"
 | 
			
		||||
// Establish Phoenix Socket and LiveView configuration.
 | 
			
		||||
import {Socket} from "phoenix"
 | 
			
		||||
import {LiveSocket} from "phoenix_live_view"
 | 
			
		||||
import topbar from "../vendor/topbar"
 | 
			
		||||
 | 
			
		||||
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())
 | 
			
		||||
 | 
			
		||||
// connect if there are any LiveViews on the page
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										74
									
								
								assets/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								assets/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
// See the Tailwind configuration guide for advanced usage
 | 
			
		||||
// https://tailwindcss.com/docs/configuration
 | 
			
		||||
 | 
			
		||||
const plugin = require("tailwindcss/plugin")
 | 
			
		||||
const fs = require("fs")
 | 
			
		||||
const path = require("path")
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  content: [
 | 
			
		||||
    "./js/**/*.js",
 | 
			
		||||
    "../lib/sukaato_web.ex",
 | 
			
		||||
    "../lib/sukaato_web/**/*.*ex"
 | 
			
		||||
  ],
 | 
			
		||||
  theme: {
 | 
			
		||||
    extend: {
 | 
			
		||||
      colors: {
 | 
			
		||||
        brand: "#FD4F00",
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  plugins: [
 | 
			
		||||
    require("@tailwindcss/forms"),
 | 
			
		||||
    // Allows prefixing tailwind classes with LiveView classes to add rules
 | 
			
		||||
    // only when LiveView classes are applied, for example:
 | 
			
		||||
    //
 | 
			
		||||
    //     <div class="phx-click-loading:animate-ping">
 | 
			
		||||
    //
 | 
			
		||||
    plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
 | 
			
		||||
    plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
 | 
			
		||||
    plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
 | 
			
		||||
 | 
			
		||||
    // Embeds Heroicons (https://heroicons.com) into your app.css bundle
 | 
			
		||||
    // See your `CoreComponents.icon/1` for more information.
 | 
			
		||||
    //
 | 
			
		||||
    plugin(function({matchComponents, theme}) {
 | 
			
		||||
      let iconsDir = path.join(__dirname, "../deps/heroicons/optimized")
 | 
			
		||||
      let values = {}
 | 
			
		||||
      let icons = [
 | 
			
		||||
        ["", "/24/outline"],
 | 
			
		||||
        ["-solid", "/24/solid"],
 | 
			
		||||
        ["-mini", "/20/solid"],
 | 
			
		||||
        ["-micro", "/16/solid"]
 | 
			
		||||
      ]
 | 
			
		||||
      icons.forEach(([suffix, dir]) => {
 | 
			
		||||
        fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
 | 
			
		||||
          let name = path.basename(file, ".svg") + suffix
 | 
			
		||||
          values[name] = {name, fullPath: path.join(iconsDir, dir, file)}
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
      matchComponents({
 | 
			
		||||
        "hero": ({name, fullPath}) => {
 | 
			
		||||
          let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
 | 
			
		||||
          let size = theme("spacing.6")
 | 
			
		||||
          if (name.endsWith("-mini")) {
 | 
			
		||||
            size = theme("spacing.5")
 | 
			
		||||
          } else if (name.endsWith("-micro")) {
 | 
			
		||||
            size = theme("spacing.4")
 | 
			
		||||
          }
 | 
			
		||||
          return {
 | 
			
		||||
            [`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
 | 
			
		||||
            "-webkit-mask": `var(--hero-${name})`,
 | 
			
		||||
            "mask": `var(--hero-${name})`,
 | 
			
		||||
            "mask-repeat": "no-repeat",
 | 
			
		||||
            "background-color": "currentColor",
 | 
			
		||||
            "vertical-align": "middle",
 | 
			
		||||
            "display": "inline-block",
 | 
			
		||||
            "width": size,
 | 
			
		||||
            "height": size
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }, {values})
 | 
			
		||||
    })
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										165
									
								
								assets/vendor/topbar.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								assets/vendor/topbar.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @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));
 | 
			
		||||
		Reference in New Issue
	
	Block a user