From 8e3222b78736c47b5dff75f8fe04b2c69b14dfbd Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Fri, 1 Aug 2025 20:35:23 +0800 Subject: [PATCH 1/8] Install and configure Cinder --- assets/tailwind.config.js | 14 +++++++++----- config/config.exs | 2 ++ mix.exs | 1 + mix.lock | 11 ++++++----- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index d2c5a0ac..c9b753a6 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -6,7 +6,11 @@ const fs = require("fs"); const path = require("path"); module.exports = { - content: ["./js/**/*.js", "./../lib/ash_admin/components/**/*.*ex"], + content: [ + "./js/**/*.js", + "./../lib/ash_admin/components/**/*.*ex", + "../deps/cinder/**/*.*ex", + ], theme: { extend: { colors: { @@ -25,19 +29,19 @@ module.exports = { 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 @@ -77,7 +81,7 @@ module.exports = { }; }, }, - { values } + { values }, ); }), ], diff --git a/config/config.exs b/config/config.exs index 53cd51f1..7584f898 100644 --- a/config/config.exs +++ b/config/config.exs @@ -45,6 +45,8 @@ config :tailwind, config :logger, level: :debug config :phoenix, :serve_endpoints, true +config :cinder, default_theme: "modern" + if config_env() == :dev do config :ash_admin, ash_domains: [ diff --git a/mix.exs b/mix.exs index 751d081b..f973d026 100644 --- a/mix.exs +++ b/mix.exs @@ -122,6 +122,7 @@ defmodule AshAdmin.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ + {:cinder, github: "sevenseacat/cinder"}, {:ash, "~> 3.0 and >= 3.4.63"}, {:ash_phoenix, "~> 2.1 and >= 2.1.8"}, {:phoenix_view, "~> 2.0"}, diff --git a/mix.lock b/mix.lock index 233e7ca6..d9f1302a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,11 +1,12 @@ %{ - "ash": {:hex, :ash, "3.5.31", "fea1abcbb58d00d1edf65ac5bccba5d679ca80754aaac6af7877cbf9056d4462", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.4 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.65 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a7e07c8ae297dd764d92dd3c478e8bbb825fc0dd14a5cce83b85d19235510a74"}, - "ash_phoenix": {:hex, :ash_phoenix, "2.3.10", "c038cbcd0550a4a26d7ee2d936d2886415dfa69fc5952f45b0e3737c3293a4d3", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:inertia, "~> 2.3", [hex: :inertia, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "c4b7d86e1636c82c6f6a89983af17f19e55f25b066120193d9d3524d2013456d"}, + "ash": {:hex, :ash, "3.5.33", "2d4986050ce1c86f711b53f9bb40d6b227871f0cc771dab0b8b814a75a27c5ab", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.4 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.65 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c7d1e043059967df749f9445bb903d62ed9c1defb5d45f6ddf32754b411ae93"}, + "ash_phoenix": {:hex, :ash_phoenix, "2.3.12", "34116f054ca4ef97b4badc73f028d78ee517692b713fd39f4c93f90bc2afd038", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:inertia, "~> 2.3", [hex: :inertia, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "27394e40b44ca06977e90bd0b38bce7bf41c6dab9fe2aa0b474fdb7c0c1f911b"}, "ash_postgres": {:hex, :ash_postgres, "2.6.11", "7c6b4fa9b8725c6644dd863323f8a2dae93f5df8ddae4b53df5dabde451a8b0c", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.72 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.14 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "d915f06406b46130559481b8b4290fd3bf60b0bd57bf5a4903cde0613b642c36"}, "ash_sql": {:hex, :ash_sql, "0.2.87", "17197c643918cdaee657946a1998860402dcf53a980f7665bb81d1fa53c224e7", [:mix], [{:ash, ">= 3.5.25 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "f82d6bf78f08bd9040af3adc28676965421598c88866074d8b1ccca65978d774"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, - "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, + "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, + "cinder": {:git, "https://github.com/sevenseacat/cinder.git", "51ae167876d3370dc5a531aa5b73a5d4efdf07b6", []}, "cowboy": {:hex, :cowboy, "2.13.0", "09d770dd5f6a22cc60c071f432cd7cb87776164527f205c5a6b0f24ff6b38990", [:make, :rebar3], [{:cowlib, ">= 2.14.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "e724d3a70995025d654c1992c7b11dbfea95205c047d86ff9bf1cda92ddc5614"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.15.0", "3c97a318a933962d1c12b96ab7c1d728267d2c523c25a5b57b0f93392b6e9e25", [:make, :rebar3], [], "hexpm", "4f00c879a64b4fe7c8fcb42a4281925e9ffdb928820b03c3ad325a617e857532"}, @@ -32,7 +33,7 @@ "git_ops": {:hex, :git_ops, "2.8.0", "29ac9ab68bf9645973cb2752047b987e75cbd3d9761489c615e3ba80018fa885", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.27 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "b535e4ad6b5d13e14c455e76f65825659081b5530b0827eb0232d18719530eec"}, "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, - "igniter": {:hex, :igniter, "0.6.22", "b170fc64ae0cae54a7713cf3f96e7c96183f81d75f31746de080b27518b0f96e", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "d753129f693a214da32f39ba5b335f7ea6e1e87833e647b280f395b3c3742acf"}, + "igniter": {:hex, :igniter, "0.6.25", "e2774a4605c2bc9fc38f689232604aea0fc925c7966ae8e928fd9ea2fa9d300c", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "b1916e1e45796d5c371c7671305e81277231617eb58b1c120915aba237fbce6a"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "lazy_html": {:hex, :lazy_html, "0.1.3", "8b9c8c135e95f7bc483de6195c4e1c0b2c913a5e2c57353ef4e82703b7ac8bd1", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "5f96f29587dcfed8a22281e8c44c6607e958ba821d90b9dfc003d1ef610f7d07"}, @@ -50,7 +51,7 @@ "phoenix": {:hex, :phoenix, "1.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"}, "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.0", "2791fac0e2776b640192308cc90c0dbcf67843ad51387ed4ecae2038263d708d", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b3a1fa036d7eb2f956774eda7a7638cf5123f8f2175aca6d6420a7f95e598e1c"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.0-rc.3", "95a531057ffb94e1fc0416369951f263af80ad5f98bcc61a118e502061d3c7a4", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9c2b8b541fbed6376593665f74548fbfc7c4d6ad42461474062215610856eeac"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.2", "af6f090e3dc7d5ff41de10aa1039e0543e8151f99afa44097a832bcb139790d8", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "54b2e4a31b8689a1604b3a2e0b1d54bb89e9476022c9ebbe585e9dd800674965"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, From f695dde2ac2492d8aa7d00bccd26c8c69c5d619d Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sat, 2 Aug 2025 21:09:39 +0800 Subject: [PATCH 2/8] Convert DataTable to use Cinder tables instead of hand-rolled tables --- .../components/resource/data_table.ex | 385 +++++------------- lib/ash_admin/components/resource/resource.ex | 9 +- lib/ash_admin/components/resource/table.ex | 18 +- 3 files changed, 112 insertions(+), 300 deletions(-) diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index 760a190d..6d7ea149 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -1,9 +1,9 @@ defmodule AshAdmin.Components.Resource.DataTable do @moduledoc false use Phoenix.LiveComponent + use Cinder.Table.UrlSync import AshAdmin.Helpers - import AshPhoenix.LiveView alias AshAdmin.Components.Resource.Table attr :resource, :atom @@ -99,26 +99,63 @@ defmodule AshAdmin.Components.Resource.DataTable do
- {render_pagination_links(assigns, :top)} -
Only showing up to 1000 rows. To show more, enable pagination for the action in question.
- - {render_pagination_links(assigns, :bottom)} + url_state={@url_state} + theme="default" + id={"cinder-table-#{@resource}"} + > + + <:col + :for={field_name <- AshAdmin.Resource.table_columns(@resource)} + :let={record} + field={to_string(field_name)} + label={to_name(field_name)} + > + {render_field_value(record, field_name, assigns)} + + + + <:col :let={record} :if={actions?(@resource)} label="Actions"> +
+
+ <.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&table=#{@table}&primary_key=#{encode_primary_key(record)}&action_type=read"}> + + +
+ +
+ <.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&table=#{@table}&primary_key=#{encode_primary_key(record)}"}> + + +
+ +
+ <.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&table=#{@table}&primary_key=#{encode_primary_key(record)}"}> + + +
+ + +
+ +
@@ -133,8 +170,8 @@ defmodule AshAdmin.Components.Resource.DataTable do |> assign_new(:initialized, fn -> false end) |> assign_new(:default, fn -> nil end) |> assign_new(:page_params, fn -> nil end) - |> assign_new(:page_num, fn -> nil end) - |> assign_new(:thousand_records_warning, fn -> false end)} + |> assign_new(:thousand_records_warning, fn -> false end) + |> assign_new(:url_state, fn -> %{} end)} end def update(assigns, socket) do @@ -165,6 +202,15 @@ defmodule AshAdmin.Components.Resource.DataTable do socket = assign(socket, :query, query) + # Build the Ash query for Cinder by extracting from the AshPhoenix.Form + socket = + if run_now? && (socket.assigns[:tables] in [[], nil] || socket.assigns[:table]) do + ash_query = socket.assigns.query.source + assign(socket, :ash_query, ash_query) + else + assign(socket, :ash_query, nil) + end + socket = if socket.assigns.action.pagination do default_limit = @@ -181,14 +227,9 @@ defmodule AshAdmin.Components.Resource.DataTable do :page_params, page_params ) - |> assign( - :page_num, - page_num_from_page_params(page_params) - ) else socket |> assign(:page_params, nil) - |> assign(:page_num, 1) end socket = @@ -221,6 +262,12 @@ defmodule AshAdmin.Components.Resource.DataTable do end end + # Handle URL state changes from Cinder + def handle_params(params, uri, socket) do + socket = Cinder.Table.UrlSync.handle_params(params, uri, socket) + {:noreply, socket} + end + defp load_fields(query) do query |> Ash.Query.select([]) @@ -245,28 +292,7 @@ defmodule AshAdmin.Components.Resource.DataTable do end) end - def handle_event("next_page", _, socket) do - params = %{"page" => AshPhoenix.LiveView.page_link_params(socket.assigns.data, "next")} - - {:noreply, - push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))} - end - - def handle_event("prev_page", _, socket) do - params = %{"page" => AshPhoenix.LiveView.page_link_params(socket.assigns.data, "prev")} - - {:noreply, - push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))} - end - - def handle_event("specific_page", %{"page" => page}, socket) do - params = %{ - "page" => AshPhoenix.LiveView.page_link_params(socket.assigns.data, String.to_integer(page)) - } - - {:noreply, - push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))} - end + # Remove all pagination event handlers since Cinder handles pagination now def handle_event("validate", %{"query" => query}, socket) do query = AshPhoenix.Form.validate(socket.assigns.query, query) @@ -417,255 +443,34 @@ defmodule AshAdmin.Components.Resource.DataTable do AshPhoenix.Form.validate(form, new_params) end - defp render_pagination_links(assigns, placement) do - assigns = assign(assigns, :placement, placement) - - ~H""" -
-
-
- - {render_pagination_information(assigns, true)} - -
- -
-
- """ - end - - defp render_page_links(assigns, page_nums) do - assigns = assign(assigns, page_nums: page_nums) - - ~H""" - - """ - end - - defp render_pagination_information(assigns, small? \\ false) do - assigns = assign(assigns, :small, small?) - - ~H""" -

- - Showing {first(@data)} - to {last(@data)} - <%= if count(@data) do %> - of - <% end %> - - - {count(@data)} results - -

- """ - end - - defp page_num_from_page_params(params) do - cond do - !params[:offset] || params[:after] || params[:before] -> - 1 - - params[:offset] && params[:limit] -> - trunc(Float.ceil(params[:offset] / params[:limit])) + 1 - - true -> + # Field rendering - delegate to existing Table component logic + defp render_field_value(record, field_name, assigns) do + attribute = Ash.Resource.Info.field(assigns.resource, field_name) + format_fields = AshAdmin.Resource.format_fields(assigns.resource) + show_sensitive_fields = AshAdmin.Resource.show_sensitive_fields(assigns.resource) + + if attribute do + Table.render_attribute( + assigns.domain, + record, + attribute, + format_fields, + show_sensitive_fields, + assigns.actor, nil - end - end - - defp show_pagination_links?({:ok, _page}, :bottom), do: true - defp show_pagination_links?({:ok, page}, :top), do: page.limit >= 20 - defp show_pagination_links?(_, _), do: false - - defp first({:ok, %Ash.Page.Offset{offset: offset}}) do - (offset || 0) + 1 - end - - defp first(_), do: nil - - defp last({:ok, %Ash.Page.Offset{offset: offset, results: results}}) do - Enum.count(results) + offset - end - - defp last(_), do: nil - - defp render_middle_page_num(assigns, num, trailing_page_nums) do - ellipsis? = num in trailing_page_nums || num <= 3 - - assigns = - assign(assigns, num: num, trailing_page_nums: trailing_page_nums, ellipsis: ellipsis?) - - ~H""" - - - ... - - - {@num} - - - """ - end - - defp show_ellipses?(%Ash.Page.Offset{count: count, limit: limit}) when not is_nil(count) do - page_nums = - count - |> Kernel./(limit) - |> Float.ceil() - |> trunc() - - page_nums > 6 - end - - defp show_ellipses?({:ok, data}), do: show_ellipses?(data) - defp show_ellipses?(_), do: false - - def leading_page_nums({:ok, data}), do: leading_page_nums(data) - def leading_page_nums(%Ash.Page.Offset{count: nil}), do: [] - - def leading_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do - page_nums = - count - |> Kernel./(limit) - |> Float.ceil() - |> trunc() - - 1..min(3, page_nums) - end - - def leading_page_nums(_), do: [] - - def trailing_page_nums({:ok, data}), do: trailing_page_nums(data) - def trailing_page_nums(%Ash.Page.Offset{count: nil}), do: [] - - def trailing_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do - page_nums = - count - |> Kernel./(limit) - |> Float.ceil() - |> trunc() - - if page_nums > 3 do - max(page_nums - 2, 4)..page_nums + ) else - [] + "..." end + rescue + _ -> + "..." end - defp data({:ok, data}), do: data(data) - defp data({:error, _}), do: [] - defp data(%Ash.Page.Offset{results: results}), do: results - defp data(%Ash.Page.Keyset{results: results}), do: results - defp data(data), do: data - - defp offset?({:ok, data}), do: offset?(data) - defp offset?(%Ash.Page.Offset{}), do: true - defp offset?(_), do: false - - defp keyset?({:ok, data}), do: keyset?(data) - defp keyset?(%Ash.Page.Keyset{}), do: true - defp keyset?(_), do: false - - defp count({:ok, %{count: count}}), do: count - defp count(%{count: count}), do: count - defp count(_), do: nil + defp actions?(resource) do + AshAdmin.Helpers.primary_action(resource, :update) || + AshAdmin.Resource.show_action(resource) || + AshAdmin.Resource.actor?(resource) || + AshAdmin.Helpers.primary_action(resource, :destroy) + end end diff --git a/lib/ash_admin/components/resource/resource.ex b/lib/ash_admin/components/resource/resource.ex index ff54cb3e..890bed0c 100644 --- a/lib/ash_admin/components/resource/resource.ex +++ b/lib/ash_admin/components/resource/resource.ex @@ -4,7 +4,14 @@ defmodule AshAdmin.Components.Resource do require Ash.Query - alias AshAdmin.Components.Resource.{DataTable, Form, GenericAction, Info, Nav, Show} + alias AshAdmin.Components.Resource.{ + DataTable, + Form, + GenericAction, + Info, + Nav, + Show + } # prop hide_filter, :boolean, default: true attr :resource, :any, required: true diff --git a/lib/ash_admin/components/resource/table.ex b/lib/ash_admin/components/resource/table.ex index a822701f..364d7a82 100644 --- a/lib/ash_admin/components/resource/table.ex +++ b/lib/ash_admin/components/resource/table.ex @@ -96,15 +96,15 @@ defmodule AshAdmin.Components.Resource.Table do |> Enum.reject(&(&1.name in skip)) end - defp render_attribute( - domain, - record, - attribute, - formats, - show_sensitive_fields, - actor, - relationship_name - ) do + def render_attribute( + domain, + record, + attribute, + formats, + show_sensitive_fields, + actor, + relationship_name + ) do process_attribute( domain, record, From fb03d457e524cccd5ace0c7914dee7a265418647 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sat, 2 Aug 2025 23:46:30 +0800 Subject: [PATCH 3/8] Create a Cinder theme from the existing table styles so that the replacement is seamless --- .formatter.exs | 2 +- .../components/resource/data_table.ex | 33 ++-- lib/ash_admin/themes/ash_admin_theme.ex | 153 ++++++++++++++++++ 3 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 lib/ash_admin/themes/ash_admin_theme.ex diff --git a/.formatter.exs b/.formatter.exs index b596ce54..853c82b8 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -35,7 +35,7 @@ macro_locals_without_parens = [ [ inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], - import_deps: [:phoenix], + import_deps: [:phoenix, :cinder], locals_without_parens: spark_locals_without_parens ++ macro_locals_without_parens, plugins: [Phoenix.LiveView.HTMLFormatter], export: [ diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index 6d7ea149..d8eed26f 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -1,10 +1,10 @@ defmodule AshAdmin.Components.Resource.DataTable do @moduledoc false use Phoenix.LiveComponent - use Cinder.Table.UrlSync import AshAdmin.Helpers alias AshAdmin.Components.Resource.Table + alias AshAdmin.Themes.AshAdminTheme attr :resource, :atom attr :domain, :atom @@ -109,14 +109,13 @@ defmodule AshAdmin.Components.Resource.DataTable do :if={@ash_query && match?({:ok, _data}, @data)} query={@ash_query} actor={@actor} - url_state={@url_state} - theme="default" + theme={AshAdminTheme} id={"cinder-table-#{@resource}"} > <:col - :for={field_name <- AshAdmin.Resource.table_columns(@resource)} :let={record} + :for={field_name <- AshAdmin.Resource.table_columns(@resource)} field={to_string(field_name)} label={to_name(field_name)} > @@ -128,19 +127,28 @@ defmodule AshAdmin.Components.Resource.DataTable do
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&table=#{@table}&primary_key=#{encode_primary_key(record)}&action_type=read"}> - +
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&table=#{@table}&primary_key=#{encode_primary_key(record)}"}> - +
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&table=#{@table}&primary_key=#{encode_primary_key(record)}"}> - +
@@ -151,7 +159,10 @@ defmodule AshAdmin.Components.Resource.DataTable do phx-value-domain={@domain} phx-value-pkey={encode_primary_key(record)} > - +
@@ -262,12 +273,6 @@ defmodule AshAdmin.Components.Resource.DataTable do end end - # Handle URL state changes from Cinder - def handle_params(params, uri, socket) do - socket = Cinder.Table.UrlSync.handle_params(params, uri, socket) - {:noreply, socket} - end - defp load_fields(query) do query |> Ash.Query.select([]) diff --git a/lib/ash_admin/themes/ash_admin_theme.ex b/lib/ash_admin/themes/ash_admin_theme.ex new file mode 100644 index 00000000..2ca90cd4 --- /dev/null +++ b/lib/ash_admin/themes/ash_admin_theme.ex @@ -0,0 +1,153 @@ +defmodule AshAdmin.Themes.AshAdminTheme do + @moduledoc """ + Custom Cinder theme that matches AshAdmin's existing table styling. + + This theme replicates the visual appearance of the original AshAdmin table + component to ensure visual consistency when using Cinder for pagination. + """ + use Cinder.Theme + + # Main table styling - matches the Resource.Table component exactly + component Cinder.Components.Table do + # Minimal container - no extra styling + set :container_class, "" + + # Table wrapper - no styling to match original + set :table_wrapper_class, "" + + # Main table with exact same classes as original Resource.Table + set :table_class, "rounded-t-lg m-5 w-5/6 mx-auto text-left" + + # Header styling - minimal to match original + set :thead_class, "text-left border-b-2" + set :header_row_class, "" + set :th_class, "" + + # Body styling - no extra classes + set :tbody_class, "" + + # Row styling - exact border from original + set :row_class, "border-b-2" + + # Cell styling - only padding from original + set :td_class, "py-3" + + # Simple loading and error states + set :loading_class, "text-center py-4 text-gray-500" + set :empty_class, "text-center py-4 text-gray-500" + set :error_container_class, "text-center py-4" + set :error_message_class, "text-red-600" + end + + # Pagination styling - minimal to integrate well with table + component Cinder.Components.Pagination do + set :pagination_wrapper_class, "w-5/6 mx-auto" + + set :pagination_container_class, + "bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200" + + # Simple info styling + set :pagination_info_class, "text-sm text-gray-700" + set :pagination_count_class, "font-medium text-gray-900" + + # Clean navigation styling + set :pagination_nav_class, "inline-flex rounded-md shadow-sm -space-x-px" + + set :pagination_button_class, + "relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50" + + set :pagination_current_class, + "bg-indigo-50 border-indigo-500 text-indigo-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium" + end + + # Sorting styling - simple indicators + component Cinder.Components.Sorting do + set :sort_indicator_class, "ml-1" + set :sort_arrow_wrapper_class, "text-gray-400" + set :sort_asc_icon_class, "h-4 w-4" + set :sort_asc_icon_name, "hero-chevron-up" + set :sort_desc_icon_class, "h-4 w-4" + set :sort_desc_icon_name, "hero-chevron-down" + set :sort_none_icon_class, "h-4 w-4" + set :sort_none_icon_name, "hero-chevron-up-down" + end + + # Filter styling - matches AshAdmin's form aesthetic + component Cinder.Components.Filters do + # Container styling - clean and simple + set :filter_container_class, "bg-white border border-gray-200 rounded-md p-4 mb-4" + set :filter_header_class, "mb-4" + set :filter_title_class, "text-lg font-medium text-gray-900 mb-2" + set :filter_count_class, "text-sm text-gray-500" + set :filter_clear_all_class, "text-sm text-indigo-600 hover:text-indigo-500 font-medium" + + # Input wrapper and labels + set :filter_inputs_class, "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3" + set :filter_input_wrapper_class, "" + set :filter_label_class, "block text-sm font-medium text-gray-700 mb-1" + set :filter_clear_button_class, "ml-2 text-gray-400 hover:text-gray-600" + + # Input styling - matches AshAdmin form inputs + set :filter_text_input_class, + "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + + set :filter_select_input_class, + "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + + set :filter_date_input_class, + "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + + set :filter_number_input_class, + "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + + # Boolean filter styling + set :filter_boolean_container_class, "flex space-x-4" + set :filter_boolean_option_class, "flex items-center" + + set :filter_boolean_radio_class, + "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300" + + set :filter_boolean_label_class, "ml-2 block text-sm text-gray-900" + + # Multi-select dropdown styling + set :filter_multiselect_container_class, "relative" + + set :filter_multiselect_dropdown_class, + "absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto" + + set :filter_multiselect_option_class, + "px-3 py-2 hover:bg-gray-100 cursor-pointer flex items-center" + + set :filter_multiselect_checkbox_class, + "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded mr-2" + + set :filter_multiselect_label_class, "text-sm text-gray-900" + set :filter_multiselect_empty_class, "px-3 py-2 text-sm text-gray-500 italic" + + # Multi-checkboxes styling + set :filter_multicheckboxes_container_class, "space-y-2 max-h-40 overflow-y-auto" + set :filter_multicheckboxes_option_class, "flex items-center" + + set :filter_multicheckboxes_checkbox_class, + "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + + set :filter_multicheckboxes_label_class, "ml-2 text-sm text-gray-900" + + # Range filter styling + set :filter_range_container_class, "flex items-center space-x-2" + set :filter_range_input_group_class, "flex items-center space-x-2" + set :filter_range_separator_class, "text-gray-500" + end + + # Loading component styling + component Cinder.Components.Loading do + set :loading_overlay_class, + "absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center" + + set :loading_container_class, "flex flex-col items-center" + set :loading_class, "mt-2 text-sm text-gray-500" + set :loading_spinner_class, "animate-spin h-8 w-8 text-indigo-600" + set :loading_spinner_circle_class, "opacity-25" + set :loading_spinner_path_class, "opacity-75" + end +end From 478215c2e3fde71b29db5fec6646e30e32c13d68 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sun, 3 Aug 2025 22:05:19 +0800 Subject: [PATCH 4/8] Add DSL to allow sort/filter columns to be defined for a resource (Updated the AshAdmin theme to make the filters fit into the look and feel --- dev/resources/accounts/resources/user.ex | 2 + .../components/resource/data_table.ex | 34 +- lib/ash_admin/resource/resource.ex | 17 + lib/ash_admin/themes/ash_admin_theme.ex | 106 +- priv/static/assets/app.css | 2501 +---- priv/static/assets/app.css.gz | Bin 8307 -> 12364 bytes priv/static/assets/app.js | 8544 +---------------- priv/static/assets/app.js.gz | Bin 42130 -> 2940 bytes priv/static/cache_manifest.json | 4 +- 9 files changed, 100 insertions(+), 11108 deletions(-) diff --git a/dev/resources/accounts/resources/user.ex b/dev/resources/accounts/resources/user.ex index 65dbb0fa..5a178090 100644 --- a/dev/resources/accounts/resources/user.ex +++ b/dev/resources/accounts/resources/user.ex @@ -20,6 +20,8 @@ defmodule Demo.Accounts.User do read_actions [:me, :read, :by_id, :by_name] table_columns [:id, :first_name, :last_name, :representative, :admin, :full_name, :api_key, :date_of_birth] + table_filterable_columns [:first_name] + table_sortable_columns [:first_name, :last_name] show_calculations [:multi_arguments, :is_super_admin?, :full_name, :nested_embed] end diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index d8eed26f..a0c8b38a 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -112,12 +112,14 @@ defmodule AshAdmin.Components.Resource.DataTable do theme={AshAdminTheme} id={"cinder-table-#{@resource}"} > - + <:col :let={record} :for={field_name <- AshAdmin.Resource.table_columns(@resource)} field={to_string(field_name)} label={to_name(field_name)} + filter={is_filterable?(@resource, field_name)} + sort={is_sortable?(@resource, field_name)} > {render_field_value(record, field_name, assigns)} @@ -181,8 +183,7 @@ defmodule AshAdmin.Components.Resource.DataTable do |> assign_new(:initialized, fn -> false end) |> assign_new(:default, fn -> nil end) |> assign_new(:page_params, fn -> nil end) - |> assign_new(:thousand_records_warning, fn -> false end) - |> assign_new(:url_state, fn -> %{} end)} + |> assign_new(:thousand_records_warning, fn -> false end)} end def update(assigns, socket) do @@ -472,6 +473,33 @@ defmodule AshAdmin.Components.Resource.DataTable do "..." end + # Check if a field should be sortable + defp is_sortable?(resource, field_name) do + sortable_columns = AshAdmin.Resource.table_sortable_columns(resource) + + case sortable_columns do + # If not specified, no columns are sortable + nil -> false + list -> field_name in list + end + end + + # Check if a field should be filterable + defp is_filterable?(resource, field_name) do + filterable_columns = AshAdmin.Resource.table_filterable_columns(resource) + + case filterable_columns do + # If not specified, no columns are filterable + nil -> false + list -> field_name in list && has_attribute?(resource, field_name) + end + end + + # Check if field is an actual resource attribute (not a relationship or calculated field) + defp has_attribute?(resource, field_name) do + Ash.Resource.Info.field(resource, field_name) != nil + end + defp actions?(resource) do AshAdmin.Helpers.primary_action(resource, :update) || AshAdmin.Resource.show_action(resource) || diff --git a/lib/ash_admin/resource/resource.ex b/lib/ash_admin/resource/resource.ex index f08d5e2d..db719617 100644 --- a/lib/ash_admin/resource/resource.ex +++ b/lib/ash_admin/resource/resource.ex @@ -76,6 +76,15 @@ defmodule AshAdmin.Resource do type: {:list, :atom}, doc: "The list of attributes to render on the table view." ], + table_sortable_columns: [ + type: {:list, :atom}, + doc: "The list of columns that can be sorted. If not specified, no columns are sortable." + ], + table_filterable_columns: [ + type: {:list, :atom}, + doc: + "The list of columns that can be filtered. If not specified, no columns are filterable." + ], format_fields: [ type: {:list, :any}, doc: """ @@ -236,6 +245,14 @@ defmodule AshAdmin.Resource do end) end + def table_sortable_columns(resource) do + Spark.Dsl.Extension.get_opt(resource, [:admin], :table_sortable_columns, nil, true) + end + + def table_filterable_columns(resource) do + Spark.Dsl.Extension.get_opt(resource, [:admin], :table_filterable_columns, nil, true) + end + defp find_polymorphic_tables(resource, domains) do domains |> Enum.flat_map(&AshAdmin.Domain.show_resources/1) diff --git a/lib/ash_admin/themes/ash_admin_theme.ex b/lib/ash_admin/themes/ash_admin_theme.ex index 2ca90cd4..e71cfaf2 100644 --- a/lib/ash_admin/themes/ash_admin_theme.ex +++ b/lib/ash_admin/themes/ash_admin_theme.ex @@ -18,10 +18,12 @@ defmodule AshAdmin.Themes.AshAdminTheme do # Main table with exact same classes as original Resource.Table set :table_class, "rounded-t-lg m-5 w-5/6 mx-auto text-left" - # Header styling - minimal to match original + # Header styling - minimal to match original with better sort interaction set :thead_class, "text-left border-b-2" set :header_row_class, "" - set :th_class, "" + + set :th_class, + "cursor-pointer select-none py-2 pr-2 text-sm font-semibold text-gray-900 transition-colors" # Body styling - no extra classes set :tbody_class, "" @@ -60,83 +62,67 @@ defmodule AshAdmin.Themes.AshAdminTheme do "bg-indigo-50 border-indigo-500 text-indigo-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium" end - # Sorting styling - simple indicators + # Sorting styling - clean indicators that match AshAdmin's minimal style component Cinder.Components.Sorting do - set :sort_indicator_class, "ml-1" - set :sort_arrow_wrapper_class, "text-gray-400" - set :sort_asc_icon_class, "h-4 w-4" + set :sort_indicator_class, "inline-block ml-1" + set :sort_arrow_wrapper_class, "text-gray-400 hover:text-gray-600" + set :sort_asc_icon_class, "h-4 w-4 bg-gray-600 inline-block" set :sort_asc_icon_name, "hero-chevron-up" - set :sort_desc_icon_class, "h-4 w-4" + set :sort_desc_icon_class, "h-4 w-4 bg-gray-600 inline-block" set :sort_desc_icon_name, "hero-chevron-down" - set :sort_none_icon_class, "h-4 w-4" + set :sort_none_icon_class, "h-4 w-4 bg-gray-400 inline-block opacity-50" set :sort_none_icon_name, "hero-chevron-up-down" end - # Filter styling - matches AshAdmin's form aesthetic + # Filter styling - matches AshAdmin's clean, minimal form aesthetic component Cinder.Components.Filters do - # Container styling - clean and simple - set :filter_container_class, "bg-white border border-gray-200 rounded-md p-4 mb-4" - set :filter_header_class, "mb-4" - set :filter_title_class, "text-lg font-medium text-gray-900 mb-2" - set :filter_count_class, "text-sm text-gray-500" - set :filter_clear_all_class, "text-sm text-indigo-600 hover:text-indigo-500 font-medium" - - # Input wrapper and labels - set :filter_inputs_class, "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3" - set :filter_input_wrapper_class, "" - set :filter_label_class, "block text-sm font-medium text-gray-700 mb-1" - set :filter_clear_button_class, "ml-2 text-gray-400 hover:text-gray-600" - - # Input styling - matches AshAdmin form inputs - set :filter_text_input_class, - "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" - - set :filter_select_input_class, - "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" - - set :filter_date_input_class, - "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + # Container styling - minimal border, matches table width + set :filter_container_class, "border-b-2 border-gray-200 py-4 w-5/6 mx-auto mb-2" + set :filter_header_class, "flex items-center justify-between mb-4" + set :filter_title_class, "font-medium text-gray-800" + set :filter_count_class, "text-sm text-gray-600 bg-gray-200 px-2 py-1 rounded" + set :filter_clear_all_class, "text-sm text-blue-600 hover:text-blue-800 font-medium underline" - set :filter_number_input_class, - "block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" - - # Boolean filter styling - set :filter_boolean_container_class, "flex space-x-4" - set :filter_boolean_option_class, "flex items-center" + # Input wrapper and labels - compact layout + set :filter_inputs_class, "flow-root" + set :filter_input_wrapper_class, "float-left mr-6 mb-4 space-y-2" + set :filter_label_class, "block text-sm font-medium text-gray-700" - set :filter_boolean_radio_class, - "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300" + set :filter_placeholder_class, + "text-sm text-gray-500 italic p-3 border border-gray-200 rounded bg-gray-50" - set :filter_boolean_label_class, "ml-2 block text-sm text-gray-900" + set :filter_clear_button_class, "ml-2 text-gray-400 hover:text-gray-600 text-sm" - # Multi-select dropdown styling - set :filter_multiselect_container_class, "relative" + # Input styling - clean, minimal inputs that match AshAdmin + set :filter_text_input_class, + "w-48 px-3 py-2 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500" - set :filter_multiselect_dropdown_class, - "absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto" + set :filter_select_input_class, + "w-48 px-3 py-2 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-white" - set :filter_multiselect_option_class, - "px-3 py-2 hover:bg-gray-100 cursor-pointer flex items-center" + set :filter_date_input_class, + "w-40 px-3 py-2 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500" - set :filter_multiselect_checkbox_class, - "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded mr-2" + set :filter_number_input_class, + "w-28 px-3 py-2 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500" - set :filter_multiselect_label_class, "text-sm text-gray-900" - set :filter_multiselect_empty_class, "px-3 py-2 text-sm text-gray-500 italic" + # Select filter (dropdown interface) + set :filter_select_container_class, "relative" - # Multi-checkboxes styling - set :filter_multicheckboxes_container_class, "space-y-2 max-h-40 overflow-y-auto" - set :filter_multicheckboxes_option_class, "flex items-center" + set :filter_select_dropdown_class, + "absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded shadow-lg max-h-48 overflow-auto" - set :filter_multicheckboxes_checkbox_class, - "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" + set :filter_select_option_class, + "px-3 py-2 hover:bg-gray-100 cursor-pointer flex items-center text-sm" - set :filter_multicheckboxes_label_class, "ml-2 text-sm text-gray-900" + set :filter_select_label_class, "text-sm text-gray-800 cursor-pointer select-none flex-1" + set :filter_select_empty_class, "px-3 py-2 text-sm text-gray-500 italic" - # Range filter styling - set :filter_range_container_class, "flex items-center space-x-2" - set :filter_range_input_group_class, "flex items-center space-x-2" - set :filter_range_separator_class, "text-gray-500" + # Boolean filter styling - compact radio buttons + set :filter_boolean_container_class, "flex space-x-4 h-10 items-center" + set :filter_boolean_option_class, "flex items-center space-x-2" + set :filter_boolean_radio_class, "h-4 w-4 text-blue-600 focus:ring-blue-500 focus:ring-1" + set :filter_boolean_label_class, "text-sm text-gray-700 cursor-pointer" end # Loading component styling diff --git a/priv/static/assets/app.css b/priv/static/assets/app.css index 4e331b93..e9d97ab6 100644 --- a/priv/static/assets/app.css +++ b/priv/static/assets/app.css @@ -1,2500 +1 @@ -/* -! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0px; - padding-top: 0.5rem; - padding-right: 0.75rem; - padding-bottom: 0.5rem; - padding-left: 0.75rem; - font-size: 1rem; - line-height: 1.5rem; - --tw-shadow: 0 0 #0000; -} - -[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - border-color: #2563eb; -} - -input::-moz-placeholder, textarea::-moz-placeholder { - color: #6b7280; - opacity: 1; -} - -input::placeholder,textarea::placeholder { - color: #6b7280; - opacity: 1; -} - -::-webkit-datetime-edit-fields-wrapper { - padding: 0; -} - -::-webkit-date-and-time-value { - min-height: 1.5em; -} - -::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { - padding-top: 0; - padding-bottom: 0; -} - -select { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); - background-position: right 0.5rem center; - background-repeat: no-repeat; - background-size: 1.5em 1.5em; - padding-right: 2.5rem; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; -} - -[multiple] { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: 0.75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -[type='checkbox'],[type='radio'] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: 0; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - display: inline-block; - vertical-align: middle; - background-origin: border-box; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-shrink: 0; - height: 1rem; - width: 1rem; - color: #2563eb; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - --tw-shadow: 0 0 #0000; -} - -[type='checkbox'] { - border-radius: 0px; -} - -[type='radio'] { - border-radius: 100%; -} - -[type='checkbox']:focus,[type='radio']:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 2px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); -} - -[type='checkbox']:checked,[type='radio']:checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type='checkbox']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); -} - -[type='radio']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); -} - -[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='checkbox']:indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='file'] { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -[type='file']:focus { - outline: 1px solid ButtonText; - outline: 1px auto -webkit-focus-ring-color; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.hero-arrow-left-solid { - --hero-arrow-left-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-arrow-left-solid); - mask: var(--hero-arrow-left-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-arrow-path { - --hero-arrow-path: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-arrow-path); - mask: var(--hero-arrow-path); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-check { - --hero-check: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-check); - mask: var(--hero-check); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-exclamation-circle-mini { - --hero-exclamation-circle-mini: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-exclamation-circle-mini); - mask: var(--hero-exclamation-circle-mini); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-eye-slash-solid { - --hero-eye-slash-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-eye-slash-solid); - mask: var(--hero-eye-slash-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-eye-solid { - --hero-eye-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-eye-solid); - mask: var(--hero-eye-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-information-circle-mini { - --hero-information-circle-mini: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-information-circle-mini); - mask: var(--hero-information-circle-mini); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-information-circle-solid { - --hero-information-circle-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-information-circle-solid); - mask: var(--hero-information-circle-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-key { - --hero-key: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-key); - mask: var(--hero-key); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-key-solid { - --hero-key-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-key-solid); - mask: var(--hero-key-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-magnifying-glass-circle { - --hero-magnifying-glass-circle: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-magnifying-glass-circle); - mask: var(--hero-magnifying-glass-circle); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-minus { - --hero-minus: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-minus); - mask: var(--hero-minus); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-pencil-solid { - --hero-pencil-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-pencil-solid); - mask: var(--hero-pencil-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-plus { - --hero-plus: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-plus); - mask: var(--hero-plus); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-x-circle { - --hero-x-circle: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-x-circle); - mask: var(--hero-x-circle); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-x-circle-solid { - --hero-x-circle-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-x-circle-solid); - mask: var(--hero-x-circle-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-x-mark { - --hero-x-mark: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-x-mark); - mask: var(--hero-x-mark); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.hero-x-mark-solid { - --hero-x-mark-solid: url('data:image/svg+xml;utf8,'); - -webkit-mask: var(--hero-x-mark-solid); - mask: var(--hero-x-mark-solid); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: currentColor; - vertical-align: middle; - display: inline-block; - width: 1.25rem; - height: 1.25rem; -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} - -.static { - position: static; -} - -.fixed { - position: fixed; -} - -.absolute { - position: absolute; -} - -.relative { - position: relative; -} - -.inset-0 { - inset: 0px; -} - -.-inset-y-px { - top: -1px; - bottom: -1px; -} - -.inset-y-0 { - top: 0px; - bottom: 0px; -} - -.-left-4 { - left: -1rem; -} - -.-right-4 { - right: -1rem; -} - -.left-0 { - left: 0px; -} - -.right-0 { - right: 0px; -} - -.right-1 { - right: 0.25rem; -} - -.right-2 { - right: 0.5rem; -} - -.right-5 { - right: 1.25rem; -} - -.top-1 { - top: 0.25rem; -} - -.top-2 { - top: 0.5rem; -} - -.top-6 { - top: 1.5rem; -} - -.z-0 { - z-index: 0; -} - -.z-10 { - z-index: 10; -} - -.z-50 { - z-index: 50; -} - -.col-span-2 { - grid-column: span 2 / span 2; -} - -.col-span-3 { - grid-column: span 3 / span 3; -} - -.col-span-6 { - grid-column: span 6 / span 6; -} - -.col-span-full { - grid-column: 1 / -1; -} - -.float-right { - float: right; -} - -.-m-3 { - margin: -0.75rem; -} - -.m-2 { - margin: 0.5rem; -} - -.m-5 { - margin: 1.25rem; -} - -.-my-4 { - margin-top: -1rem; - margin-bottom: -1rem; -} - -.mx-1 { - margin-left: 0.25rem; - margin-right: 0.25rem; -} - -.mx-12 { - margin-left: 3rem; - margin-right: 3rem; -} - -.mx-24 { - margin-left: 6rem; - margin-right: 6rem; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.my-1 { - margin-top: 0.25rem; - margin-bottom: 0.25rem; -} - -.my-4 { - margin-top: 1rem; - margin-bottom: 1rem; -} - -.my-auto { - margin-top: auto; - margin-bottom: auto; -} - -.-mr-1 { - margin-right: -0.25rem; -} - -.-mr-2 { - margin-right: -0.5rem; -} - -.mb-1 { - margin-bottom: 0.25rem; -} - -.mb-10 { - margin-bottom: 2.5rem; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.mb-3 { - margin-bottom: 0.75rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-10 { - margin-left: 2.5rem; -} - -.ml-12 { - margin-left: 3rem; -} - -.ml-2 { - margin-left: 0.5rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-4 { - margin-left: 1rem; -} - -.mr-1 { - margin-right: 0.25rem; -} - -.mr-4 { - margin-right: 1rem; -} - -.mr-5 { - margin-right: 1.25rem; -} - -.mt-0 { - margin-top: 0px; -} - -.mt-0\.5 { - margin-top: 0.125rem; -} - -.mt-1 { - margin-top: 0.25rem; -} - -.mt-10 { - margin-top: 2.5rem; -} - -.mt-11 { - margin-top: 2.75rem; -} - -.mt-14 { - margin-top: 3.5rem; -} - -.mt-16 { - margin-top: 4rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - -.mt-3 { - margin-top: 0.75rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mt-5 { - margin-top: 1.25rem; -} - -.block { - display: block; -} - -.flex { - display: flex; -} - -.inline-flex { - display: inline-flex; -} - -.table { - display: table; -} - -.grid { - display: grid; -} - -.hidden { - display: none; -} - -.h-1 { - height: 0.25rem; -} - -.h-1\.5 { - height: 0.375rem; -} - -.h-16 { - height: 4rem; -} - -.h-3 { - height: 0.75rem; -} - -.h-4 { - height: 1rem; -} - -.h-5 { - height: 1.25rem; -} - -.h-6 { - height: 1.5rem; -} - -.h-full { - height: 100%; -} - -.h-max { - height: -moz-max-content; - height: max-content; -} - -.h-screen { - height: 100vh; -} - -.min-h-\[6rem\] { - min-height: 6rem; -} - -.min-h-full { - min-height: 100%; -} - -.min-h-screen { - min-height: 100vh; -} - -.w-1\/4 { - width: 25%; -} - -.w-14 { - width: 3.5rem; -} - -.w-3 { - width: 0.75rem; -} - -.w-4 { - width: 1rem; -} - -.w-5 { - width: 1.25rem; -} - -.w-5\/6 { - width: 83.333333%; -} - -.w-56 { - width: 14rem; -} - -.w-6 { - width: 1.5rem; -} - -.w-80 { - width: 20rem; -} - -.w-\[40rem\] { - width: 40rem; -} - -.w-full { - width: 100%; -} - -.max-w-3xl { - max-width: 48rem; -} - -.max-w-none { - max-width: none; -} - -.max-w-sm { - max-width: 24rem; -} - -.flex-1 { - flex: 1 1 0%; -} - -.flex-none { - flex: none; -} - -.flex-shrink-0 { - flex-shrink: 0; -} - -.table-auto { - table-layout: auto; -} - -.origin-top-right { - transform-origin: top right; -} - -.-translate-y-3 { - --tw-translate-y: -0.75rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.translate-y-4 { - --tw-translate-y: 1rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.animate-spin { - animation: spin 1s linear infinite; -} - -.cursor-pointer { - cursor: pointer; -} - -.select-none { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.resize-y { - resize: vertical; -} - -.grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); -} - -.grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); -} - -.flex-row { - flex-direction: row; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-items-center { - justify-items: center; -} - -.gap-1 { - gap: 0.25rem; -} - -.gap-1\.5 { - gap: 0.375rem; -} - -.gap-3 { - gap: 0.75rem; -} - -.gap-4 { - gap: 1rem; -} - -.gap-6 { - gap: 1.5rem; -} - -.-space-x-px > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(-1px * var(--tw-space-x-reverse)); - margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-1 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.25rem * var(--tw-space-x-reverse)); - margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-y-8 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(2rem * var(--tw-space-y-reverse)); -} - -.divide-y > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); -} - -.divide-gray-100 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(243 244 246 / var(--tw-divide-opacity)); -} - -.divide-zinc-100 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(244 244 245 / var(--tw-divide-opacity)); -} - -.overflow-auto { - overflow: auto; -} - -.overflow-hidden { - overflow: hidden; -} - -.overflow-scroll { - overflow: scroll; -} - -.overflow-y-auto { - overflow-y: auto; -} - -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.whitespace-nowrap { - white-space: nowrap; -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-2xl { - border-radius: 1rem; -} - -.rounded-full { - border-radius: 9999px; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.rounded-md { - border-radius: 0.375rem; -} - -.rounded-l-md { - border-top-left-radius: 0.375rem; - border-bottom-left-radius: 0.375rem; -} - -.rounded-r-md { - border-top-right-radius: 0.375rem; - border-bottom-right-radius: 0.375rem; -} - -.rounded-t { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.rounded-t-lg { - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; -} - -.border { - border-width: 1px; -} - -.border-b-2 { - border-bottom-width: 2px; -} - -.border-t { - border-top-width: 1px; -} - -.border-gray-200 { - --tw-border-opacity: 1; - border-color: rgb(229 231 235 / var(--tw-border-opacity)); -} - -.border-gray-300 { - --tw-border-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-border-opacity)); -} - -.border-gray-600 { - --tw-border-opacity: 1; - border-color: rgb(75 85 99 / var(--tw-border-opacity)); -} - -.border-rose-400 { - --tw-border-opacity: 1; - border-color: rgb(251 113 133 / var(--tw-border-opacity)); -} - -.border-transparent { - border-color: transparent; -} - -.border-zinc-200 { - --tw-border-opacity: 1; - border-color: rgb(228 228 231 / var(--tw-border-opacity)); -} - -.border-zinc-300 { - --tw-border-opacity: 1; - border-color: rgb(212 212 216 / var(--tw-border-opacity)); -} - -.bg-emerald-50 { - --tw-bg-opacity: 1; - background-color: rgb(236 253 245 / var(--tw-bg-opacity)); -} - -.bg-gray-100 { - --tw-bg-opacity: 1; - background-color: rgb(243 244 246 / var(--tw-bg-opacity)); -} - -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); -} - -.bg-gray-300 { - --tw-bg-opacity: 1; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)); -} - -.bg-gray-50 { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity)); -} - -.bg-gray-600 { - --tw-bg-opacity: 1; - background-color: rgb(75 85 99 / var(--tw-bg-opacity)); -} - -.bg-gray-700 { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.bg-gray-800 { - --tw-bg-opacity: 1; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); -} - -.bg-indigo-500 { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity)); -} - -.bg-indigo-600 { - --tw-bg-opacity: 1; - background-color: rgb(79 70 229 / var(--tw-bg-opacity)); -} - -.bg-rose-50 { - --tw-bg-opacity: 1; - background-color: rgb(255 241 242 / var(--tw-bg-opacity)); -} - -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.bg-zinc-50\/90 { - background-color: rgb(250 250 250 / 0.9); -} - -.bg-zinc-900 { - --tw-bg-opacity: 1; - background-color: rgb(24 24 27 / var(--tw-bg-opacity)); -} - -.fill-cyan-900 { - fill: #164e63; -} - -.fill-rose-900 { - fill: #881337; -} - -.p-0 { - padding: 0px; -} - -.p-14 { - padding: 3.5rem; -} - -.p-2 { - padding: 0.5rem; -} - -.p-3 { - padding: 0.75rem; -} - -.p-4 { - padding: 1rem; -} - -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.px-8 { - padding-left: 2rem; - padding-right: 2rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-2\.5 { - padding-top: 0.625rem; - padding-bottom: 0.625rem; -} - -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.py-5 { - padding-top: 1.25rem; - padding-bottom: 1.25rem; -} - -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} - -.pb-20 { - padding-bottom: 5rem; -} - -.pb-3 { - padding-bottom: 0.75rem; -} - -.pb-4 { - padding-bottom: 1rem; -} - -.pl-2 { - padding-left: 0.5rem; -} - -.pl-4 { - padding-left: 1rem; -} - -.pr-2 { - padding-right: 0.5rem; -} - -.pr-4 { - padding-right: 1rem; -} - -.pr-6 { - padding-right: 1.5rem; -} - -.pr-9 { - padding-right: 2.25rem; -} - -.pt-2 { - padding-top: 0.5rem; -} - -.pt-4 { - padding-top: 1rem; -} - -.text-left { - text-align: left; -} - -.text-right { - text-align: right; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - -.font-medium { - font-weight: 500; -} - -.font-normal { - font-weight: 400; -} - -.font-semibold { - font-weight: 600; -} - -.capitalize { - text-transform: capitalize; -} - -.italic { - font-style: italic; -} - -.leading-5 { - line-height: 1.25rem; -} - -.leading-6 { - line-height: 1.5rem; -} - -.leading-8 { - line-height: 2rem; -} - -.text-black { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); -} - -.text-emerald-800 { - --tw-text-opacity: 1; - color: rgb(6 95 70 / var(--tw-text-opacity)); -} - -.text-gray-300 { - --tw-text-opacity: 1; - color: rgb(209 213 219 / var(--tw-text-opacity)); -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); -} - -.text-gray-600 { - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity)); -} - -.text-gray-700 { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity)); -} - -.text-gray-900 { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity)); -} - -.text-red-500 { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); -} - -.text-rose-600 { - --tw-text-opacity: 1; - color: rgb(225 29 72 / var(--tw-text-opacity)); -} - -.text-rose-900 { - --tw-text-opacity: 1; - color: rgb(136 19 55 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.text-zinc-500 { - --tw-text-opacity: 1; - color: rgb(113 113 122 / var(--tw-text-opacity)); -} - -.text-zinc-600 { - --tw-text-opacity: 1; - color: rgb(82 82 91 / var(--tw-text-opacity)); -} - -.text-zinc-700 { - --tw-text-opacity: 1; - color: rgb(63 63 70 / var(--tw-text-opacity)); -} - -.text-zinc-800 { - --tw-text-opacity: 1; - color: rgb(39 39 42 / var(--tw-text-opacity)); -} - -.text-zinc-900 { - --tw-text-opacity: 1; - color: rgb(24 24 27 / var(--tw-text-opacity)); -} - -.opacity-0 { - opacity: 0; -} - -.opacity-100 { - opacity: 1; -} - -.opacity-20 { - opacity: 0.2; -} - -.opacity-40 { - opacity: 0.4; -} - -.shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-sm { - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-zinc-700\/10 { - --tw-shadow-color: rgb(63 63 70 / 0.1); - --tw-shadow: var(--tw-shadow-colored); -} - -.outline { - outline-style: solid; -} - -.ring-1 { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.ring-inset { - --tw-ring-inset: inset; -} - -.ring-black { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity)); -} - -.ring-emerald-500 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(16 185 129 / var(--tw-ring-opacity)); -} - -.ring-gray-500\/10 { - --tw-ring-color: rgb(107 114 128 / 0.1); -} - -.ring-rose-500 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(244 63 94 / var(--tw-ring-opacity)); -} - -.ring-zinc-700\/10 { - --tw-ring-color: rgb(63 63 70 / 0.1); -} - -.ring-opacity-5 { - --tw-ring-opacity: 0.05; -} - -.blur { - --tw-blur: blur(8px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-150 { - transition-duration: 150ms; -} - -.duration-200 { - transition-duration: 200ms; -} - -.duration-300 { - transition-duration: 300ms; -} - -.ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); -} - -.ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); -} - -.file\:me-4::file-selector-button { - -webkit-margin-end: 1rem; - margin-inline-end: 1rem; -} - -.file\:cursor-pointer::file-selector-button { - cursor: pointer; -} - -.file\:border-0::file-selector-button { - border-width: 0px; -} - -.file\:bg-gray-200::file-selector-button { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); -} - -.file\:px-4::file-selector-button { - padding-left: 1rem; - padding-right: 1rem; -} - -.file\:py-2::file-selector-button { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.file\:py-2\.5::file-selector-button { - padding-top: 0.625rem; - padding-bottom: 0.625rem; -} - -.file\:text-sm::file-selector-button { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.target\:border-zinc-400:target { - --tw-border-opacity: 1; - border-color: rgb(161 161 170 / var(--tw-border-opacity)); -} - -.hover\:cursor-pointer:hover { - cursor: pointer; -} - -.hover\:bg-gray-100:hover { - --tw-bg-opacity: 1; - background-color: rgb(243 244 246 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-300:hover { - --tw-bg-opacity: 1; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-400:hover { - --tw-bg-opacity: 1; - background-color: rgb(156 163 175 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-50:hover { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.hover\:bg-gray-900:hover { - --tw-bg-opacity: 1; - background-color: rgb(17 24 39 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-400:hover { - --tw-bg-opacity: 1; - background-color: rgb(129 140 248 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(67 56 202 / var(--tw-bg-opacity)); -} - -.hover\:bg-zinc-50:hover { - --tw-bg-opacity: 1; - background-color: rgb(250 250 250 / var(--tw-bg-opacity)); -} - -.hover\:bg-zinc-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(63 63 70 / var(--tw-bg-opacity)); -} - -.hover\:text-blue-400:hover { - --tw-text-opacity: 1; - color: rgb(96 165 250 / var(--tw-text-opacity)); -} - -.hover\:text-gray-100:hover { - --tw-text-opacity: 1; - color: rgb(243 244 246 / var(--tw-text-opacity)); -} - -.hover\:text-gray-500:hover { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); -} - -.hover\:text-gray-600:hover { - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity)); -} - -.hover\:text-gray-900:hover { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity)); -} - -.hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.hover\:text-zinc-700:hover { - --tw-text-opacity: 1; - color: rgb(63 63 70 / var(--tw-text-opacity)); -} - -.hover\:underline:hover { - text-decoration-line: underline; -} - -.hover\:opacity-40:hover { - opacity: 0.4; -} - -.focus\:border-indigo-500:focus { - --tw-border-opacity: 1; - border-color: rgb(99 102 241 / var(--tw-border-opacity)); -} - -.focus\:border-rose-400:focus { - --tw-border-opacity: 1; - border-color: rgb(251 113 133 / var(--tw-border-opacity)); -} - -.focus\:border-zinc-400:focus { - --tw-border-opacity: 1; - border-color: rgb(161 161 170 / var(--tw-border-opacity)); -} - -.focus\:bg-gray-700:focus { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.focus\:text-white:focus { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.focus\:ring-0:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.focus\:ring-indigo-500:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); -} - -.focus\:ring-offset-2:focus { - --tw-ring-offset-width: 2px; -} - -.focus\:ring-offset-gray-100:focus { - --tw-ring-offset-color: #f3f4f6; -} - -.active\:border-zinc-400:active { - --tw-border-opacity: 1; - border-color: rgb(161 161 170 / var(--tw-border-opacity)); -} - -.active\:text-white\/80:active { - color: rgb(255 255 255 / 0.8); -} - -.group:hover .group-hover\:bg-zinc-50 { - --tw-bg-opacity: 1; - background-color: rgb(250 250 250 / var(--tw-bg-opacity)); -} - -.group:hover .group-hover\:opacity-70 { - opacity: 0.7; -} - -.phx-submit-loading.phx-submit-loading\:opacity-75 { - opacity: 0.75; -} - -.phx-submit-loading .phx-submit-loading\:opacity-75 { - opacity: 0.75; -} - -@media (min-width: 640px) { - .sm\:col-span-2 { - grid-column: span 2 / span 2; - } - - .sm\:col-span-3 { - grid-column: span 3 / span 3; - } - - .sm\:col-span-full { - grid-column: 1 / -1; - } - - .sm\:mt-0 { - margin-top: 0px; - } - - .sm\:block { - display: block; - } - - .sm\:flex { - display: flex; - } - - .sm\:hidden { - display: none; - } - - .sm\:w-96 { - width: 24rem; - } - - .sm\:w-full { - width: 100%; - } - - .sm\:flex-1 { - flex: 1 1 0%; - } - - .sm\:translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:scale-95 { - --tw-scale-x: .95; - --tw-scale-y: .95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:items-center { - align-items: center; - } - - .sm\:justify-between { - justify-content: space-between; - } - - .sm\:gap-8 { - gap: 2rem; - } - - .sm\:overflow-visible { - overflow: visible; - } - - .sm\:rounded-md { - border-radius: 0.375rem; - } - - .sm\:rounded-l-xl { - border-top-left-radius: 0.75rem; - border-bottom-left-radius: 0.75rem; - } - - .sm\:rounded-r-xl { - border-top-right-radius: 0.75rem; - border-bottom-right-radius: 0.75rem; - } - - .sm\:p-6 { - padding: 1.5rem; - } - - .sm\:px-0 { - padding-left: 0px; - padding-right: 0px; - } - - .sm\:px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; - } - - .sm\:px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; - } - - .sm\:text-sm { - font-size: 0.875rem; - line-height: 1.25rem; - } - - .sm\:leading-6 { - line-height: 1.5rem; - } -} - -@media (min-width: 768px) { - .md\:col-span-2 { - grid-column: span 2 / span 2; - } - - .md\:mx-16 { - margin-left: 4rem; - margin-right: 4rem; - } - - .md\:mx-4 { - margin-left: 1rem; - margin-right: 1rem; - } - - .md\:mt-0 { - margin-top: 0px; - } - - .md\:mt-10 { - margin-top: 2.5rem; - } - - .md\:block { - display: block; - } - - .md\:grid { - display: grid; - } - - .md\:hidden { - display: none; - } - - .md\:grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .md\:gap-6 { - gap: 1.5rem; - } - - .md\:pt-10 { - padding-top: 2.5rem; - } -} - -@media (min-width: 1024px) { - .lg\:px-8 { - padding-left: 2rem; - padding-right: 2rem; - } - - .lg\:py-8 { - padding-top: 2rem; - padding-bottom: 2rem; - } -} - +/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:#0000}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:#0000}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:#0000}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.hero-arrow-left-solid{--hero-arrow-left-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-arrow-left-solid);mask:var(--hero-arrow-left-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-arrow-left-solid,.hero-arrow-path{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-arrow-path{--hero-arrow-path:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-arrow-path);mask:var(--hero-arrow-path);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-check{--hero-check:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-check);mask:var(--hero-check);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-check,.hero-chevron-down{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-chevron-down{--hero-chevron-down:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-chevron-down);mask:var(--hero-chevron-down);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-chevron-up{--hero-chevron-up:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-chevron-up);mask:var(--hero-chevron-up);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-chevron-up,.hero-chevron-up-down{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-chevron-up-down{--hero-chevron-up-down:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-chevron-up-down);mask:var(--hero-chevron-up-down);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-exclamation-circle-mini{--hero-exclamation-circle-mini:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-exclamation-circle-mini);mask:var(--hero-exclamation-circle-mini);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-exclamation-circle-mini,.hero-eye-slash-solid{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-eye-slash-solid{--hero-eye-slash-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-eye-slash-solid);mask:var(--hero-eye-slash-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-eye-solid{--hero-eye-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-eye-solid);mask:var(--hero-eye-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-eye-solid,.hero-information-circle-mini{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-information-circle-mini{--hero-information-circle-mini:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-information-circle-mini);mask:var(--hero-information-circle-mini);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-information-circle-solid{--hero-information-circle-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-information-circle-solid);mask:var(--hero-information-circle-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-information-circle-solid,.hero-key{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-key{--hero-key:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-key);mask:var(--hero-key);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-key-solid{--hero-key-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-key-solid);mask:var(--hero-key-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-key-solid,.hero-magnifying-glass-circle{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-magnifying-glass-circle{--hero-magnifying-glass-circle:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-magnifying-glass-circle);mask:var(--hero-magnifying-glass-circle);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-minus{--hero-minus:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-minus);mask:var(--hero-minus);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-minus,.hero-pencil-solid{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-pencil-solid{--hero-pencil-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-pencil-solid);mask:var(--hero-pencil-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-plus{--hero-plus:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-plus);mask:var(--hero-plus);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-plus,.hero-x-circle{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-x-circle{--hero-x-circle:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-x-circle);mask:var(--hero-x-circle);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-x-circle-solid{--hero-x-circle-solid:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-x-circle-solid);mask:var(--hero-x-circle-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-x-circle-solid,.hero-x-mark{background-color:currentColor;display:inline-block;height:1.25rem;vertical-align:middle;width:1.25rem}.hero-x-mark{--hero-x-mark:url('data:image/svg+xml;utf8,');-webkit-mask:var(--hero-x-mark);mask:var(--hero-x-mark);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.hero-x-mark-solid{--hero-x-mark-solid:url('data:image/svg+xml;utf8,');background-color:currentColor;display:inline-block;height:1.25rem;-webkit-mask:var(--hero-x-mark-solid);mask:var(--hero-x-mark-solid);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;vertical-align:middle;width:1.25rem}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.-inset-y-px{bottom:-1px;top:-1px}.inset-y-0{bottom:0;top:0}.-left-4{left:-1rem}.-right-4{right:-1rem}.left-0{left:0}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-1{top:.25rem}.top-2{top:.5rem}.top-4{top:1rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-6{grid-column:span 6/span 6}.col-span-full{grid-column:1/-1}.float-right{float:right}.float-left{float:left}.-m-3{margin:-.75rem}.m-2{margin:.5rem}.m-5{margin:1.25rem}.-my-4{margin-bottom:-1rem;margin-top:-1rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-12{margin-left:3rem;margin-right:3rem}.mx-24{margin-left:6rem;margin-right:6rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-bottom:.25rem;margin-top:.25rem}.my-4{margin-bottom:1rem;margin-top:1rem}.my-auto{margin-bottom:auto;margin-top:auto}.-mb-3{margin-bottom:-.75rem}.-mb-4{margin-bottom:-1rem}.-mb-6{margin-bottom:-1.5rem}.-mr-1{margin-right:-.25rem}.-mr-2{margin-right:-.5rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-10{margin-left:2.5rem}.ml-12{margin-left:3rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-5{margin-right:1.25rem}.mr-6{margin-right:1.5rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-11{margin-top:2.75rem}.mt-14{margin-top:3.5rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-16{height:4rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-\[34px\]{height:34px}.h-\[36px\]{height:36px}.h-\[42px\]{height:42px}.h-\[48px\]{height:48px}.h-full{height:100%}.h-max{height:-moz-max-content;height:max-content}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.min-h-\[6rem\]{min-height:6rem}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-14{width:3.5rem}.w-16{width:4rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\/6{width:83.333333%}.w-56{width:14rem}.w-6{width:1.5rem}.w-80{width:20rem}.w-\[160px\]{width:160px}.w-\[40rem\]{width:40rem}.w-full{width:100%}.max-w-3xl{max-width:48rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.flex-shrink-0{flex-shrink:0}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.-translate-y-3{--tw-translate-y:-0.75rem}.-translate-y-3,.translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.translate-y-4{--tw-translate-y:1rem}.transform,.translate-y-4{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-y{resize:vertical}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(0px*(1 - var(--tw-space-x-reverse)));margin-right:calc(0px*var(--tw-space-x-reverse))}.space-x-0\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.125rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.125rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-blue-500\/20>:not([hidden])~:not([hidden]){border-color:#3b82f633}.divide-cyan-400\/30>:not([hidden])~:not([hidden]){border-color:#22d3ee4d}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}.divide-pink-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(252 231 243/var(--tw-divide-opacity))}.divide-zinc-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(244 244 245/var(--tw-divide-opacity))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity))}.border-blue-500\/20{border-color:#3b82f633}.border-blue-500\/30{border-color:#3b82f64d}.border-blue-500\/40{border-color:#3b82f666}.border-blue-600{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity))}.border-cyan-400{--tw-border-opacity:1;border-color:rgb(34 211 238/var(--tw-border-opacity))}.border-cyan-400\/50{border-color:#22d3ee80}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.border-green-400{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity))}.border-green-500\/30{border-color:#22c55e4d}.border-green-500\/50{border-color:#22c55e80}.border-pink-200{--tw-border-opacity:1;border-color:rgb(251 207 232/var(--tw-border-opacity))}.border-pink-300{--tw-border-opacity:1;border-color:rgb(249 168 212/var(--tw-border-opacity))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity))}.border-purple-500{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity))}.border-purple-600{--tw-border-opacity:1;border-color:rgb(147 51 234/var(--tw-border-opacity))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-red-500\/50{border-color:#ef444480}.border-red-600\/50{border-color:#dc262680}.border-rose-400{--tw-border-opacity:1;border-color:rgb(251 113 133/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity))}.border-yellow-300{--tw-border-opacity:1;border-color:rgb(253 224 71/var(--tw-border-opacity))}.border-yellow-400{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity))}.border-yellow-400\/50{border-color:#facc1580}.border-yellow-600\/50{border-color:#ca8a0480}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity))}.border-zinc-300{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-800\/50{background-color:#1f293780}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-pink-100{--tw-bg-opacity:1;background-color:rgb(252 231 243/var(--tw-bg-opacity))}.bg-pink-50{--tw-bg-opacity:1;background-color:rgb(253 242 248/var(--tw-bg-opacity))}.bg-pink-50\/50{background-color:#fdf2f880}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity))}.bg-purple-400{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity))}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity))}.bg-red-900\/50{background-color:#7f1d1d80}.bg-red-950\/50{background-color:#450a0a80}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity))}.bg-slate-900\/50{background-color:#0f172a80}.bg-slate-900\/60{background-color:#0f172a99}.bg-slate-900\/95{background-color:#0f172af2}.bg-slate-950{--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity))}.bg-slate-950\/80{background-color:#020617cc}.bg-transparent{background-color:initial}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/80{background-color:#fffc}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-yellow-900{--tw-bg-opacity:1;background-color:rgb(113 63 18/var(--tw-bg-opacity))}.bg-yellow-900\/30{background-color:#713f124d}.bg-yellow-950\/30{background-color:#4220064d}.bg-zinc-50\/90{background-color:#fafafae6}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-400{--tw-gradient-from:#60a5fa var(--tw-gradient-from-position);--tw-gradient-to:#60a5fa00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-50{--tw-gradient-from:#eff6ff var(--tw-gradient-from-position);--tw-gradient-to:#eff6ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-900\/60{--tw-gradient-from:#1e3a8a99 var(--tw-gradient-from-position);--tw-gradient-to:#1e3a8a00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-400{--tw-gradient-from:#22d3ee var(--tw-gradient-from-position);--tw-gradient-to:#22d3ee00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-600{--tw-gradient-from:#0891b2 var(--tw-gradient-from-position);--tw-gradient-to:#0891b200 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-50{--tw-gradient-from:#f9fafb var(--tw-gradient-from-position);--tw-gradient-to:#f9fafb00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-800{--tw-gradient-from:#1f2937 var(--tw-gradient-from-position);--tw-gradient-to:#1f293700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-400{--tw-gradient-from:#4ade80 var(--tw-gradient-from-position);--tw-gradient-to:#4ade8000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-50{--tw-gradient-from:#f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to:#f0fdf400 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-pink-50{--tw-gradient-from:#fdf2f8 var(--tw-gradient-from-position);--tw-gradient-to:#fdf2f800 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-100{--tw-gradient-from:#f3e8ff var(--tw-gradient-from-position);--tw-gradient-to:#f3e8ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500{--tw-gradient-from:#a855f7 var(--tw-gradient-from-position);--tw-gradient-to:#a855f700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-900{--tw-gradient-from:#581c87 var(--tw-gradient-from-position);--tw-gradient-to:#581c8700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-900\/30{--tw-gradient-from:#581c874d var(--tw-gradient-from-position);--tw-gradient-to:#581c8700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-900\/80{--tw-gradient-from:#0f172acc var(--tw-gradient-from-position);--tw-gradient-to:#0f172a00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-purple-50{--tw-gradient-to:#faf5ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#faf5ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-blue-400{--tw-gradient-to:#60a5fa var(--tw-gradient-to-position)}.to-blue-50{--tw-gradient-to:#eff6ff var(--tw-gradient-to-position)}.to-blue-500\/5{--tw-gradient-to:#3b82f60d var(--tw-gradient-to-position)}.to-blue-900\/20{--tw-gradient-to:#1e3a8a33 var(--tw-gradient-to-position)}.to-blue-900\/30{--tw-gradient-to:#1e3a8a4d var(--tw-gradient-to-position)}.to-gray-900{--tw-gradient-to:#111827 var(--tw-gradient-to-position)}.to-green-400{--tw-gradient-to:#4ade80 var(--tw-gradient-to-position)}.to-green-50{--tw-gradient-to:#f0fdf4 var(--tw-gradient-to-position)}.to-green-900\/60{--tw-gradient-to:#14532d99 var(--tw-gradient-to-position)}.to-indigo-50{--tw-gradient-to:#eef2ff var(--tw-gradient-to-position)}.to-pink-100{--tw-gradient-to:#fce7f3 var(--tw-gradient-to-position)}.to-pink-50{--tw-gradient-to:#fdf2f8 var(--tw-gradient-to-position)}.to-pink-500{--tw-gradient-to:#ec4899 var(--tw-gradient-to-position)}.to-pink-900{--tw-gradient-to:#831843 var(--tw-gradient-to-position)}.to-purple-50{--tw-gradient-to:#faf5ff var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to:#9333ea var(--tw-gradient-to-position)}.to-white{--tw-gradient-to:#fff var(--tw-gradient-to-position)}.fill-blue-600{fill:#2563eb}.fill-cyan-900{fill:#164e63}.fill-rose-900{fill:#881337}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-14{padding:3.5rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-bottom:0;padding-top:0}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-12{padding-bottom:3rem;padding-top:3rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.py-8{padding-bottom:2rem;padding-top:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-2{padding-left:.5rem}.pl-4{padding-left:1rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pr-6{padding-right:1.5rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-baseline{vertical-align:initial}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-8{line-height:2rem}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity))}.text-blue-300\/70{color:#93c5fdb3}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.text-cyan-100{--tw-text-opacity:1;color:rgb(207 250 254/var(--tw-text-opacity))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-100{--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.text-pink-400{--tw-text-opacity:1;color:rgb(244 114 182/var(--tw-text-opacity))}.text-pink-500{--tw-text-opacity:1;color:rgb(236 72 153/var(--tw-text-opacity))}.text-pink-600{--tw-text-opacity:1;color:rgb(219 39 119/var(--tw-text-opacity))}.text-pink-800{--tw-text-opacity:1;color:rgb(157 23 77/var(--tw-text-opacity))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.text-purple-800{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity))}.text-red-100{--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-rose-600{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity))}.text-rose-900{--tw-text-opacity:1;color:rgb(136 19 55/var(--tw-text-opacity))}.text-slate-200{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-100{--tw-text-opacity:1;color:rgb(254 249 195/var(--tw-text-opacity))}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}.placeholder-gray-400::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity))}.placeholder-purple-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(192 132 252/var(--tw-placeholder-opacity))}.placeholder-purple-400::placeholder{--tw-placeholder-opacity:1;color:rgb(192 132 252/var(--tw-placeholder-opacity))}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(100 116 139/var(--tw-placeholder-opacity))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity:1;color:rgb(100 116 139/var(--tw-placeholder-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-blue-500\/10{--tw-shadow-color:#3b82f61a;--tw-shadow:var(--tw-shadow-colored)}.shadow-blue-500\/20{--tw-shadow-color:#3b82f633;--tw-shadow:var(--tw-shadow-colored)}.shadow-cyan-400\/20{--tw-shadow-color:#22d3ee33;--tw-shadow:var(--tw-shadow-colored)}.shadow-green-400\/20{--tw-shadow-color:#4ade8033;--tw-shadow:var(--tw-shadow-colored)}.shadow-green-500\/10{--tw-shadow-color:#22c55e1a;--tw-shadow:var(--tw-shadow-colored)}.shadow-red-400\/30{--tw-shadow-color:#f871714d;--tw-shadow:var(--tw-shadow-colored)}.shadow-red-500\/20{--tw-shadow-color:#ef444433;--tw-shadow:var(--tw-shadow-colored)}.shadow-yellow-400\/20{--tw-shadow-color:#facc1533;--tw-shadow:var(--tw-shadow-colored)}.shadow-yellow-400\/30{--tw-shadow-color:#facc154d;--tw-shadow:var(--tw-shadow-colored)}.shadow-zinc-700\/10{--tw-shadow-color:#3f3f461a;--tw-shadow:var(--tw-shadow-colored)}.outline{outline-style:solid}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-emerald-500{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity))}.ring-gray-500\/10{--tw-ring-color:#6b72801a}.ring-rose-500{--tw-ring-opacity:1;--tw-ring-color:rgb(244 63 94/var(--tw-ring-opacity))}.ring-zinc-700\/10{--tw-ring-color:#3f3f461a}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.drop-shadow-lg{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px #0000000a) drop-shadow(0 4px 3px #0000001a)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d)}.drop-shadow-sm,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[-moz-appearance\:textfield\]{-moz-appearance:textfield}.file\:me-4::file-selector-button{-webkit-margin-end:1rem;margin-inline-end:1rem}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:border-0::file-selector-button{border-width:0}.file\:bg-gray-200::file-selector-button{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.file\:px-4::file-selector-button{padding-left:1rem;padding-right:1rem}.file\:py-2::file-selector-button{padding-bottom:.5rem;padding-top:.5rem}.file\:py-2\.5::file-selector-button{padding-bottom:.625rem;padding-top:.625rem}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.last\:border-b-0:last-child{border-bottom-width:0}.target\:border-zinc-400:target{--tw-border-opacity:1;border-color:rgb(161 161 170/var(--tw-border-opacity))}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:border-gray-400:hover{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.hover\:border-gray-500:hover{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.hover\:border-green-400\/60:hover{border-color:#4ade8099}.hover\:border-green-400\/70:hover{border-color:#4ade80b3}.hover\:border-purple-300:hover{--tw-border-opacity:1;border-color:rgb(216 180 254/var(--tw-border-opacity))}.hover\:border-yellow-400:hover{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity))}.hover\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.hover\:bg-blue-50\/30:hover{background-color:#eff6ff4d}.hover\:bg-blue-800:hover{--tw-bg-opacity:1;background-color:rgb(30 64 175/var(--tw-bg-opacity))}.hover\:bg-blue-950\/60:hover{background-color:#17255499}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.hover\:bg-gray-800\/50:hover{background-color:#1f293780}.hover\:bg-gray-900:hover{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.hover\:bg-indigo-400:hover{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}.hover\:bg-pink-200:hover{--tw-bg-opacity:1;background-color:rgb(251 207 232/var(--tw-bg-opacity))}.hover\:bg-purple-50:hover{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.hover\:bg-purple-900\/50:hover{background-color:#581c8780}.hover\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.hover\:bg-zinc-700:hover{--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity))}.hover\:bg-gradient-to-r:hover{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.hover\:from-blue-50\/50:hover{--tw-gradient-from:#eff6ff80 var(--tw-gradient-from-position);--tw-gradient-to:#eff6ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-blue-900\/60:hover{--tw-gradient-from:#1e3a8a99 var(--tw-gradient-from-position);--tw-gradient-to:#1e3a8a00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-blue-950\/60:hover{--tw-gradient-from:#17255499 var(--tw-gradient-from-position);--tw-gradient-to:#17255400 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-purple-900\/50:hover{--tw-gradient-from:#581c8780 var(--tw-gradient-from-position);--tw-gradient-to:#581c8700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-green-900\/60:hover{--tw-gradient-to:#14532d99 var(--tw-gradient-to-position)}.hover\:to-green-950\/60:hover{--tw-gradient-to:#052e1699 var(--tw-gradient-to-position)}.hover\:to-pink-900\/50:hover{--tw-gradient-to:#83184380 var(--tw-gradient-to-position)}.hover\:to-purple-50\/50:hover{--tw-gradient-to:#faf5ff80 var(--tw-gradient-to-position)}.hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.hover\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity))}.hover\:text-pink-700:hover{--tw-text-opacity:1;color:rgb(190 24 93/var(--tw-text-opacity))}.hover\:text-purple-300:hover{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity))}.hover\:text-red-400:hover{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.hover\:text-red-500:hover{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:text-yellow-300:hover{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.hover\:text-zinc-700:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-40:hover{opacity:.4}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-lg:hover,.hover\:shadow-md:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.hover\:shadow-blue-500\/10:hover{--tw-shadow-color:#3b82f61a;--tw-shadow:var(--tw-shadow-colored)}.hover\:shadow-blue-500\/20:hover{--tw-shadow-color:#3b82f633;--tw-shadow:var(--tw-shadow-colored)}.hover\:shadow-green-500\/20:hover{--tw-shadow-color:#22c55e33;--tw-shadow:var(--tw-shadow-colored)}.hover\:drop-shadow-sm:hover{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.focus\:border-green-400\/60:focus{border-color:#4ade8099}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.focus\:border-purple-400:focus{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity))}.focus\:border-purple-500:focus{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity))}.focus\:border-rose-400:focus{--tw-border-opacity:1;border-color:rgb(251 113 133/var(--tw-border-opacity))}.focus\:border-zinc-400:focus{--tw-border-opacity:1;border-color:rgb(161 161 170/var(--tw-border-opacity))}.focus\:bg-gray-700:focus{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.focus\:text-white:focus{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:shadow-lg:focus{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:shadow-green-400\/20:focus{--tw-shadow-color:#4ade8033;--tw-shadow:var(--tw-shadow-colored)}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus,.focus\:ring-1:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus,.focus\:ring-4:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-4:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-blue-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(147 197 253/var(--tw-ring-opacity))}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.focus\:ring-green-400\/50:focus{--tw-ring-color:#4ade8080}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-pink-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(244 114 182/var(--tw-ring-opacity))}.focus\:ring-purple-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(216 180 254/var(--tw-ring-opacity))}.focus\:ring-purple-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(168 85 247/var(--tw-ring-opacity))}.focus\:ring-yellow-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 204 21/var(--tw-ring-opacity))}.focus\:ring-yellow-400\/50:focus{--tw-ring-color:#facc1580}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-gray-100:focus{--tw-ring-offset-color:#f3f4f6}.active\:border-zinc-400:active{--tw-border-opacity:1;border-color:rgb(161 161 170/var(--tw-border-opacity))}.active\:text-white\/80:active{color:#fffc}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.group:hover .group-hover\:opacity-70{opacity:.7}.phx-submit-loading .phx-submit-loading\:opacity-75,.phx-submit-loading.phx-submit-loading\:opacity-75{opacity:.75}@media (prefers-color-scheme:dark){.dark\:border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.dark\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.dark\:border-red-800{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity))}.dark\:border-yellow-800{--tw-border-opacity:1;border-color:rgb(133 77 14/var(--tw-border-opacity))}.dark\:bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.dark\:bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.dark\:bg-blue-900{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity))}.dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.dark\:text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.dark\:text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.dark\:text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.dark\:text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.dark\:text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.dark\:placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}.dark\:placeholder-gray-400::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}.dark\:ring-offset-gray-800{--tw-ring-offset-color:#1f2937}.dark\:hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.dark\:hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark\:hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.dark\:focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.dark\:focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.dark\:focus\:ring-blue-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity))}.dark\:focus\:ring-blue-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(30 64 175/var(--tw-ring-opacity))}}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-full{grid-column:1/-1}.sm\:mt-0{margin-top:0}.sm\:block{display:block}.sm\:w-96{width:24rem}.sm\:w-full{width:100%}.sm\:translate-y-0{--tw-translate-y:0px}.sm\:scale-100,.sm\:translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.sm\:scale-95{--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:gap-8{gap:2rem}.sm\:overflow-visible{overflow:visible}.sm\:rounded-lg{border-radius:.5rem}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-l-xl{border-bottom-left-radius:.75rem;border-top-left-radius:.75rem}.sm\:rounded-r-xl{border-bottom-right-radius:.75rem;border-top-right-radius:.75rem}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-3{padding-left:.75rem;padding-right:.75rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:leading-6{line-height:1.5rem}}@media (min-width:768px){.md\:col-span-2{grid-column:span 2/span 2}.md\:mx-16{margin-left:4rem;margin-right:4rem}.md\:mx-4{margin-left:1rem;margin-right:1rem}.md\:mt-0{margin-top:0}.md\:mt-10{margin-top:2.5rem}.md\:block{display:block}.md\:grid{display:grid}.md\:hidden{display:none}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:gap-6{gap:1.5rem}.md\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.md\:pt-10{padding-top:2.5rem}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-8{padding-bottom:2rem;padding-top:2rem}}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button,.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none} \ No newline at end of file diff --git a/priv/static/assets/app.css.gz b/priv/static/assets/app.css.gz index 697ee8d4fc27c0b43b2d136eb8c6b78a3aa7821f..68d9ba3ea0ae55599508fbe054feb92fff80ba28 100644 GIT binary patch literal 12364 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 literal 8307 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 { - var __create = Object.create; - var __defProp = Object.defineProperty; - var __defProps = Object.defineProperties; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __getOwnPropDescs = Object.getOwnPropertyDescriptors; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __getOwnPropSymbols = Object.getOwnPropertySymbols; - var __getProtoOf = Object.getPrototypeOf; - var __hasOwnProp = Object.prototype.hasOwnProperty; - var __propIsEnum = Object.prototype.propertyIsEnumerable; - var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __spreadValues = (a, b) => { - for (var prop in b || (b = {})) - if (__hasOwnProp.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - if (__getOwnPropSymbols) - for (var prop of __getOwnPropSymbols(b)) { - if (__propIsEnum.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - } - return a; - }; - var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); - var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; - }; - var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; - }; - var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod - )); - - // vendor/topbar.js - var require_topbar = __commonJS({ - "vendor/topbar.js"(exports, module) { - (function(window2, document2) { - "use strict"; - (function() { - var lastTime = 0; - var vendors = ["ms", "moz", "webkit", "o"]; - for (var x = 0; x < vendors.length && !window2.requestAnimationFrame; ++x) { - window2.requestAnimationFrame = window2[vendors[x] + "RequestAnimationFrame"]; - window2.cancelAnimationFrame = window2[vendors[x] + "CancelAnimationFrame"] || window2[vendors[x] + "CancelRequestAnimationFrame"]; - } - if (!window2.requestAnimationFrame) - window2.requestAnimationFrame = function(callback, element) { - var currTime = (/* @__PURE__ */ new Date()).getTime(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = window2.setTimeout(function() { - callback(currTime + timeToCall); - }, timeToCall); - lastTime = currTime + timeToCall; - return id; - }; - if (!window2.cancelAnimationFrame) - window2.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 = window2.innerWidth; - canvas.height = options.barThickness * 5; - 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 = document2.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); - document2.body.appendChild(canvas); - addEvent(window2, "resize", repaint); - }, topbar2 = { - 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(() => topbar2.show(), delay); - } else { - showing = true; - if (fadeTimerId !== null) - window2.cancelAnimationFrame(fadeTimerId); - if (!canvas) - createCanvas(); - canvas.style.opacity = 1; - canvas.style.display = "block"; - topbar2.progress(0); - if (options.autoRun) { - (function loop() { - progressTimerId = window2.requestAnimationFrame(loop); - topbar2.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) { - window2.cancelAnimationFrame(progressTimerId); - progressTimerId = null; - } - (function loop() { - if (topbar2.progress("+.1") >= 1) { - canvas.style.opacity -= 0.05; - if (canvas.style.opacity <= 0.05) { - canvas.style.display = "none"; - fadeTimerId = null; - return; - } - } - fadeTimerId = window2.requestAnimationFrame(loop); - })(); - } - }; - if (typeof module === "object" && typeof module.exports === "object") { - module.exports = topbar2; - } else if (typeof define === "function" && define.amd) { - define(function() { - return topbar2; - }); - } else { - this.topbar = topbar2; - } - }).call(exports, window, document); - } - }); - - // ../deps/phoenix_html/priv/static/phoenix_html.js - (function() { - var PolyfillEvent = eventConstructor(); - function eventConstructor() { - if (typeof window.CustomEvent === "function") - return window.CustomEvent; - function CustomEvent2(event, params2) { - params2 = params2 || { bubbles: false, cancelable: false, detail: void 0 }; - var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event, params2.bubbles, params2.cancelable, params2.detail); - return evt; - } - CustomEvent2.prototype = window.Event.prototype; - return CustomEvent2; - } - function buildHiddenInput(name, value) { - var input = document.createElement("input"); - input.type = "hidden"; - input.name = name; - input.value = value; - return input; - } - function handleClick(element, targetModifierKey) { - var to = element.getAttribute("data-to"), method = buildHiddenInput("_method", element.getAttribute("data-method")), csrf = buildHiddenInput("_csrf_token", element.getAttribute("data-csrf")), form = document.createElement("form"), submit = document.createElement("input"), target = element.getAttribute("target"); - form.method = element.getAttribute("data-method") === "get" ? "get" : "post"; - form.action = to; - form.style.display = "none"; - if (target) - form.target = target; - else if (targetModifierKey) - form.target = "_blank"; - form.appendChild(csrf); - form.appendChild(method); - document.body.appendChild(form); - submit.type = "submit"; - form.appendChild(submit); - submit.click(); - } - window.addEventListener("click", function(e) { - var element = e.target; - if (e.defaultPrevented) - return; - while (element && element.getAttribute) { - var phoenixLinkEvent = new PolyfillEvent("phoenix.link.click", { - "bubbles": true, - "cancelable": true - }); - if (!element.dispatchEvent(phoenixLinkEvent)) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - if (element.getAttribute("data-method") && element.getAttribute("data-to")) { - handleClick(element, e.metaKey || e.shiftKey); - e.preventDefault(); - return false; - } else { - element = element.parentNode; - } - } - }, false); - window.addEventListener("phoenix.link.click", function(e) { - var message = e.target.getAttribute("data-confirm"); - if (message && !window.confirm(message)) { - e.preventDefault(); - } - }, false); - })(); - - // ../deps/phoenix/priv/static/phoenix.mjs - var closure = (value) => { - if (typeof value === "function") { - return value; - } else { - let closure22 = function() { - return value; - }; - return closure22; - } - }; - var globalSelf = typeof self !== "undefined" ? self : null; - var phxWindow = typeof window !== "undefined" ? window : null; - var global = globalSelf || phxWindow || global; - var DEFAULT_VSN = "2.0.0"; - var SOCKET_STATES = { connecting: 0, open: 1, closing: 2, closed: 3 }; - var DEFAULT_TIMEOUT = 1e4; - var WS_CLOSE_NORMAL = 1e3; - var CHANNEL_STATES = { - closed: "closed", - errored: "errored", - joined: "joined", - joining: "joining", - leaving: "leaving" - }; - var CHANNEL_EVENTS = { - close: "phx_close", - error: "phx_error", - join: "phx_join", - reply: "phx_reply", - leave: "phx_leave" - }; - var TRANSPORTS = { - longpoll: "longpoll", - websocket: "websocket" - }; - var XHR_STATES = { - complete: 4 - }; - var Push = class { - constructor(channel, event, payload, timeout) { - this.channel = channel; - this.event = event; - this.payload = payload || function() { - return {}; - }; - this.receivedResp = null; - this.timeout = timeout; - this.timeoutTimer = null; - this.recHooks = []; - this.sent = false; - } - /** - * - * @param {number} timeout - */ - resend(timeout) { - this.timeout = timeout; - this.reset(); - this.send(); - } - /** - * - */ - send() { - if (this.hasReceived("timeout")) { - return; - } - this.startTimeout(); - this.sent = true; - this.channel.socket.push({ - topic: this.channel.topic, - event: this.event, - payload: this.payload(), - ref: this.ref, - join_ref: this.channel.joinRef() - }); - } - /** - * - * @param {*} status - * @param {*} callback - */ - receive(status, callback) { - if (this.hasReceived(status)) { - callback(this.receivedResp.response); - } - this.recHooks.push({ status, callback }); - return this; - } - /** - * @private - */ - reset() { - this.cancelRefEvent(); - this.ref = null; - this.refEvent = null; - this.receivedResp = null; - this.sent = false; - } - /** - * @private - */ - matchReceive({ status, response, _ref }) { - this.recHooks.filter((h) => h.status === status).forEach((h) => h.callback(response)); - } - /** - * @private - */ - cancelRefEvent() { - if (!this.refEvent) { - return; - } - this.channel.off(this.refEvent); - } - /** - * @private - */ - cancelTimeout() { - clearTimeout(this.timeoutTimer); - this.timeoutTimer = null; - } - /** - * @private - */ - startTimeout() { - if (this.timeoutTimer) { - this.cancelTimeout(); - } - this.ref = this.channel.socket.makeRef(); - this.refEvent = this.channel.replyEventName(this.ref); - this.channel.on(this.refEvent, (payload) => { - this.cancelRefEvent(); - this.cancelTimeout(); - this.receivedResp = payload; - this.matchReceive(payload); - }); - this.timeoutTimer = setTimeout(() => { - this.trigger("timeout", {}); - }, this.timeout); - } - /** - * @private - */ - hasReceived(status) { - return this.receivedResp && this.receivedResp.status === status; - } - /** - * @private - */ - trigger(status, response) { - this.channel.trigger(this.refEvent, { status, response }); - } - }; - var Timer = class { - constructor(callback, timerCalc) { - this.callback = callback; - this.timerCalc = timerCalc; - this.timer = null; - this.tries = 0; - } - reset() { - this.tries = 0; - clearTimeout(this.timer); - } - /** - * Cancels any previous scheduleTimeout and schedules callback - */ - scheduleTimeout() { - clearTimeout(this.timer); - this.timer = setTimeout(() => { - this.tries = this.tries + 1; - this.callback(); - }, this.timerCalc(this.tries + 1)); - } - }; - var Channel = class { - constructor(topic, params2, socket) { - this.state = CHANNEL_STATES.closed; - this.topic = topic; - this.params = closure(params2 || {}); - this.socket = socket; - this.bindings = []; - this.bindingRef = 0; - this.timeout = this.socket.timeout; - this.joinedOnce = false; - this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout); - this.pushBuffer = []; - this.stateChangeRefs = []; - this.rejoinTimer = new Timer(() => { - if (this.socket.isConnected()) { - this.rejoin(); - } - }, this.socket.rejoinAfterMs); - this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset())); - this.stateChangeRefs.push( - this.socket.onOpen(() => { - this.rejoinTimer.reset(); - if (this.isErrored()) { - this.rejoin(); - } - }) - ); - this.joinPush.receive("ok", () => { - this.state = CHANNEL_STATES.joined; - this.rejoinTimer.reset(); - this.pushBuffer.forEach((pushEvent) => pushEvent.send()); - this.pushBuffer = []; - }); - this.joinPush.receive("error", () => { - this.state = CHANNEL_STATES.errored; - if (this.socket.isConnected()) { - this.rejoinTimer.scheduleTimeout(); - } - }); - this.onClose(() => { - this.rejoinTimer.reset(); - if (this.socket.hasLogger()) - this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`); - this.state = CHANNEL_STATES.closed; - this.socket.remove(this); - }); - this.onError((reason) => { - if (this.socket.hasLogger()) - this.socket.log("channel", `error ${this.topic}`, reason); - if (this.isJoining()) { - this.joinPush.reset(); - } - this.state = CHANNEL_STATES.errored; - if (this.socket.isConnected()) { - this.rejoinTimer.scheduleTimeout(); - } - }); - this.joinPush.receive("timeout", () => { - if (this.socket.hasLogger()) - this.socket.log("channel", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout); - let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout); - leavePush.send(); - this.state = CHANNEL_STATES.errored; - this.joinPush.reset(); - if (this.socket.isConnected()) { - this.rejoinTimer.scheduleTimeout(); - } - }); - this.on(CHANNEL_EVENTS.reply, (payload, ref) => { - this.trigger(this.replyEventName(ref), payload); - }); - } - /** - * Join the channel - * @param {integer} timeout - * @returns {Push} - */ - join(timeout = this.timeout) { - if (this.joinedOnce) { - throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance"); - } else { - this.timeout = timeout; - this.joinedOnce = true; - this.rejoin(); - return this.joinPush; - } - } - /** - * Hook into channel close - * @param {Function} callback - */ - onClose(callback) { - this.on(CHANNEL_EVENTS.close, callback); - } - /** - * Hook into channel errors - * @param {Function} callback - */ - onError(callback) { - return this.on(CHANNEL_EVENTS.error, (reason) => callback(reason)); - } - /** - * Subscribes on channel events - * - * Subscription returns a ref counter, which can be used later to - * unsubscribe the exact event listener - * - * @example - * const ref1 = channel.on("event", do_stuff) - * const ref2 = channel.on("event", do_other_stuff) - * channel.off("event", ref1) - * // Since unsubscription, do_stuff won't fire, - * // while do_other_stuff will keep firing on the "event" - * - * @param {string} event - * @param {Function} callback - * @returns {integer} ref - */ - on(event, callback) { - let ref = this.bindingRef++; - this.bindings.push({ event, ref, callback }); - return ref; - } - /** - * Unsubscribes off of channel events - * - * Use the ref returned from a channel.on() to unsubscribe one - * handler, or pass nothing for the ref to unsubscribe all - * handlers for the given event. - * - * @example - * // Unsubscribe the do_stuff handler - * const ref1 = channel.on("event", do_stuff) - * channel.off("event", ref1) - * - * // Unsubscribe all handlers from event - * channel.off("event") - * - * @param {string} event - * @param {integer} ref - */ - off(event, ref) { - this.bindings = this.bindings.filter((bind) => { - return !(bind.event === event && (typeof ref === "undefined" || ref === bind.ref)); - }); - } - /** - * @private - */ - canPush() { - return this.socket.isConnected() && this.isJoined(); - } - /** - * Sends a message `event` to phoenix with the payload `payload`. - * Phoenix receives this in the `handle_in(event, payload, socket)` - * function. if phoenix replies or it times out (default 10000ms), - * then optionally the reply can be received. - * - * @example - * channel.push("event") - * .receive("ok", payload => console.log("phoenix replied:", payload)) - * .receive("error", err => console.log("phoenix errored", err)) - * .receive("timeout", () => console.log("timed out pushing")) - * @param {string} event - * @param {Object} payload - * @param {number} [timeout] - * @returns {Push} - */ - push(event, payload, timeout = this.timeout) { - payload = payload || {}; - if (!this.joinedOnce) { - throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`); - } - let pushEvent = new Push(this, event, function() { - return payload; - }, timeout); - if (this.canPush()) { - pushEvent.send(); - } else { - pushEvent.startTimeout(); - this.pushBuffer.push(pushEvent); - } - return pushEvent; - } - /** Leaves the channel - * - * Unsubscribes from server events, and - * instructs channel to terminate on server - * - * Triggers onClose() hooks - * - * To receive leave acknowledgements, use the `receive` - * hook to bind to the server ack, ie: - * - * @example - * channel.leave().receive("ok", () => alert("left!") ) - * - * @param {integer} timeout - * @returns {Push} - */ - leave(timeout = this.timeout) { - this.rejoinTimer.reset(); - this.joinPush.cancelTimeout(); - this.state = CHANNEL_STATES.leaving; - let onClose = () => { - if (this.socket.hasLogger()) - this.socket.log("channel", `leave ${this.topic}`); - this.trigger(CHANNEL_EVENTS.close, "leave"); - }; - let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout); - leavePush.receive("ok", () => onClose()).receive("timeout", () => onClose()); - leavePush.send(); - if (!this.canPush()) { - leavePush.trigger("ok", {}); - } - return leavePush; - } - /** - * Overridable message hook - * - * Receives all events for specialized message handling - * before dispatching to the channel callbacks. - * - * Must return the payload, modified or unmodified - * @param {string} event - * @param {Object} payload - * @param {integer} ref - * @returns {Object} - */ - onMessage(_event, payload, _ref) { - return payload; - } - /** - * @private - */ - isMember(topic, event, payload, joinRef) { - if (this.topic !== topic) { - return false; - } - if (joinRef && joinRef !== this.joinRef()) { - if (this.socket.hasLogger()) - this.socket.log("channel", "dropping outdated message", { topic, event, payload, joinRef }); - return false; - } else { - return true; - } - } - /** - * @private - */ - joinRef() { - return this.joinPush.ref; - } - /** - * @private - */ - rejoin(timeout = this.timeout) { - if (this.isLeaving()) { - return; - } - this.socket.leaveOpenTopic(this.topic); - this.state = CHANNEL_STATES.joining; - this.joinPush.resend(timeout); - } - /** - * @private - */ - trigger(event, payload, ref, joinRef) { - let handledPayload = this.onMessage(event, payload, ref, joinRef); - if (payload && !handledPayload) { - throw new Error("channel onMessage callbacks must return the payload, modified or unmodified"); - } - let eventBindings = this.bindings.filter((bind) => bind.event === event); - for (let i = 0; i < eventBindings.length; i++) { - let bind = eventBindings[i]; - bind.callback(handledPayload, ref, joinRef || this.joinRef()); - } - } - /** - * @private - */ - replyEventName(ref) { - return `chan_reply_${ref}`; - } - /** - * @private - */ - isClosed() { - return this.state === CHANNEL_STATES.closed; - } - /** - * @private - */ - isErrored() { - return this.state === CHANNEL_STATES.errored; - } - /** - * @private - */ - isJoined() { - return this.state === CHANNEL_STATES.joined; - } - /** - * @private - */ - isJoining() { - return this.state === CHANNEL_STATES.joining; - } - /** - * @private - */ - isLeaving() { - return this.state === CHANNEL_STATES.leaving; - } - }; - var Ajax = class { - static request(method, endPoint, accept, body, timeout, ontimeout, callback) { - if (global.XDomainRequest) { - let req = new global.XDomainRequest(); - return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback); - } else { - let req = new global.XMLHttpRequest(); - return this.xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback); - } - } - static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback) { - req.timeout = timeout; - req.open(method, endPoint); - req.onload = () => { - let response = this.parseJSON(req.responseText); - callback && callback(response); - }; - if (ontimeout) { - req.ontimeout = ontimeout; - } - req.onprogress = () => { - }; - req.send(body); - return req; - } - static xhrRequest(req, method, endPoint, accept, body, timeout, ontimeout, callback) { - req.open(method, endPoint, true); - req.timeout = timeout; - req.setRequestHeader("Content-Type", accept); - req.onerror = () => callback && callback(null); - req.onreadystatechange = () => { - if (req.readyState === XHR_STATES.complete && callback) { - let response = this.parseJSON(req.responseText); - callback(response); - } - }; - if (ontimeout) { - req.ontimeout = ontimeout; - } - req.send(body); - return req; - } - static parseJSON(resp) { - if (!resp || resp === "") { - return null; - } - try { - return JSON.parse(resp); - } catch (e) { - console && console.log("failed to parse JSON response", resp); - return null; - } - } - static serialize(obj, parentKey) { - let queryStr = []; - for (var key in obj) { - if (!Object.prototype.hasOwnProperty.call(obj, key)) { - continue; - } - let paramKey = parentKey ? `${parentKey}[${key}]` : key; - let paramVal = obj[key]; - if (typeof paramVal === "object") { - queryStr.push(this.serialize(paramVal, paramKey)); - } else { - queryStr.push(encodeURIComponent(paramKey) + "=" + encodeURIComponent(paramVal)); - } - } - return queryStr.join("&"); - } - static appendParams(url, params2) { - if (Object.keys(params2).length === 0) { - return url; - } - let prefix = url.match(/\?/) ? "&" : "?"; - return `${url}${prefix}${this.serialize(params2)}`; - } - }; - var arrayBufferToBase64 = (buffer) => { - let binary = ""; - let bytes = new Uint8Array(buffer); - let len = bytes.byteLength; - for (let i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); - }; - var LongPoll = class { - constructor(endPoint) { - this.endPoint = null; - this.token = null; - this.skipHeartbeat = true; - this.reqs = /* @__PURE__ */ new Set(); - this.awaitingBatchAck = false; - this.currentBatch = null; - this.currentBatchTimer = null; - this.batchBuffer = []; - this.onopen = function() { - }; - this.onerror = function() { - }; - this.onmessage = function() { - }; - this.onclose = function() { - }; - this.pollEndpoint = this.normalizeEndpoint(endPoint); - this.readyState = SOCKET_STATES.connecting; - setTimeout(() => this.poll(), 0); - } - normalizeEndpoint(endPoint) { - return endPoint.replace("ws://", "http://").replace("wss://", "https://").replace(new RegExp("(.*)/" + TRANSPORTS.websocket), "$1/" + TRANSPORTS.longpoll); - } - endpointURL() { - return Ajax.appendParams(this.pollEndpoint, { token: this.token }); - } - closeAndRetry(code, reason, wasClean) { - this.close(code, reason, wasClean); - this.readyState = SOCKET_STATES.connecting; - } - ontimeout() { - this.onerror("timeout"); - this.closeAndRetry(1005, "timeout", false); - } - isActive() { - return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting; - } - poll() { - this.ajax("GET", "application/json", null, () => this.ontimeout(), (resp) => { - if (resp) { - var { status, token, messages } = resp; - this.token = token; - } else { - status = 0; - } - switch (status) { - case 200: - messages.forEach((msg) => { - setTimeout(() => this.onmessage({ data: msg }), 0); - }); - this.poll(); - break; - case 204: - this.poll(); - break; - case 410: - this.readyState = SOCKET_STATES.open; - this.onopen({}); - this.poll(); - break; - case 403: - this.onerror(403); - this.close(1008, "forbidden", false); - break; - case 0: - case 500: - this.onerror(500); - this.closeAndRetry(1011, "internal server error", 500); - break; - default: - throw new Error(`unhandled poll status ${status}`); - } - }); - } - // we collect all pushes within the current event loop by - // setTimeout 0, which optimizes back-to-back procedural - // pushes against an empty buffer - send(body) { - if (typeof body !== "string") { - body = arrayBufferToBase64(body); - } - if (this.currentBatch) { - this.currentBatch.push(body); - } else if (this.awaitingBatchAck) { - this.batchBuffer.push(body); - } else { - this.currentBatch = [body]; - this.currentBatchTimer = setTimeout(() => { - this.batchSend(this.currentBatch); - this.currentBatch = null; - }, 0); - } - } - batchSend(messages) { - this.awaitingBatchAck = true; - this.ajax("POST", "application/x-ndjson", messages.join("\n"), () => this.onerror("timeout"), (resp) => { - this.awaitingBatchAck = false; - if (!resp || resp.status !== 200) { - this.onerror(resp && resp.status); - this.closeAndRetry(1011, "internal server error", false); - } else if (this.batchBuffer.length > 0) { - this.batchSend(this.batchBuffer); - this.batchBuffer = []; - } - }); - } - close(code, reason, wasClean) { - for (let req of this.reqs) { - req.abort(); - } - this.readyState = SOCKET_STATES.closed; - let opts = Object.assign({ code: 1e3, reason: void 0, wasClean: true }, { code, reason, wasClean }); - this.batchBuffer = []; - clearTimeout(this.currentBatchTimer); - this.currentBatchTimer = null; - if (typeof CloseEvent !== "undefined") { - this.onclose(new CloseEvent("close", opts)); - } else { - this.onclose(opts); - } - } - ajax(method, contentType, body, onCallerTimeout, callback) { - let req; - let ontimeout = () => { - this.reqs.delete(req); - onCallerTimeout(); - }; - req = Ajax.request(method, this.endpointURL(), contentType, body, this.timeout, ontimeout, (resp) => { - this.reqs.delete(req); - if (this.isActive()) { - callback(resp); - } - }); - this.reqs.add(req); - } - }; - var serializer_default = { - HEADER_LENGTH: 1, - META_LENGTH: 4, - KINDS: { push: 0, reply: 1, broadcast: 2 }, - encode(msg, callback) { - if (msg.payload.constructor === ArrayBuffer) { - return callback(this.binaryEncode(msg)); - } else { - let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]; - return callback(JSON.stringify(payload)); - } - }, - decode(rawPayload, callback) { - if (rawPayload.constructor === ArrayBuffer) { - return callback(this.binaryDecode(rawPayload)); - } else { - let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload); - return callback({ join_ref, ref, topic, event, payload }); - } - }, - // private - binaryEncode(message) { - let { join_ref, ref, event, topic, payload } = message; - let metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length; - let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength); - let view = new DataView(header); - let offset = 0; - view.setUint8(offset++, this.KINDS.push); - view.setUint8(offset++, join_ref.length); - view.setUint8(offset++, ref.length); - view.setUint8(offset++, topic.length); - view.setUint8(offset++, event.length); - Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0))); - Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0))); - Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0))); - Array.from(event, (char) => view.setUint8(offset++, char.charCodeAt(0))); - var combined = new Uint8Array(header.byteLength + payload.byteLength); - combined.set(new Uint8Array(header), 0); - combined.set(new Uint8Array(payload), header.byteLength); - return combined.buffer; - }, - binaryDecode(buffer) { - let view = new DataView(buffer); - let kind = view.getUint8(0); - let decoder = new TextDecoder(); - switch (kind) { - case this.KINDS.push: - return this.decodePush(buffer, view, decoder); - case this.KINDS.reply: - return this.decodeReply(buffer, view, decoder); - case this.KINDS.broadcast: - return this.decodeBroadcast(buffer, view, decoder); - } - }, - decodePush(buffer, view, decoder) { - let joinRefSize = view.getUint8(1); - let topicSize = view.getUint8(2); - let eventSize = view.getUint8(3); - let offset = this.HEADER_LENGTH + this.META_LENGTH - 1; - let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize)); - offset = offset + joinRefSize; - let topic = decoder.decode(buffer.slice(offset, offset + topicSize)); - offset = offset + topicSize; - let event = decoder.decode(buffer.slice(offset, offset + eventSize)); - offset = offset + eventSize; - let data = buffer.slice(offset, buffer.byteLength); - return { join_ref: joinRef, ref: null, topic, event, payload: data }; - }, - decodeReply(buffer, view, decoder) { - let joinRefSize = view.getUint8(1); - let refSize = view.getUint8(2); - let topicSize = view.getUint8(3); - let eventSize = view.getUint8(4); - let offset = this.HEADER_LENGTH + this.META_LENGTH; - let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize)); - offset = offset + joinRefSize; - let ref = decoder.decode(buffer.slice(offset, offset + refSize)); - offset = offset + refSize; - let topic = decoder.decode(buffer.slice(offset, offset + topicSize)); - offset = offset + topicSize; - let event = decoder.decode(buffer.slice(offset, offset + eventSize)); - offset = offset + eventSize; - let data = buffer.slice(offset, buffer.byteLength); - let payload = { status: event, response: data }; - return { join_ref: joinRef, ref, topic, event: CHANNEL_EVENTS.reply, payload }; - }, - decodeBroadcast(buffer, view, decoder) { - let topicSize = view.getUint8(1); - let eventSize = view.getUint8(2); - let offset = this.HEADER_LENGTH + 2; - let topic = decoder.decode(buffer.slice(offset, offset + topicSize)); - offset = offset + topicSize; - let event = decoder.decode(buffer.slice(offset, offset + eventSize)); - offset = offset + eventSize; - let data = buffer.slice(offset, buffer.byteLength); - return { join_ref: null, ref: null, topic, event, payload: data }; - } - }; - var Socket = class { - constructor(endPoint, opts = {}) { - this.stateChangeCallbacks = { open: [], close: [], error: [], message: [] }; - this.channels = []; - this.sendBuffer = []; - this.ref = 0; - this.timeout = opts.timeout || DEFAULT_TIMEOUT; - this.transport = opts.transport || global.WebSocket || LongPoll; - this.primaryPassedHealthCheck = false; - this.longPollFallbackMs = opts.longPollFallbackMs; - this.fallbackTimer = null; - this.sessionStore = opts.sessionStorage || global && global.sessionStorage; - this.establishedConnections = 0; - this.defaultEncoder = serializer_default.encode.bind(serializer_default); - this.defaultDecoder = serializer_default.decode.bind(serializer_default); - this.closeWasClean = false; - this.disconnecting = false; - this.binaryType = opts.binaryType || "arraybuffer"; - this.connectClock = 1; - if (this.transport !== LongPoll) { - this.encode = opts.encode || this.defaultEncoder; - this.decode = opts.decode || this.defaultDecoder; - } else { - this.encode = this.defaultEncoder; - this.decode = this.defaultDecoder; - } - let awaitingConnectionOnPageShow = null; - if (phxWindow && phxWindow.addEventListener) { - phxWindow.addEventListener("pagehide", (_e) => { - if (this.conn) { - this.disconnect(); - awaitingConnectionOnPageShow = this.connectClock; - } - }); - phxWindow.addEventListener("pageshow", (_e) => { - if (awaitingConnectionOnPageShow === this.connectClock) { - awaitingConnectionOnPageShow = null; - this.connect(); - } - }); - } - this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 3e4; - this.rejoinAfterMs = (tries) => { - if (opts.rejoinAfterMs) { - return opts.rejoinAfterMs(tries); - } else { - return [1e3, 2e3, 5e3][tries - 1] || 1e4; - } - }; - this.reconnectAfterMs = (tries) => { - if (opts.reconnectAfterMs) { - return opts.reconnectAfterMs(tries); - } else { - return [10, 50, 100, 150, 200, 250, 500, 1e3, 2e3][tries - 1] || 5e3; - } - }; - this.logger = opts.logger || null; - if (!this.logger && opts.debug) { - this.logger = (kind, msg, data) => { - console.log(`${kind}: ${msg}`, data); - }; - } - this.longpollerTimeout = opts.longpollerTimeout || 2e4; - this.params = closure(opts.params || {}); - this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`; - this.vsn = opts.vsn || DEFAULT_VSN; - this.heartbeatTimeoutTimer = null; - this.heartbeatTimer = null; - this.pendingHeartbeatRef = null; - this.reconnectTimer = new Timer(() => { - this.teardown(() => this.connect()); - }, this.reconnectAfterMs); - } - /** - * Returns the LongPoll transport reference - */ - getLongPollTransport() { - return LongPoll; - } - /** - * Disconnects and replaces the active transport - * - * @param {Function} newTransport - The new transport class to instantiate - * - */ - replaceTransport(newTransport) { - this.connectClock++; - this.closeWasClean = true; - clearTimeout(this.fallbackTimer); - this.reconnectTimer.reset(); - if (this.conn) { - this.conn.close(); - this.conn = null; - } - this.transport = newTransport; - } - /** - * Returns the socket protocol - * - * @returns {string} - */ - protocol() { - return location.protocol.match(/^https/) ? "wss" : "ws"; - } - /** - * The fully qualified socket url - * - * @returns {string} - */ - endPointURL() { - let uri = Ajax.appendParams( - Ajax.appendParams(this.endPoint, this.params()), - { vsn: this.vsn } - ); - if (uri.charAt(0) !== "/") { - return uri; - } - if (uri.charAt(1) === "/") { - return `${this.protocol()}:${uri}`; - } - return `${this.protocol()}://${location.host}${uri}`; - } - /** - * Disconnects the socket - * - * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes. - * - * @param {Function} callback - Optional callback which is called after socket is disconnected. - * @param {integer} code - A status code for disconnection (Optional). - * @param {string} reason - A textual description of the reason to disconnect. (Optional) - */ - disconnect(callback, code, reason) { - this.connectClock++; - this.disconnecting = true; - this.closeWasClean = true; - clearTimeout(this.fallbackTimer); - this.reconnectTimer.reset(); - this.teardown(() => { - this.disconnecting = false; - callback && callback(); - }, code, reason); - } - /** - * - * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}` - * - * Passing params to connect is deprecated; pass them in the Socket constructor instead: - * `new Socket("/socket", {params: {user_id: userToken}})`. - */ - connect(params2) { - if (params2) { - console && console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"); - this.params = closure(params2); - } - if (this.conn && !this.disconnecting) { - return; - } - if (this.longPollFallbackMs && this.transport !== LongPoll) { - this.connectWithFallback(LongPoll, this.longPollFallbackMs); - } else { - this.transportConnect(); - } - } - /** - * Logs the message. Override `this.logger` for specialized logging. noops by default - * @param {string} kind - * @param {string} msg - * @param {Object} data - */ - log(kind, msg, data) { - this.logger && this.logger(kind, msg, data); - } - /** - * Returns true if a logger has been set on this socket. - */ - hasLogger() { - return this.logger !== null; - } - /** - * Registers callbacks for connection open events - * - * @example socket.onOpen(function(){ console.info("the socket was opened") }) - * - * @param {Function} callback - */ - onOpen(callback) { - let ref = this.makeRef(); - this.stateChangeCallbacks.open.push([ref, callback]); - return ref; - } - /** - * Registers callbacks for connection close events - * @param {Function} callback - */ - onClose(callback) { - let ref = this.makeRef(); - this.stateChangeCallbacks.close.push([ref, callback]); - return ref; - } - /** - * Registers callbacks for connection error events - * - * @example socket.onError(function(error){ alert("An error occurred") }) - * - * @param {Function} callback - */ - onError(callback) { - let ref = this.makeRef(); - this.stateChangeCallbacks.error.push([ref, callback]); - return ref; - } - /** - * Registers callbacks for connection message events - * @param {Function} callback - */ - onMessage(callback) { - let ref = this.makeRef(); - this.stateChangeCallbacks.message.push([ref, callback]); - return ref; - } - /** - * Pings the server and invokes the callback with the RTT in milliseconds - * @param {Function} callback - * - * Returns true if the ping was pushed or false if unable to be pushed. - */ - ping(callback) { - if (!this.isConnected()) { - return false; - } - let ref = this.makeRef(); - let startTime = Date.now(); - this.push({ topic: "phoenix", event: "heartbeat", payload: {}, ref }); - let onMsgRef = this.onMessage((msg) => { - if (msg.ref === ref) { - this.off([onMsgRef]); - callback(Date.now() - startTime); - } - }); - return true; - } - /** - * @private - */ - transportConnect() { - this.connectClock++; - this.closeWasClean = false; - this.conn = new this.transport(this.endPointURL()); - this.conn.binaryType = this.binaryType; - this.conn.timeout = this.longpollerTimeout; - this.conn.onopen = () => this.onConnOpen(); - this.conn.onerror = (error) => this.onConnError(error); - this.conn.onmessage = (event) => this.onConnMessage(event); - this.conn.onclose = (event) => this.onConnClose(event); - } - getSession(key) { - return this.sessionStore && this.sessionStore.getItem(key); - } - storeSession(key, val) { - this.sessionStore && this.sessionStore.setItem(key, val); - } - connectWithFallback(fallbackTransport, fallbackThreshold = 2500) { - clearTimeout(this.fallbackTimer); - let established = false; - let primaryTransport = true; - let openRef, errorRef; - let fallback = (reason) => { - this.log("transport", `falling back to ${fallbackTransport.name}...`, reason); - this.off([openRef, errorRef]); - primaryTransport = false; - this.replaceTransport(fallbackTransport); - this.transportConnect(); - }; - if (this.getSession(`phx:fallback:${fallbackTransport.name}`)) { - return fallback("memorized"); - } - this.fallbackTimer = setTimeout(fallback, fallbackThreshold); - errorRef = this.onError((reason) => { - this.log("transport", "error", reason); - if (primaryTransport && !established) { - clearTimeout(this.fallbackTimer); - fallback(reason); - } - }); - this.onOpen(() => { - established = true; - if (!primaryTransport) { - if (!this.primaryPassedHealthCheck) { - this.storeSession(`phx:fallback:${fallbackTransport.name}`, "true"); - } - return this.log("transport", `established ${fallbackTransport.name} fallback`); - } - clearTimeout(this.fallbackTimer); - this.fallbackTimer = setTimeout(fallback, fallbackThreshold); - this.ping((rtt) => { - this.log("transport", "connected to primary after", rtt); - this.primaryPassedHealthCheck = true; - clearTimeout(this.fallbackTimer); - }); - }); - this.transportConnect(); - } - clearHeartbeats() { - clearTimeout(this.heartbeatTimer); - clearTimeout(this.heartbeatTimeoutTimer); - } - onConnOpen() { - if (this.hasLogger()) - this.log("transport", `${this.transport.name} connected to ${this.endPointURL()}`); - this.closeWasClean = false; - this.disconnecting = false; - this.establishedConnections++; - this.flushSendBuffer(); - this.reconnectTimer.reset(); - this.resetHeartbeat(); - this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); - } - /** - * @private - */ - heartbeatTimeout() { - if (this.pendingHeartbeatRef) { - this.pendingHeartbeatRef = null; - if (this.hasLogger()) { - this.log("transport", "heartbeat timeout. Attempting to re-establish connection"); - } - this.triggerChanError(); - this.closeWasClean = false; - this.teardown(() => this.reconnectTimer.scheduleTimeout(), WS_CLOSE_NORMAL, "heartbeat timeout"); - } - } - resetHeartbeat() { - if (this.conn && this.conn.skipHeartbeat) { - return; - } - this.pendingHeartbeatRef = null; - this.clearHeartbeats(); - this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs); - } - teardown(callback, code, reason) { - if (!this.conn) { - return callback && callback(); - } - let connectClock = this.connectClock; - this.waitForBufferDone(() => { - if (connectClock !== this.connectClock) { - return; - } - if (this.conn) { - if (code) { - this.conn.close(code, reason || ""); - } else { - this.conn.close(); - } - } - this.waitForSocketClosed(() => { - if (connectClock !== this.connectClock) { - return; - } - if (this.conn) { - this.conn.onopen = function() { - }; - this.conn.onerror = function() { - }; - this.conn.onmessage = function() { - }; - this.conn.onclose = function() { - }; - this.conn = null; - } - callback && callback(); - }); - }); - } - waitForBufferDone(callback, tries = 1) { - if (tries === 5 || !this.conn || !this.conn.bufferedAmount) { - callback(); - return; - } - setTimeout(() => { - this.waitForBufferDone(callback, tries + 1); - }, 150 * tries); - } - waitForSocketClosed(callback, tries = 1) { - if (tries === 5 || !this.conn || this.conn.readyState === SOCKET_STATES.closed) { - callback(); - return; - } - setTimeout(() => { - this.waitForSocketClosed(callback, tries + 1); - }, 150 * tries); - } - onConnClose(event) { - let closeCode = event && event.code; - if (this.hasLogger()) - this.log("transport", "close", event); - this.triggerChanError(); - this.clearHeartbeats(); - if (!this.closeWasClean && closeCode !== 1e3) { - this.reconnectTimer.scheduleTimeout(); - } - this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event)); - } - /** - * @private - */ - onConnError(error) { - if (this.hasLogger()) - this.log("transport", error); - let transportBefore = this.transport; - let establishedBefore = this.establishedConnections; - this.stateChangeCallbacks.error.forEach(([, callback]) => { - callback(error, transportBefore, establishedBefore); - }); - if (transportBefore === this.transport || establishedBefore > 0) { - this.triggerChanError(); - } - } - /** - * @private - */ - triggerChanError() { - this.channels.forEach((channel) => { - if (!(channel.isErrored() || channel.isLeaving() || channel.isClosed())) { - channel.trigger(CHANNEL_EVENTS.error); - } - }); - } - /** - * @returns {string} - */ - connectionState() { - switch (this.conn && this.conn.readyState) { - case SOCKET_STATES.connecting: - return "connecting"; - case SOCKET_STATES.open: - return "open"; - case SOCKET_STATES.closing: - return "closing"; - default: - return "closed"; - } - } - /** - * @returns {boolean} - */ - isConnected() { - return this.connectionState() === "open"; - } - /** - * @private - * - * @param {Channel} - */ - remove(channel) { - this.off(channel.stateChangeRefs); - this.channels = this.channels.filter((c) => c !== channel); - } - /** - * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations. - * - * @param {refs} - list of refs returned by calls to - * `onOpen`, `onClose`, `onError,` and `onMessage` - */ - off(refs) { - for (let key in this.stateChangeCallbacks) { - this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => { - return refs.indexOf(ref) === -1; - }); - } - } - /** - * Initiates a new channel for the given topic - * - * @param {string} topic - * @param {Object} chanParams - Parameters for the channel - * @returns {Channel} - */ - channel(topic, chanParams = {}) { - let chan = new Channel(topic, chanParams, this); - this.channels.push(chan); - return chan; - } - /** - * @param {Object} data - */ - push(data) { - if (this.hasLogger()) { - let { topic, event, payload, ref, join_ref } = data; - this.log("push", `${topic} ${event} (${join_ref}, ${ref})`, payload); - } - if (this.isConnected()) { - this.encode(data, (result) => this.conn.send(result)); - } else { - this.sendBuffer.push(() => this.encode(data, (result) => this.conn.send(result))); - } - } - /** - * Return the next message ref, accounting for overflows - * @returns {string} - */ - makeRef() { - let newRef = this.ref + 1; - if (newRef === this.ref) { - this.ref = 0; - } else { - this.ref = newRef; - } - return this.ref.toString(); - } - sendHeartbeat() { - if (this.pendingHeartbeatRef && !this.isConnected()) { - return; - } - this.pendingHeartbeatRef = this.makeRef(); - this.push({ topic: "phoenix", event: "heartbeat", payload: {}, ref: this.pendingHeartbeatRef }); - this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs); - } - flushSendBuffer() { - if (this.isConnected() && this.sendBuffer.length > 0) { - this.sendBuffer.forEach((callback) => callback()); - this.sendBuffer = []; - } - } - onConnMessage(rawMessage) { - this.decode(rawMessage.data, (msg) => { - let { topic, event, payload, ref, join_ref } = msg; - if (ref && ref === this.pendingHeartbeatRef) { - this.clearHeartbeats(); - this.pendingHeartbeatRef = null; - this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs); - } - if (this.hasLogger()) - this.log("receive", `${payload.status || ""} ${topic} ${event} ${ref && "(" + ref + ")" || ""}`, payload); - for (let i = 0; i < this.channels.length; i++) { - const channel = this.channels[i]; - if (!channel.isMember(topic, event, payload, join_ref)) { - continue; - } - channel.trigger(event, payload, ref, join_ref); - } - for (let i = 0; i < this.stateChangeCallbacks.message.length; i++) { - let [, callback] = this.stateChangeCallbacks.message[i]; - callback(msg); - } - }); - } - leaveOpenTopic(topic) { - let dupChannel = this.channels.find((c) => c.topic === topic && (c.isJoined() || c.isJoining())); - if (dupChannel) { - if (this.hasLogger()) - this.log("transport", `leaving duplicate topic "${topic}"`); - dupChannel.leave(); - } - } - }; - - // js/app.js - var import_topbar = __toESM(require_topbar()); - - // ../deps/phoenix_live_view/priv/static/phoenix_live_view.esm.js - var CONSECUTIVE_RELOADS = "consecutive-reloads"; - var MAX_RELOADS = 10; - var RELOAD_JITTER_MIN = 5e3; - var RELOAD_JITTER_MAX = 1e4; - var FAILSAFE_JITTER = 3e4; - var PHX_EVENT_CLASSES = [ - "phx-click-loading", - "phx-change-loading", - "phx-submit-loading", - "phx-keydown-loading", - "phx-keyup-loading", - "phx-blur-loading", - "phx-focus-loading", - "phx-hook-loading" - ]; - var PHX_COMPONENT = "data-phx-component"; - var PHX_VIEW_REF = "data-phx-view"; - var PHX_LIVE_LINK = "data-phx-link"; - var PHX_TRACK_STATIC = "track-static"; - var PHX_LINK_STATE = "data-phx-link-state"; - var PHX_REF_LOADING = "data-phx-ref-loading"; - var PHX_REF_SRC = "data-phx-ref-src"; - var PHX_REF_LOCK = "data-phx-ref-lock"; - var PHX_PENDING_REFS = "phx-pending-refs"; - var PHX_TRACK_UPLOADS = "track-uploads"; - var PHX_UPLOAD_REF = "data-phx-upload-ref"; - var PHX_PREFLIGHTED_REFS = "data-phx-preflighted-refs"; - var PHX_DONE_REFS = "data-phx-done-refs"; - var PHX_DROP_TARGET = "drop-target"; - var PHX_ACTIVE_ENTRY_REFS = "data-phx-active-refs"; - var PHX_LIVE_FILE_UPDATED = "phx:live-file:updated"; - var PHX_SKIP = "data-phx-skip"; - var PHX_MAGIC_ID = "data-phx-id"; - var PHX_PRUNE = "data-phx-prune"; - var PHX_CONNECTED_CLASS = "phx-connected"; - var PHX_LOADING_CLASS = "phx-loading"; - var PHX_ERROR_CLASS = "phx-error"; - var PHX_CLIENT_ERROR_CLASS = "phx-client-error"; - var PHX_SERVER_ERROR_CLASS = "phx-server-error"; - var PHX_PARENT_ID = "data-phx-parent-id"; - var PHX_MAIN = "data-phx-main"; - var PHX_ROOT_ID = "data-phx-root-id"; - var PHX_VIEWPORT_TOP = "viewport-top"; - var PHX_VIEWPORT_BOTTOM = "viewport-bottom"; - var PHX_TRIGGER_ACTION = "trigger-action"; - var PHX_HAS_FOCUSED = "phx-has-focused"; - var FOCUSABLE_INPUTS = [ - "text", - "textarea", - "number", - "email", - "password", - "search", - "tel", - "url", - "date", - "time", - "datetime-local", - "color", - "range" - ]; - var CHECKABLE_INPUTS = ["checkbox", "radio"]; - var PHX_HAS_SUBMITTED = "phx-has-submitted"; - var PHX_SESSION = "data-phx-session"; - var PHX_VIEW_SELECTOR = `[${PHX_SESSION}]`; - var PHX_STICKY = "data-phx-sticky"; - var PHX_STATIC = "data-phx-static"; - var PHX_READONLY = "data-phx-readonly"; - var PHX_DISABLED = "data-phx-disabled"; - var PHX_DISABLE_WITH = "disable-with"; - var PHX_DISABLE_WITH_RESTORE = "data-phx-disable-with-restore"; - var PHX_HOOK = "hook"; - var PHX_DEBOUNCE = "debounce"; - var PHX_THROTTLE = "throttle"; - var PHX_UPDATE = "update"; - var PHX_STREAM = "stream"; - var PHX_STREAM_REF = "data-phx-stream"; - var PHX_PORTAL = "data-phx-portal"; - var PHX_TELEPORTED_REF = "data-phx-teleported"; - var PHX_TELEPORTED_SRC = "data-phx-teleported-src"; - var PHX_RUNTIME_HOOK = "data-phx-runtime-hook"; - var PHX_KEY = "key"; - var PHX_PRIVATE = "phxPrivate"; - var PHX_AUTO_RECOVER = "auto-recover"; - var PHX_LV_DEBUG = "phx:live-socket:debug"; - var PHX_LV_PROFILE = "phx:live-socket:profiling"; - var PHX_LV_LATENCY_SIM = "phx:live-socket:latency-sim"; - var PHX_LV_HISTORY_POSITION = "phx:nav-history-position"; - var PHX_PROGRESS = "progress"; - var PHX_MOUNTED = "mounted"; - var PHX_RELOAD_STATUS = "__phoenix_reload_status__"; - var LOADER_TIMEOUT = 1; - var MAX_CHILD_JOIN_ATTEMPTS = 3; - var BEFORE_UNLOAD_LOADER_TIMEOUT = 200; - var DISCONNECTED_TIMEOUT = 500; - var BINDING_PREFIX = "phx-"; - var PUSH_TIMEOUT = 3e4; - var DEBOUNCE_TRIGGER = "debounce-trigger"; - var THROTTLED = "throttled"; - var DEBOUNCE_PREV_KEY = "debounce-prev-key"; - var DEFAULTS = { - debounce: 300, - throttle: 300 - }; - var PHX_PENDING_ATTRS = [PHX_REF_LOADING, PHX_REF_SRC, PHX_REF_LOCK]; - var STATIC = "s"; - var ROOT = "r"; - var COMPONENTS = "c"; - var KEYED = "k"; - var KEYED_COUNT = "kc"; - var EVENTS = "e"; - var REPLY = "r"; - var TITLE = "t"; - var TEMPLATES = "p"; - var STREAM = "stream"; - var EntryUploader = class { - constructor(entry, config, liveSocket2) { - const { chunk_size, chunk_timeout } = config; - this.liveSocket = liveSocket2; - this.entry = entry; - this.offset = 0; - this.chunkSize = chunk_size; - this.chunkTimeout = chunk_timeout; - this.chunkTimer = null; - this.errored = false; - this.uploadChannel = liveSocket2.channel(`lvu:${entry.ref}`, { - token: entry.metadata() - }); - } - error(reason) { - if (this.errored) { - return; - } - this.uploadChannel.leave(); - this.errored = true; - clearTimeout(this.chunkTimer); - this.entry.error(reason); - } - upload() { - this.uploadChannel.onError((reason) => this.error(reason)); - this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", (reason) => this.error(reason)); - } - isDone() { - return this.offset >= this.entry.file.size; - } - readNextChunk() { - const reader = new window.FileReader(); - const blob = this.entry.file.slice( - this.offset, - this.chunkSize + this.offset - ); - reader.onload = (e) => { - if (e.target.error === null) { - this.offset += /** @type {ArrayBuffer} */ - e.target.result.byteLength; - this.pushChunk( - /** @type {ArrayBuffer} */ - e.target.result - ); - } else { - return logError("Read error: " + e.target.error); - } - }; - reader.readAsArrayBuffer(blob); - } - pushChunk(chunk) { - if (!this.uploadChannel.isJoined()) { - return; - } - this.uploadChannel.push("chunk", chunk, this.chunkTimeout).receive("ok", () => { - this.entry.progress(this.offset / this.entry.file.size * 100); - if (!this.isDone()) { - this.chunkTimer = setTimeout( - () => this.readNextChunk(), - this.liveSocket.getLatencySim() || 0 - ); - } - }).receive("error", ({ reason }) => this.error(reason)); - } - }; - var logError = (msg, obj) => console.error && console.error(msg, obj); - var isCid = (cid) => { - const type = typeof cid; - return type === "number" || type === "string" && /^(0|[1-9]\d*)$/.test(cid); - }; - function detectDuplicateIds() { - const ids = /* @__PURE__ */ new Set(); - const elems = document.querySelectorAll("*[id]"); - for (let i = 0, len = elems.length; i < len; i++) { - if (ids.has(elems[i].id)) { - console.error( - `Multiple IDs detected: ${elems[i].id}. Ensure unique element ids.` - ); - } else { - ids.add(elems[i].id); - } - } - } - function detectInvalidStreamInserts(inserts) { - const errors = /* @__PURE__ */ new Set(); - Object.keys(inserts).forEach((id) => { - const streamEl = document.getElementById(id); - if (streamEl && streamEl.parentElement && streamEl.parentElement.getAttribute("phx-update") !== "stream") { - errors.add( - `The stream container with id "${streamEl.parentElement.id}" is missing the phx-update="stream" attribute. Ensure it is set for streams to work properly.` - ); - } - }); - errors.forEach((error) => console.error(error)); - } - var debug = (view, kind, msg, obj) => { - if (view.liveSocket.isDebugEnabled()) { - console.log(`${view.id} ${kind}: ${msg} - `, obj); - } - }; - var closure2 = (val) => typeof val === "function" ? val : function() { - return val; - }; - var clone = (obj) => { - return JSON.parse(JSON.stringify(obj)); - }; - var closestPhxBinding = (el, binding, borderEl) => { - do { - if (el.matches(`[${binding}]`) && !el.disabled) { - return el; - } - el = el.parentElement || el.parentNode; - } while (el !== null && el.nodeType === 1 && !(borderEl && borderEl.isSameNode(el) || el.matches(PHX_VIEW_SELECTOR))); - return null; - }; - var isObject = (obj) => { - return obj !== null && typeof obj === "object" && !(obj instanceof Array); - }; - var isEqualObj = (obj1, obj2) => JSON.stringify(obj1) === JSON.stringify(obj2); - var isEmpty = (obj) => { - for (const x in obj) { - return false; - } - return true; - }; - var maybe = (el, callback) => el && callback(el); - var channelUploader = function(entries, onError, resp, liveSocket2) { - entries.forEach((entry) => { - const entryUploader = new EntryUploader(entry, resp.config, liveSocket2); - entryUploader.upload(); - }); - }; - var Browser = { - canPushState() { - return typeof history.pushState !== "undefined"; - }, - dropLocal(localStorage, namespace, subkey) { - return localStorage.removeItem(this.localKey(namespace, subkey)); - }, - updateLocal(localStorage, namespace, subkey, initial, func) { - const current = this.getLocal(localStorage, namespace, subkey); - const key = this.localKey(namespace, subkey); - const newVal = current === null ? initial : func(current); - localStorage.setItem(key, JSON.stringify(newVal)); - return newVal; - }, - getLocal(localStorage, namespace, subkey) { - return JSON.parse(localStorage.getItem(this.localKey(namespace, subkey))); - }, - updateCurrentState(callback) { - if (!this.canPushState()) { - return; - } - history.replaceState( - callback(history.state || {}), - "", - window.location.href - ); - }, - pushState(kind, meta, to) { - if (this.canPushState()) { - if (to !== window.location.href) { - if (meta.type == "redirect" && meta.scroll) { - const currentState = history.state || {}; - currentState.scroll = meta.scroll; - history.replaceState(currentState, "", window.location.href); - } - delete meta.scroll; - history[kind + "State"](meta, "", to || null); - window.requestAnimationFrame(() => { - const hashEl = this.getHashTargetEl(window.location.hash); - if (hashEl) { - hashEl.scrollIntoView(); - } else if (meta.type === "redirect") { - window.scroll(0, 0); - } - }); - } - } else { - this.redirect(to); - } - }, - setCookie(name, value, maxAgeSeconds) { - const expires = typeof maxAgeSeconds === "number" ? ` max-age=${maxAgeSeconds};` : ""; - document.cookie = `${name}=${value};${expires} path=/`; - }, - getCookie(name) { - return document.cookie.replace( - new RegExp(`(?:(?:^|.*;s*)${name}s*=s*([^;]*).*$)|^.*$`), - "$1" - ); - }, - deleteCookie(name) { - document.cookie = `${name}=; max-age=-1; path=/`; - }, - redirect(toURL, flash, navigate = (url) => { - window.location.href = url; - }) { - if (flash) { - this.setCookie("__phoenix_flash__", flash, 60); - } - navigate(toURL); - }, - localKey(namespace, subkey) { - return `${namespace}-${subkey}`; - }, - getHashTargetEl(maybeHash) { - const hash = maybeHash.toString().substring(1); - if (hash === "") { - return; - } - return document.getElementById(hash) || document.querySelector(`a[name="${hash}"]`); - } - }; - var browser_default = Browser; - var DOM = { - byId(id) { - return document.getElementById(id) || logError(`no id found for ${id}`); - }, - removeClass(el, className) { - el.classList.remove(className); - if (el.classList.length === 0) { - el.removeAttribute("class"); - } - }, - all(node, query, callback) { - if (!node) { - return []; - } - const array = Array.from(node.querySelectorAll(query)); - if (callback) { - array.forEach(callback); - } - return array; - }, - childNodeLength(html) { - const template = document.createElement("template"); - template.innerHTML = html; - return template.content.childElementCount; - }, - isUploadInput(el) { - return el.type === "file" && el.getAttribute(PHX_UPLOAD_REF) !== null; - }, - isAutoUpload(inputEl) { - return inputEl.hasAttribute("data-phx-auto-upload"); - }, - findUploadInputs(node) { - const formId = node.id; - const inputsOutsideForm = this.all( - document, - `input[type="file"][${PHX_UPLOAD_REF}][form="${formId}"]` - ); - return this.all(node, `input[type="file"][${PHX_UPLOAD_REF}]`).concat( - inputsOutsideForm - ); - }, - findComponentNodeList(viewId, cid, doc2 = document) { - return this.all( - doc2, - `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]` - ); - }, - isPhxDestroyed(node) { - return node.id && DOM.private(node, "destroyed") ? true : false; - }, - wantsNewTab(e) { - const wantsNewTab = e.ctrlKey || e.shiftKey || e.metaKey || e.button && e.button === 1; - const isDownload = e.target instanceof HTMLAnchorElement && e.target.hasAttribute("download"); - const isTargetBlank = e.target.hasAttribute("target") && e.target.getAttribute("target").toLowerCase() === "_blank"; - const isTargetNamedTab = e.target.hasAttribute("target") && !e.target.getAttribute("target").startsWith("_"); - return wantsNewTab || isTargetBlank || isDownload || isTargetNamedTab; - }, - isUnloadableFormSubmit(e) { - const isDialogSubmit = e.target && e.target.getAttribute("method") === "dialog" || e.submitter && e.submitter.getAttribute("formmethod") === "dialog"; - if (isDialogSubmit) { - return false; - } else { - return !e.defaultPrevented && !this.wantsNewTab(e); - } - }, - isNewPageClick(e, currentLocation) { - const href = e.target instanceof HTMLAnchorElement ? e.target.getAttribute("href") : null; - let url; - if (e.defaultPrevented || href === null || this.wantsNewTab(e)) { - return false; - } - if (href.startsWith("mailto:") || href.startsWith("tel:")) { - return false; - } - if (e.target.isContentEditable) { - return false; - } - try { - url = new URL(href); - } catch (e2) { - try { - url = new URL(href, currentLocation); - } catch (e3) { - return true; - } - } - if (url.host === currentLocation.host && url.protocol === currentLocation.protocol) { - if (url.pathname === currentLocation.pathname && url.search === currentLocation.search) { - return url.hash === "" && !url.href.endsWith("#"); - } - } - return url.protocol.startsWith("http"); - }, - markPhxChildDestroyed(el) { - if (this.isPhxChild(el)) { - el.setAttribute(PHX_SESSION, ""); - } - this.putPrivate(el, "destroyed", true); - }, - findPhxChildrenInFragment(html, parentId) { - const template = document.createElement("template"); - template.innerHTML = html; - return this.findPhxChildren(template.content, parentId); - }, - isIgnored(el, phxUpdate) { - return (el.getAttribute(phxUpdate) || el.getAttribute("data-phx-update")) === "ignore"; - }, - isPhxUpdate(el, phxUpdate, updateTypes) { - return el.getAttribute && updateTypes.indexOf(el.getAttribute(phxUpdate)) >= 0; - }, - findPhxSticky(el) { - return this.all(el, `[${PHX_STICKY}]`); - }, - findPhxChildren(el, parentId) { - return this.all(el, `${PHX_VIEW_SELECTOR}[${PHX_PARENT_ID}="${parentId}"]`); - }, - findExistingParentCIDs(viewId, cids) { - const parentCids = /* @__PURE__ */ new Set(); - const childrenCids = /* @__PURE__ */ new Set(); - cids.forEach((cid) => { - this.all( - document, - `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]` - ).forEach((parent) => { - parentCids.add(cid); - this.all(parent, `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}]`).map((el) => parseInt(el.getAttribute(PHX_COMPONENT))).forEach((childCID) => childrenCids.add(childCID)); - }); - }); - childrenCids.forEach((childCid) => parentCids.delete(childCid)); - return parentCids; - }, - private(el, key) { - return el[PHX_PRIVATE] && el[PHX_PRIVATE][key]; - }, - deletePrivate(el, key) { - el[PHX_PRIVATE] && delete el[PHX_PRIVATE][key]; - }, - putPrivate(el, key, value) { - if (!el[PHX_PRIVATE]) { - el[PHX_PRIVATE] = {}; - } - el[PHX_PRIVATE][key] = value; - }, - updatePrivate(el, key, defaultVal, updateFunc) { - const existing = this.private(el, key); - if (existing === void 0) { - this.putPrivate(el, key, updateFunc(defaultVal)); - } else { - this.putPrivate(el, key, updateFunc(existing)); - } - }, - syncPendingAttrs(fromEl, toEl) { - if (!fromEl.hasAttribute(PHX_REF_SRC)) { - return; - } - PHX_EVENT_CLASSES.forEach((className) => { - fromEl.classList.contains(className) && toEl.classList.add(className); - }); - PHX_PENDING_ATTRS.filter((attr) => fromEl.hasAttribute(attr)).forEach( - (attr) => { - toEl.setAttribute(attr, fromEl.getAttribute(attr)); - } - ); - }, - copyPrivates(target, source) { - if (source[PHX_PRIVATE]) { - target[PHX_PRIVATE] = source[PHX_PRIVATE]; - } - }, - putTitle(str) { - const titleEl = document.querySelector("title"); - if (titleEl) { - const { prefix, suffix, default: defaultTitle } = titleEl.dataset; - const isEmpty2 = typeof str !== "string" || str.trim() === ""; - if (isEmpty2 && typeof defaultTitle !== "string") { - return; - } - const inner = isEmpty2 ? defaultTitle : str; - document.title = `${prefix || ""}${inner || ""}${suffix || ""}`; - } else { - document.title = str; - } - }, - debounce(el, event, phxDebounce, defaultDebounce, phxThrottle, defaultThrottle, asyncFilter, callback) { - let debounce = el.getAttribute(phxDebounce); - let throttle = el.getAttribute(phxThrottle); - if (debounce === "") { - debounce = defaultDebounce; - } - if (throttle === "") { - throttle = defaultThrottle; - } - const value = debounce || throttle; - switch (value) { - case null: - return callback(); - case "blur": - this.incCycle(el, "debounce-blur-cycle", () => { - if (asyncFilter()) { - callback(); - } - }); - if (this.once(el, "debounce-blur")) { - el.addEventListener( - "blur", - () => this.triggerCycle(el, "debounce-blur-cycle") - ); - } - return; - default: - const timeout = parseInt(value); - const trigger = () => throttle ? this.deletePrivate(el, THROTTLED) : callback(); - const currentCycle = this.incCycle(el, DEBOUNCE_TRIGGER, trigger); - if (isNaN(timeout)) { - return logError(`invalid throttle/debounce value: ${value}`); - } - if (throttle) { - let newKeyDown = false; - if (event.type === "keydown") { - const prevKey = this.private(el, DEBOUNCE_PREV_KEY); - this.putPrivate(el, DEBOUNCE_PREV_KEY, event.key); - newKeyDown = prevKey !== event.key; - } - if (!newKeyDown && this.private(el, THROTTLED)) { - return false; - } else { - callback(); - const t = setTimeout(() => { - if (asyncFilter()) { - this.triggerCycle(el, DEBOUNCE_TRIGGER); - } - }, timeout); - this.putPrivate(el, THROTTLED, t); - } - } else { - setTimeout(() => { - if (asyncFilter()) { - this.triggerCycle(el, DEBOUNCE_TRIGGER, currentCycle); - } - }, timeout); - } - const form = el.form; - if (form && this.once(form, "bind-debounce")) { - form.addEventListener("submit", () => { - Array.from(new FormData(form).entries(), ([name]) => { - const input = form.querySelector(`[name="${name}"]`); - this.incCycle(input, DEBOUNCE_TRIGGER); - this.deletePrivate(input, THROTTLED); - }); - }); - } - if (this.once(el, "bind-debounce")) { - el.addEventListener("blur", () => { - clearTimeout(this.private(el, THROTTLED)); - this.triggerCycle(el, DEBOUNCE_TRIGGER); - }); - } - } - }, - triggerCycle(el, key, currentCycle) { - const [cycle, trigger] = this.private(el, key); - if (!currentCycle) { - currentCycle = cycle; - } - if (currentCycle === cycle) { - this.incCycle(el, key); - trigger(); - } - }, - once(el, key) { - if (this.private(el, key) === true) { - return false; - } - this.putPrivate(el, key, true); - return true; - }, - incCycle(el, key, trigger = function() { - }) { - let [currentCycle] = this.private(el, key) || [0, trigger]; - currentCycle++; - this.putPrivate(el, key, [currentCycle, trigger]); - return currentCycle; - }, - // maintains or adds privately used hook information - // fromEl and toEl can be the same element in the case of a newly added node - // fromEl and toEl can be any HTML node type, so we need to check if it's an element node - maintainPrivateHooks(fromEl, toEl, phxViewportTop, phxViewportBottom) { - if (fromEl.hasAttribute && fromEl.hasAttribute("data-phx-hook") && !toEl.hasAttribute("data-phx-hook")) { - toEl.setAttribute("data-phx-hook", fromEl.getAttribute("data-phx-hook")); - } - if (toEl.hasAttribute && (toEl.hasAttribute(phxViewportTop) || toEl.hasAttribute(phxViewportBottom))) { - toEl.setAttribute("data-phx-hook", "Phoenix.InfiniteScroll"); - } - }, - putCustomElHook(el, hook) { - if (el.isConnected) { - el.setAttribute("data-phx-hook", ""); - } else { - console.error(` - hook attached to non-connected DOM element - ensure you are calling createHook within your connectedCallback. ${el.outerHTML} - `); - } - this.putPrivate(el, "custom-el-hook", hook); - }, - getCustomElHook(el) { - return this.private(el, "custom-el-hook"); - }, - isUsedInput(el) { - return el.nodeType === Node.ELEMENT_NODE && (this.private(el, PHX_HAS_FOCUSED) || this.private(el, PHX_HAS_SUBMITTED)); - }, - resetForm(form) { - Array.from(form.elements).forEach((input) => { - this.deletePrivate(input, PHX_HAS_FOCUSED); - this.deletePrivate(input, PHX_HAS_SUBMITTED); - }); - }, - isPhxChild(node) { - return node.getAttribute && node.getAttribute(PHX_PARENT_ID); - }, - isPhxSticky(node) { - return node.getAttribute && node.getAttribute(PHX_STICKY) !== null; - }, - isChildOfAny(el, parents) { - return !!parents.find((parent) => parent.contains(el)); - }, - firstPhxChild(el) { - return this.isPhxChild(el) ? el : this.all(el, `[${PHX_PARENT_ID}]`)[0]; - }, - isPortalTemplate(el) { - return el.tagName === "TEMPLATE" && el.hasAttribute(PHX_PORTAL); - }, - closestViewEl(el) { - const portalOrViewEl = el.closest( - `[${PHX_TELEPORTED_REF}],${PHX_VIEW_SELECTOR}` - ); - if (!portalOrViewEl) { - return null; - } - if (portalOrViewEl.hasAttribute(PHX_TELEPORTED_REF)) { - return this.byId(portalOrViewEl.getAttribute(PHX_TELEPORTED_REF)); - } else if (portalOrViewEl.hasAttribute(PHX_SESSION)) { - return portalOrViewEl; - } - return null; - }, - dispatchEvent(target, name, opts = {}) { - let defaultBubble = true; - const isUploadTarget = target.nodeName === "INPUT" && target.type === "file"; - if (isUploadTarget && name === "click") { - defaultBubble = false; - } - const bubbles = opts.bubbles === void 0 ? defaultBubble : !!opts.bubbles; - const eventOpts = { - bubbles, - cancelable: true, - detail: opts.detail || {} - }; - const event = name === "click" ? new MouseEvent("click", eventOpts) : new CustomEvent(name, eventOpts); - target.dispatchEvent(event); - }, - cloneNode(node, html) { - if (typeof html === "undefined") { - return node.cloneNode(true); - } else { - const cloned = node.cloneNode(false); - cloned.innerHTML = html; - return cloned; - } - }, - // merge attributes from source to target - // if an element is ignored, we only merge data attributes - // including removing data attributes that are no longer in the source - mergeAttrs(target, source, opts = {}) { - const exclude = new Set(opts.exclude || []); - const isIgnored = opts.isIgnored; - const sourceAttrs = source.attributes; - for (let i = sourceAttrs.length - 1; i >= 0; i--) { - const name = sourceAttrs[i].name; - if (!exclude.has(name)) { - const sourceValue = source.getAttribute(name); - if (target.getAttribute(name) !== sourceValue && (!isIgnored || isIgnored && name.startsWith("data-"))) { - target.setAttribute(name, sourceValue); - } - } else { - if (name === "value" && target.value === source.value) { - target.setAttribute("value", source.getAttribute(name)); - } - } - } - const targetAttrs = target.attributes; - for (let i = targetAttrs.length - 1; i >= 0; i--) { - const name = targetAttrs[i].name; - if (isIgnored) { - if (name.startsWith("data-") && !source.hasAttribute(name) && !PHX_PENDING_ATTRS.includes(name)) { - target.removeAttribute(name); - } - } else { - if (!source.hasAttribute(name)) { - target.removeAttribute(name); - } - } - } - }, - mergeFocusedInput(target, source) { - if (!(target instanceof HTMLSelectElement)) { - DOM.mergeAttrs(target, source, { exclude: ["value"] }); - } - if (source.readOnly) { - target.setAttribute("readonly", true); - } else { - target.removeAttribute("readonly"); - } - }, - hasSelectionRange(el) { - return el.setSelectionRange && (el.type === "text" || el.type === "textarea"); - }, - restoreFocus(focused, selectionStart, selectionEnd) { - if (focused instanceof HTMLSelectElement) { - focused.focus(); - } - if (!DOM.isTextualInput(focused)) { - return; - } - const wasFocused = focused.matches(":focus"); - if (!wasFocused) { - focused.focus(); - } - if (this.hasSelectionRange(focused)) { - focused.setSelectionRange(selectionStart, selectionEnd); - } - }, - isFormInput(el) { - if (el.localName && customElements.get(el.localName)) { - return customElements.get(el.localName)[`formAssociated`]; - } - return /^(?:input|select|textarea)$/i.test(el.tagName) && el.type !== "button"; - }, - syncAttrsToProps(el) { - if (el instanceof HTMLInputElement && CHECKABLE_INPUTS.indexOf(el.type.toLocaleLowerCase()) >= 0) { - el.checked = el.getAttribute("checked") !== null; - } - }, - isTextualInput(el) { - return FOCUSABLE_INPUTS.indexOf(el.type) >= 0; - }, - isNowTriggerFormExternal(el, phxTriggerExternal) { - return el.getAttribute && el.getAttribute(phxTriggerExternal) !== null && document.body.contains(el); - }, - cleanChildNodes(container, phxUpdate) { - if (DOM.isPhxUpdate(container, phxUpdate, ["append", "prepend", PHX_STREAM])) { - const toRemove = []; - container.childNodes.forEach((childNode) => { - if (!childNode.id) { - const isEmptyTextNode = childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue.trim() === ""; - if (!isEmptyTextNode && childNode.nodeType !== Node.COMMENT_NODE) { - logError( - `only HTML element tags with an id are allowed inside containers with phx-update. - -removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}" - -` - ); - } - toRemove.push(childNode); - } - }); - toRemove.forEach((childNode) => childNode.remove()); - } - }, - replaceRootContainer(container, tagName, attrs) { - const retainedAttrs = /* @__PURE__ */ new Set([ - "id", - PHX_SESSION, - PHX_STATIC, - PHX_MAIN, - PHX_ROOT_ID - ]); - if (container.tagName.toLowerCase() === tagName.toLowerCase()) { - Array.from(container.attributes).filter((attr) => !retainedAttrs.has(attr.name.toLowerCase())).forEach((attr) => container.removeAttribute(attr.name)); - Object.keys(attrs).filter((name) => !retainedAttrs.has(name.toLowerCase())).forEach((attr) => container.setAttribute(attr, attrs[attr])); - return container; - } else { - const newContainer = document.createElement(tagName); - Object.keys(attrs).forEach( - (attr) => newContainer.setAttribute(attr, attrs[attr]) - ); - retainedAttrs.forEach( - (attr) => newContainer.setAttribute(attr, container.getAttribute(attr)) - ); - newContainer.innerHTML = container.innerHTML; - container.replaceWith(newContainer); - return newContainer; - } - }, - getSticky(el, name, defaultVal) { - const op = (DOM.private(el, "sticky") || []).find( - ([existingName]) => name === existingName - ); - if (op) { - const [_name, _op, stashedResult] = op; - return stashedResult; - } else { - return typeof defaultVal === "function" ? defaultVal() : defaultVal; - } - }, - deleteSticky(el, name) { - this.updatePrivate(el, "sticky", [], (ops) => { - return ops.filter(([existingName, _]) => existingName !== name); - }); - }, - putSticky(el, name, op) { - const stashedResult = op(el); - this.updatePrivate(el, "sticky", [], (ops) => { - const existingIndex = ops.findIndex( - ([existingName]) => name === existingName - ); - if (existingIndex >= 0) { - ops[existingIndex] = [name, op, stashedResult]; - } else { - ops.push([name, op, stashedResult]); - } - return ops; - }); - }, - applyStickyOperations(el) { - const ops = DOM.private(el, "sticky"); - if (!ops) { - return; - } - ops.forEach(([name, op, _stashed]) => this.putSticky(el, name, op)); - }, - isLocked(el) { - return el.hasAttribute && el.hasAttribute(PHX_REF_LOCK); - } - }; - var dom_default = DOM; - var UploadEntry = class { - static isActive(fileEl, file) { - const isNew = file._phxRef === void 0; - const activeRefs = fileEl.getAttribute(PHX_ACTIVE_ENTRY_REFS).split(","); - const isActive = activeRefs.indexOf(LiveUploader.genFileRef(file)) >= 0; - return file.size > 0 && (isNew || isActive); - } - static isPreflighted(fileEl, file) { - const preflightedRefs = fileEl.getAttribute(PHX_PREFLIGHTED_REFS).split(","); - const isPreflighted = preflightedRefs.indexOf(LiveUploader.genFileRef(file)) >= 0; - return isPreflighted && this.isActive(fileEl, file); - } - static isPreflightInProgress(file) { - return file._preflightInProgress === true; - } - static markPreflightInProgress(file) { - file._preflightInProgress = true; - } - constructor(fileEl, file, view, autoUpload) { - this.ref = LiveUploader.genFileRef(file); - this.fileEl = fileEl; - this.file = file; - this.view = view; - this.meta = null; - this._isCancelled = false; - this._isDone = false; - this._progress = 0; - this._lastProgressSent = -1; - this._onDone = function() { - }; - this._onElUpdated = this.onElUpdated.bind(this); - this.fileEl.addEventListener(PHX_LIVE_FILE_UPDATED, this._onElUpdated); - this.autoUpload = autoUpload; - } - metadata() { - return this.meta; - } - progress(progress) { - this._progress = Math.floor(progress); - if (this._progress > this._lastProgressSent) { - if (this._progress >= 100) { - this._progress = 100; - this._lastProgressSent = 100; - this._isDone = true; - this.view.pushFileProgress(this.fileEl, this.ref, 100, () => { - LiveUploader.untrackFile(this.fileEl, this.file); - this._onDone(); - }); - } else { - this._lastProgressSent = this._progress; - this.view.pushFileProgress(this.fileEl, this.ref, this._progress); - } - } - } - isCancelled() { - return this._isCancelled; - } - cancel() { - this.file._preflightInProgress = false; - this._isCancelled = true; - this._isDone = true; - this._onDone(); - } - isDone() { - return this._isDone; - } - error(reason = "failed") { - this.fileEl.removeEventListener(PHX_LIVE_FILE_UPDATED, this._onElUpdated); - this.view.pushFileProgress(this.fileEl, this.ref, { error: reason }); - if (!this.isAutoUpload()) { - LiveUploader.clearFiles(this.fileEl); - } - } - isAutoUpload() { - return this.autoUpload; - } - //private - onDone(callback) { - this._onDone = () => { - this.fileEl.removeEventListener(PHX_LIVE_FILE_UPDATED, this._onElUpdated); - callback(); - }; - } - onElUpdated() { - const activeRefs = this.fileEl.getAttribute(PHX_ACTIVE_ENTRY_REFS).split(","); - if (activeRefs.indexOf(this.ref) === -1) { - LiveUploader.untrackFile(this.fileEl, this.file); - this.cancel(); - } - } - toPreflightPayload() { - return { - last_modified: this.file.lastModified, - name: this.file.name, - relative_path: this.file.webkitRelativePath, - size: this.file.size, - type: this.file.type, - ref: this.ref, - meta: typeof this.file.meta === "function" ? this.file.meta() : void 0 - }; - } - uploader(uploaders) { - if (this.meta.uploader) { - const callback = uploaders[this.meta.uploader] || logError(`no uploader configured for ${this.meta.uploader}`); - return { name: this.meta.uploader, callback }; - } else { - return { name: "channel", callback: channelUploader }; - } - } - zipPostFlight(resp) { - this.meta = resp.entries[this.ref]; - if (!this.meta) { - logError(`no preflight upload response returned with ref ${this.ref}`, { - input: this.fileEl, - response: resp - }); - } - } - }; - var liveUploaderFileRef = 0; - var LiveUploader = class _LiveUploader { - static genFileRef(file) { - const ref = file._phxRef; - if (ref !== void 0) { - return ref; - } else { - file._phxRef = (liveUploaderFileRef++).toString(); - return file._phxRef; - } - } - static getEntryDataURL(inputEl, ref, callback) { - const file = this.activeFiles(inputEl).find( - (file2) => this.genFileRef(file2) === ref - ); - callback(URL.createObjectURL(file)); - } - static hasUploadsInProgress(formEl) { - let active = 0; - dom_default.findUploadInputs(formEl).forEach((input) => { - if (input.getAttribute(PHX_PREFLIGHTED_REFS) !== input.getAttribute(PHX_DONE_REFS)) { - active++; - } - }); - return active > 0; - } - static serializeUploads(inputEl) { - const files = this.activeFiles(inputEl); - const fileData = {}; - files.forEach((file) => { - const entry = { path: inputEl.name }; - const uploadRef = inputEl.getAttribute(PHX_UPLOAD_REF); - fileData[uploadRef] = fileData[uploadRef] || []; - entry.ref = this.genFileRef(file); - entry.last_modified = file.lastModified; - entry.name = file.name || entry.ref; - entry.relative_path = file.webkitRelativePath; - entry.type = file.type; - entry.size = file.size; - if (typeof file.meta === "function") { - entry.meta = file.meta(); - } - fileData[uploadRef].push(entry); - }); - return fileData; - } - static clearFiles(inputEl) { - inputEl.value = null; - inputEl.removeAttribute(PHX_UPLOAD_REF); - dom_default.putPrivate(inputEl, "files", []); - } - static untrackFile(inputEl, file) { - dom_default.putPrivate( - inputEl, - "files", - dom_default.private(inputEl, "files").filter((f) => !Object.is(f, file)) - ); - } - /** - * @param {HTMLInputElement} inputEl - * @param {Array} files - * @param {DataTransfer} [dataTransfer] - */ - static trackFiles(inputEl, files, dataTransfer) { - if (inputEl.getAttribute("multiple") !== null) { - const newFiles = files.filter( - (file) => !this.activeFiles(inputEl).find((f) => Object.is(f, file)) - ); - dom_default.updatePrivate( - inputEl, - "files", - [], - (existing) => existing.concat(newFiles) - ); - inputEl.value = null; - } else { - if (dataTransfer && dataTransfer.files.length > 0) { - inputEl.files = dataTransfer.files; - } - dom_default.putPrivate(inputEl, "files", files); - } - } - static activeFileInputs(formEl) { - const fileInputs = dom_default.findUploadInputs(formEl); - return Array.from(fileInputs).filter( - (el) => el.files && this.activeFiles(el).length > 0 - ); - } - static activeFiles(input) { - return (dom_default.private(input, "files") || []).filter( - (f) => UploadEntry.isActive(input, f) - ); - } - static inputsAwaitingPreflight(formEl) { - const fileInputs = dom_default.findUploadInputs(formEl); - return Array.from(fileInputs).filter( - (input) => this.filesAwaitingPreflight(input).length > 0 - ); - } - static filesAwaitingPreflight(input) { - return this.activeFiles(input).filter( - (f) => !UploadEntry.isPreflighted(input, f) && !UploadEntry.isPreflightInProgress(f) - ); - } - static markPreflightInProgress(entries) { - entries.forEach((entry) => UploadEntry.markPreflightInProgress(entry.file)); - } - constructor(inputEl, view, onComplete) { - this.autoUpload = dom_default.isAutoUpload(inputEl); - this.view = view; - this.onComplete = onComplete; - this._entries = Array.from( - _LiveUploader.filesAwaitingPreflight(inputEl) || [] - ).map((file) => new UploadEntry(inputEl, file, view, this.autoUpload)); - _LiveUploader.markPreflightInProgress(this._entries); - this.numEntriesInProgress = this._entries.length; - } - isAutoUpload() { - return this.autoUpload; - } - entries() { - return this._entries; - } - initAdapterUpload(resp, onError, liveSocket2) { - this._entries = this._entries.map((entry) => { - if (entry.isCancelled()) { - this.numEntriesInProgress--; - if (this.numEntriesInProgress === 0) { - this.onComplete(); - } - } else { - entry.zipPostFlight(resp); - entry.onDone(() => { - this.numEntriesInProgress--; - if (this.numEntriesInProgress === 0) { - this.onComplete(); - } - }); - } - return entry; - }); - const groupedEntries = this._entries.reduce((acc, entry) => { - if (!entry.meta) { - return acc; - } - const { name, callback } = entry.uploader(liveSocket2.uploaders); - acc[name] = acc[name] || { callback, entries: [] }; - acc[name].entries.push(entry); - return acc; - }, {}); - for (const name in groupedEntries) { - const { callback, entries } = groupedEntries[name]; - callback(entries, onError, resp, liveSocket2); - } - } - }; - var ARIA = { - anyOf(instance, classes) { - return classes.find((name) => instance instanceof name); - }, - isFocusable(el, interactiveOnly) { - return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !el.disabled && this.anyOf(el, [ - HTMLInputElement, - HTMLSelectElement, - HTMLTextAreaElement, - HTMLButtonElement - ]) || el instanceof HTMLIFrameElement || el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true" || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true"; - }, - attemptFocus(el, interactiveOnly) { - if (this.isFocusable(el, interactiveOnly)) { - try { - el.focus(); - } catch (e) { - } - } - return !!document.activeElement && document.activeElement.isSameNode(el); - }, - focusFirstInteractive(el) { - let child = el.firstElementChild; - while (child) { - if (this.attemptFocus(child, true) || this.focusFirstInteractive(child)) { - return true; - } - child = child.nextElementSibling; - } - }, - focusFirst(el) { - let child = el.firstElementChild; - while (child) { - if (this.attemptFocus(child) || this.focusFirst(child)) { - return true; - } - child = child.nextElementSibling; - } - }, - focusLast(el) { - let child = el.lastElementChild; - while (child) { - if (this.attemptFocus(child) || this.focusLast(child)) { - return true; - } - child = child.previousElementSibling; - } - } - }; - var aria_default = ARIA; - var Hooks = { - LiveFileUpload: { - activeRefs() { - return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS); - }, - preflightedRefs() { - return this.el.getAttribute(PHX_PREFLIGHTED_REFS); - }, - mounted() { - this.preflightedWas = this.preflightedRefs(); - }, - updated() { - const newPreflights = this.preflightedRefs(); - if (this.preflightedWas !== newPreflights) { - this.preflightedWas = newPreflights; - if (newPreflights === "") { - this.__view().cancelSubmit(this.el.form); - } - } - if (this.activeRefs() === "") { - this.el.value = null; - } - this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED)); - } - }, - LiveImgPreview: { - mounted() { - this.ref = this.el.getAttribute("data-phx-entry-ref"); - this.inputEl = document.getElementById( - this.el.getAttribute(PHX_UPLOAD_REF) - ); - LiveUploader.getEntryDataURL(this.inputEl, this.ref, (url) => { - this.url = url; - this.el.src = url; - }); - }, - destroyed() { - URL.revokeObjectURL(this.url); - } - }, - FocusWrap: { - mounted() { - this.focusStart = this.el.firstElementChild; - this.focusEnd = this.el.lastElementChild; - this.focusStart.addEventListener("focus", (e) => { - if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) { - const nextFocus = e.target.nextElementSibling; - aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus); - } else { - aria_default.focusLast(this.el); - } - }); - this.focusEnd.addEventListener("focus", (e) => { - if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) { - const nextFocus = e.target.previousElementSibling; - aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus); - } else { - aria_default.focusFirst(this.el); - } - }); - if (!this.el.contains(document.activeElement)) { - this.el.addEventListener("phx:show-end", () => this.el.focus()); - if (window.getComputedStyle(this.el).display !== "none") { - aria_default.focusFirst(this.el); - } - } - } - } - }; - var findScrollContainer = (el) => { - if (["HTML", "BODY"].indexOf(el.nodeName.toUpperCase()) >= 0) - return null; - if (["scroll", "auto"].indexOf(getComputedStyle(el).overflowY) >= 0) - return el; - return findScrollContainer(el.parentElement); - }; - var scrollTop = (scrollContainer) => { - if (scrollContainer) { - return scrollContainer.scrollTop; - } else { - return document.documentElement.scrollTop || document.body.scrollTop; - } - }; - var bottom = (scrollContainer) => { - if (scrollContainer) { - return scrollContainer.getBoundingClientRect().bottom; - } else { - return window.innerHeight || document.documentElement.clientHeight; - } - }; - var top = (scrollContainer) => { - if (scrollContainer) { - return scrollContainer.getBoundingClientRect().top; - } else { - return 0; - } - }; - var isAtViewportTop = (el, scrollContainer) => { - const rect = el.getBoundingClientRect(); - return Math.ceil(rect.top) >= top(scrollContainer) && Math.ceil(rect.left) >= 0 && Math.floor(rect.top) <= bottom(scrollContainer); - }; - var isAtViewportBottom = (el, scrollContainer) => { - const rect = el.getBoundingClientRect(); - return Math.ceil(rect.bottom) >= top(scrollContainer) && Math.ceil(rect.left) >= 0 && Math.floor(rect.bottom) <= bottom(scrollContainer); - }; - var isWithinViewport = (el, scrollContainer) => { - const rect = el.getBoundingClientRect(); - return Math.ceil(rect.top) >= top(scrollContainer) && Math.ceil(rect.left) >= 0 && Math.floor(rect.top) <= bottom(scrollContainer); - }; - Hooks.InfiniteScroll = { - mounted() { - this.scrollContainer = findScrollContainer(this.el); - let scrollBefore = scrollTop(this.scrollContainer); - let topOverran = false; - const throttleInterval = 500; - let pendingOp = null; - const onTopOverrun = this.throttle( - throttleInterval, - (topEvent, firstChild) => { - pendingOp = () => true; - this.liveSocket.js().push(this.el, topEvent, { - value: { id: firstChild.id, _overran: true }, - callback: () => { - pendingOp = null; - } - }); - } - ); - const onFirstChildAtTop = this.throttle( - throttleInterval, - (topEvent, firstChild) => { - pendingOp = () => firstChild.scrollIntoView({ block: "start" }); - this.liveSocket.js().push(this.el, topEvent, { - value: { id: firstChild.id }, - callback: () => { - pendingOp = null; - window.requestAnimationFrame(() => { - if (!isWithinViewport(firstChild, this.scrollContainer)) { - firstChild.scrollIntoView({ block: "start" }); - } - }); - } - }); - } - ); - const onLastChildAtBottom = this.throttle( - throttleInterval, - (bottomEvent, lastChild) => { - pendingOp = () => lastChild.scrollIntoView({ block: "end" }); - this.liveSocket.js().push(this.el, bottomEvent, { - value: { id: lastChild.id }, - callback: () => { - pendingOp = null; - window.requestAnimationFrame(() => { - if (!isWithinViewport(lastChild, this.scrollContainer)) { - lastChild.scrollIntoView({ block: "end" }); - } - }); - } - }); - } - ); - this.onScroll = (_e) => { - const scrollNow = scrollTop(this.scrollContainer); - if (pendingOp) { - scrollBefore = scrollNow; - return pendingOp(); - } - const rect = this.el.getBoundingClientRect(); - const topEvent = this.el.getAttribute( - this.liveSocket.binding("viewport-top") - ); - const bottomEvent = this.el.getAttribute( - this.liveSocket.binding("viewport-bottom") - ); - const lastChild = this.el.lastElementChild; - const firstChild = this.el.firstElementChild; - const isScrollingUp = scrollNow < scrollBefore; - const isScrollingDown = scrollNow > scrollBefore; - if (isScrollingUp && topEvent && !topOverran && rect.top >= 0) { - topOverran = true; - onTopOverrun(topEvent, firstChild); - } else if (isScrollingDown && topOverran && rect.top <= 0) { - topOverran = false; - } - if (topEvent && isScrollingUp && isAtViewportTop(firstChild, this.scrollContainer)) { - onFirstChildAtTop(topEvent, firstChild); - } else if (bottomEvent && isScrollingDown && isAtViewportBottom(lastChild, this.scrollContainer)) { - onLastChildAtBottom(bottomEvent, lastChild); - } - scrollBefore = scrollNow; - }; - if (this.scrollContainer) { - this.scrollContainer.addEventListener("scroll", this.onScroll); - } else { - window.addEventListener("scroll", this.onScroll); - } - }, - destroyed() { - if (this.scrollContainer) { - this.scrollContainer.removeEventListener("scroll", this.onScroll); - } else { - window.removeEventListener("scroll", this.onScroll); - } - }, - throttle(interval, callback) { - let lastCallAt = 0; - let timer; - return (...args) => { - const now = Date.now(); - const remainingTime = interval - (now - lastCallAt); - if (remainingTime <= 0 || remainingTime > interval) { - if (timer) { - clearTimeout(timer); - timer = null; - } - lastCallAt = now; - callback(...args); - } else if (!timer) { - timer = setTimeout(() => { - lastCallAt = Date.now(); - timer = null; - callback(...args); - }, remainingTime); - } - }; - } - }; - var hooks_default = Hooks; - var ElementRef = class { - static onUnlock(el, callback) { - if (!dom_default.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) { - return callback(); - } - const closestLock = el.closest(`[${PHX_REF_LOCK}]`); - const ref = closestLock.closest(`[${PHX_REF_LOCK}]`).getAttribute(PHX_REF_LOCK); - closestLock.addEventListener( - `phx:undo-lock:${ref}`, - () => { - callback(); - }, - { once: true } - ); - } - constructor(el) { - this.el = el; - this.loadingRef = el.hasAttribute(PHX_REF_LOADING) ? parseInt(el.getAttribute(PHX_REF_LOADING), 10) : null; - this.lockRef = el.hasAttribute(PHX_REF_LOCK) ? parseInt(el.getAttribute(PHX_REF_LOCK), 10) : null; - } - // public - maybeUndo(ref, phxEvent, eachCloneCallback) { - if (!this.isWithin(ref)) { - dom_default.updatePrivate(this.el, PHX_PENDING_REFS, [], (pendingRefs) => { - pendingRefs.push(ref); - return pendingRefs; - }); - return; - } - this.undoLocks(ref, phxEvent, eachCloneCallback); - this.undoLoading(ref, phxEvent); - dom_default.updatePrivate(this.el, PHX_PENDING_REFS, [], (pendingRefs) => { - return pendingRefs.filter((pendingRef) => { - let opts = { - detail: { ref: pendingRef, event: phxEvent }, - bubbles: true, - cancelable: false - }; - if (this.loadingRef && this.loadingRef > pendingRef) { - this.el.dispatchEvent( - new CustomEvent(`phx:undo-loading:${pendingRef}`, opts) - ); - } - if (this.lockRef && this.lockRef > pendingRef) { - this.el.dispatchEvent( - new CustomEvent(`phx:undo-lock:${pendingRef}`, opts) - ); - } - return pendingRef > ref; - }); - }); - if (this.isFullyResolvedBy(ref)) { - this.el.removeAttribute(PHX_REF_SRC); - } - } - // private - isWithin(ref) { - return !(this.loadingRef !== null && this.loadingRef > ref && this.lockRef !== null && this.lockRef > ref); - } - // Check for cloned PHX_REF_LOCK element that has been morphed behind - // the scenes while this element was locked in the DOM. - // When we apply the cloned tree to the active DOM element, we must - // - // 1. execute pending mounted hooks for nodes now in the DOM - // 2. undo any ref inside the cloned tree that has since been ack'd - undoLocks(ref, phxEvent, eachCloneCallback) { - if (!this.isLockUndoneBy(ref)) { - return; - } - const clonedTree = dom_default.private(this.el, PHX_REF_LOCK); - if (clonedTree) { - eachCloneCallback(clonedTree); - dom_default.deletePrivate(this.el, PHX_REF_LOCK); - } - this.el.removeAttribute(PHX_REF_LOCK); - const opts = { - detail: { ref, event: phxEvent }, - bubbles: true, - cancelable: false - }; - this.el.dispatchEvent( - new CustomEvent(`phx:undo-lock:${this.lockRef}`, opts) - ); - } - undoLoading(ref, phxEvent) { - if (!this.isLoadingUndoneBy(ref)) { - if (this.canUndoLoading(ref) && this.el.classList.contains("phx-submit-loading")) { - this.el.classList.remove("phx-change-loading"); - } - return; - } - if (this.canUndoLoading(ref)) { - this.el.removeAttribute(PHX_REF_LOADING); - const disabledVal = this.el.getAttribute(PHX_DISABLED); - const readOnlyVal = this.el.getAttribute(PHX_READONLY); - if (readOnlyVal !== null) { - this.el.readOnly = readOnlyVal === "true" ? true : false; - this.el.removeAttribute(PHX_READONLY); - } - if (disabledVal !== null) { - this.el.disabled = disabledVal === "true" ? true : false; - this.el.removeAttribute(PHX_DISABLED); - } - const disableRestore = this.el.getAttribute(PHX_DISABLE_WITH_RESTORE); - if (disableRestore !== null) { - this.el.innerText = disableRestore; - this.el.removeAttribute(PHX_DISABLE_WITH_RESTORE); - } - const opts = { - detail: { ref, event: phxEvent }, - bubbles: true, - cancelable: false - }; - this.el.dispatchEvent( - new CustomEvent(`phx:undo-loading:${this.loadingRef}`, opts) - ); - } - PHX_EVENT_CLASSES.forEach((name) => { - if (name !== "phx-submit-loading" || this.canUndoLoading(ref)) { - dom_default.removeClass(this.el, name); - } - }); - } - isLoadingUndoneBy(ref) { - return this.loadingRef === null ? false : this.loadingRef <= ref; - } - isLockUndoneBy(ref) { - return this.lockRef === null ? false : this.lockRef <= ref; - } - isFullyResolvedBy(ref) { - return (this.loadingRef === null || this.loadingRef <= ref) && (this.lockRef === null || this.lockRef <= ref); - } - // only remove the phx-submit-loading class if we are not locked - canUndoLoading(ref) { - return this.lockRef === null || this.lockRef <= ref; - } - }; - var DOMPostMorphRestorer = class { - constructor(containerBefore, containerAfter, updateType) { - const idsBefore = /* @__PURE__ */ new Set(); - const idsAfter = new Set( - [...containerAfter.children].map((child) => child.id) - ); - const elementsToModify = []; - Array.from(containerBefore.children).forEach((child) => { - if (child.id) { - idsBefore.add(child.id); - if (idsAfter.has(child.id)) { - const previousElementId = child.previousElementSibling && child.previousElementSibling.id; - elementsToModify.push({ - elementId: child.id, - previousElementId - }); - } - } - }); - this.containerId = containerAfter.id; - this.updateType = updateType; - this.elementsToModify = elementsToModify; - this.elementIdsToAdd = [...idsAfter].filter((id) => !idsBefore.has(id)); - } - // We do the following to optimize append/prepend operations: - // 1) Track ids of modified elements & of new elements - // 2) All the modified elements are put back in the correct position in the DOM tree - // by storing the id of their previous sibling - // 3) New elements are going to be put in the right place by morphdom during append. - // For prepend, we move them to the first position in the container - perform() { - const container = dom_default.byId(this.containerId); - if (!container) { - return; - } - this.elementsToModify.forEach((elementToModify) => { - if (elementToModify.previousElementId) { - maybe( - document.getElementById(elementToModify.previousElementId), - (previousElem) => { - maybe( - document.getElementById(elementToModify.elementId), - (elem) => { - const isInRightPlace = elem.previousElementSibling && elem.previousElementSibling.id == previousElem.id; - if (!isInRightPlace) { - previousElem.insertAdjacentElement("afterend", elem); - } - } - ); - } - ); - } else { - maybe(document.getElementById(elementToModify.elementId), (elem) => { - const isInRightPlace = elem.previousElementSibling == null; - if (!isInRightPlace) { - container.insertAdjacentElement("afterbegin", elem); - } - }); - } - }); - if (this.updateType == "prepend") { - this.elementIdsToAdd.reverse().forEach((elemId) => { - maybe( - document.getElementById(elemId), - (elem) => container.insertAdjacentElement("afterbegin", elem) - ); - }); - } - } - }; - var DOCUMENT_FRAGMENT_NODE = 11; - function morphAttrs(fromNode, toNode) { - var toNodeAttrs = toNode.attributes; - var attr; - var attrName; - var attrNamespaceURI; - var attrValue; - var fromValue; - if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) { - return; - } - for (var i = toNodeAttrs.length - 1; i >= 0; i--) { - attr = toNodeAttrs[i]; - attrName = attr.name; - attrNamespaceURI = attr.namespaceURI; - attrValue = attr.value; - if (attrNamespaceURI) { - attrName = attr.localName || attrName; - fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); - if (fromValue !== attrValue) { - if (attr.prefix === "xmlns") { - attrName = attr.name; - } - fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); - } - } else { - fromValue = fromNode.getAttribute(attrName); - if (fromValue !== attrValue) { - fromNode.setAttribute(attrName, attrValue); - } - } - } - var fromNodeAttrs = fromNode.attributes; - for (var d = fromNodeAttrs.length - 1; d >= 0; d--) { - attr = fromNodeAttrs[d]; - attrName = attr.name; - attrNamespaceURI = attr.namespaceURI; - if (attrNamespaceURI) { - attrName = attr.localName || attrName; - if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) { - fromNode.removeAttributeNS(attrNamespaceURI, attrName); - } - } else { - if (!toNode.hasAttribute(attrName)) { - fromNode.removeAttribute(attrName); - } - } - } - } - var range; - var NS_XHTML = "http://www.w3.org/1999/xhtml"; - var doc = typeof document === "undefined" ? void 0 : document; - var HAS_TEMPLATE_SUPPORT = !!doc && "content" in doc.createElement("template"); - var HAS_RANGE_SUPPORT = !!doc && doc.createRange && "createContextualFragment" in doc.createRange(); - function createFragmentFromTemplate(str) { - var template = doc.createElement("template"); - template.innerHTML = str; - return template.content.childNodes[0]; - } - function createFragmentFromRange(str) { - if (!range) { - range = doc.createRange(); - range.selectNode(doc.body); - } - var fragment = range.createContextualFragment(str); - return fragment.childNodes[0]; - } - function createFragmentFromWrap(str) { - var fragment = doc.createElement("body"); - fragment.innerHTML = str; - return fragment.childNodes[0]; - } - function toElement(str) { - str = str.trim(); - if (HAS_TEMPLATE_SUPPORT) { - return createFragmentFromTemplate(str); - } else if (HAS_RANGE_SUPPORT) { - return createFragmentFromRange(str); - } - return createFragmentFromWrap(str); - } - function compareNodeNames(fromEl, toEl) { - var fromNodeName = fromEl.nodeName; - var toNodeName = toEl.nodeName; - var fromCodeStart, toCodeStart; - if (fromNodeName === toNodeName) { - return true; - } - fromCodeStart = fromNodeName.charCodeAt(0); - toCodeStart = toNodeName.charCodeAt(0); - if (fromCodeStart <= 90 && toCodeStart >= 97) { - return fromNodeName === toNodeName.toUpperCase(); - } else if (toCodeStart <= 90 && fromCodeStart >= 97) { - return toNodeName === fromNodeName.toUpperCase(); - } else { - return false; - } - } - function createElementNS(name, namespaceURI) { - return !namespaceURI || namespaceURI === NS_XHTML ? doc.createElement(name) : doc.createElementNS(namespaceURI, name); - } - function moveChildren(fromEl, toEl) { - var curChild = fromEl.firstChild; - while (curChild) { - var nextChild = curChild.nextSibling; - toEl.appendChild(curChild); - curChild = nextChild; - } - return toEl; - } - function syncBooleanAttrProp(fromEl, toEl, name) { - if (fromEl[name] !== toEl[name]) { - fromEl[name] = toEl[name]; - if (fromEl[name]) { - fromEl.setAttribute(name, ""); - } else { - fromEl.removeAttribute(name); - } - } - } - var specialElHandlers = { - OPTION: function(fromEl, toEl) { - var parentNode = fromEl.parentNode; - if (parentNode) { - var parentName = parentNode.nodeName.toUpperCase(); - if (parentName === "OPTGROUP") { - parentNode = parentNode.parentNode; - parentName = parentNode && parentNode.nodeName.toUpperCase(); - } - if (parentName === "SELECT" && !parentNode.hasAttribute("multiple")) { - if (fromEl.hasAttribute("selected") && !toEl.selected) { - fromEl.setAttribute("selected", "selected"); - fromEl.removeAttribute("selected"); - } - parentNode.selectedIndex = -1; - } - } - syncBooleanAttrProp(fromEl, toEl, "selected"); - }, - /** - * The "value" attribute is special for the element since it sets - * the initial value. Changing the "value" attribute without changing the - * "value" property will have no effect since it is only used to the set the - * initial value. Similar for the "checked" attribute, and "disabled". - */ - INPUT: function(fromEl, toEl) { - syncBooleanAttrProp(fromEl, toEl, "checked"); - syncBooleanAttrProp(fromEl, toEl, "disabled"); - if (fromEl.value !== toEl.value) { - fromEl.value = toEl.value; - } - if (!toEl.hasAttribute("value")) { - fromEl.removeAttribute("value"); - } - }, - TEXTAREA: function(fromEl, toEl) { - var newValue = toEl.value; - if (fromEl.value !== newValue) { - fromEl.value = newValue; - } - var firstChild = fromEl.firstChild; - if (firstChild) { - var oldValue = firstChild.nodeValue; - if (oldValue == newValue || !newValue && oldValue == fromEl.placeholder) { - return; - } - firstChild.nodeValue = newValue; - } - }, - SELECT: function(fromEl, toEl) { - if (!toEl.hasAttribute("multiple")) { - var selectedIndex = -1; - var i = 0; - var curChild = fromEl.firstChild; - var optgroup; - var nodeName; - while (curChild) { - nodeName = curChild.nodeName && curChild.nodeName.toUpperCase(); - if (nodeName === "OPTGROUP") { - optgroup = curChild; - curChild = optgroup.firstChild; - if (!curChild) { - curChild = optgroup.nextSibling; - optgroup = null; - } - } else { - if (nodeName === "OPTION") { - if (curChild.hasAttribute("selected")) { - selectedIndex = i; - break; - } - i++; - } - curChild = curChild.nextSibling; - if (!curChild && optgroup) { - curChild = optgroup.nextSibling; - optgroup = null; - } - } - } - fromEl.selectedIndex = selectedIndex; - } - } - }; - var ELEMENT_NODE = 1; - var DOCUMENT_FRAGMENT_NODE$1 = 11; - var TEXT_NODE = 3; - var COMMENT_NODE = 8; - function noop() { - } - function defaultGetNodeKey(node) { - if (node) { - return node.getAttribute && node.getAttribute("id") || node.id; - } - } - function morphdomFactory(morphAttrs2) { - return function morphdom2(fromNode, toNode, options) { - if (!options) { - options = {}; - } - if (typeof toNode === "string") { - if (fromNode.nodeName === "#document" || fromNode.nodeName === "HTML" || fromNode.nodeName === "BODY") { - var toNodeHtml = toNode; - toNode = doc.createElement("html"); - toNode.innerHTML = toNodeHtml; - } else { - toNode = toElement(toNode); - } - } else if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE$1) { - toNode = toNode.firstElementChild; - } - var getNodeKey = options.getNodeKey || defaultGetNodeKey; - var onBeforeNodeAdded = options.onBeforeNodeAdded || noop; - var onNodeAdded = options.onNodeAdded || noop; - var onBeforeElUpdated = options.onBeforeElUpdated || noop; - var onElUpdated = options.onElUpdated || noop; - var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop; - var onNodeDiscarded = options.onNodeDiscarded || noop; - var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop; - var skipFromChildren = options.skipFromChildren || noop; - var addChild = options.addChild || function(parent, child) { - return parent.appendChild(child); - }; - var childrenOnly = options.childrenOnly === true; - var fromNodesLookup = /* @__PURE__ */ Object.create(null); - var keyedRemovalList = []; - function addKeyedRemoval(key) { - keyedRemovalList.push(key); - } - function walkDiscardedChildNodes(node, skipKeyedNodes) { - if (node.nodeType === ELEMENT_NODE) { - var curChild = node.firstChild; - while (curChild) { - var key = void 0; - if (skipKeyedNodes && (key = getNodeKey(curChild))) { - addKeyedRemoval(key); - } else { - onNodeDiscarded(curChild); - if (curChild.firstChild) { - walkDiscardedChildNodes(curChild, skipKeyedNodes); - } - } - curChild = curChild.nextSibling; - } - } - } - function removeNode(node, parentNode, skipKeyedNodes) { - if (onBeforeNodeDiscarded(node) === false) { - return; - } - if (parentNode) { - parentNode.removeChild(node); - } - onNodeDiscarded(node); - walkDiscardedChildNodes(node, skipKeyedNodes); - } - function indexTree(node) { - if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE$1) { - var curChild = node.firstChild; - while (curChild) { - var key = getNodeKey(curChild); - if (key) { - fromNodesLookup[key] = curChild; - } - indexTree(curChild); - curChild = curChild.nextSibling; - } - } - } - indexTree(fromNode); - function handleNodeAdded(el) { - onNodeAdded(el); - var curChild = el.firstChild; - while (curChild) { - var nextSibling = curChild.nextSibling; - var key = getNodeKey(curChild); - if (key) { - var unmatchedFromEl = fromNodesLookup[key]; - if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) { - curChild.parentNode.replaceChild(unmatchedFromEl, curChild); - morphEl(unmatchedFromEl, curChild); - } else { - handleNodeAdded(curChild); - } - } else { - handleNodeAdded(curChild); - } - curChild = nextSibling; - } - } - function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) { - while (curFromNodeChild) { - var fromNextSibling = curFromNodeChild.nextSibling; - if (curFromNodeKey = getNodeKey(curFromNodeChild)) { - addKeyedRemoval(curFromNodeKey); - } else { - removeNode( - curFromNodeChild, - fromEl, - true - /* skip keyed nodes */ - ); - } - curFromNodeChild = fromNextSibling; - } - } - function morphEl(fromEl, toEl, childrenOnly2) { - var toElKey = getNodeKey(toEl); - if (toElKey) { - delete fromNodesLookup[toElKey]; - } - if (!childrenOnly2) { - var beforeUpdateResult = onBeforeElUpdated(fromEl, toEl); - if (beforeUpdateResult === false) { - return; - } else if (beforeUpdateResult instanceof HTMLElement) { - fromEl = beforeUpdateResult; - indexTree(fromEl); - } - morphAttrs2(fromEl, toEl); - onElUpdated(fromEl); - if (onBeforeElChildrenUpdated(fromEl, toEl) === false) { - return; - } - } - if (fromEl.nodeName !== "TEXTAREA") { - morphChildren(fromEl, toEl); - } else { - specialElHandlers.TEXTAREA(fromEl, toEl); - } - } - function morphChildren(fromEl, toEl) { - var skipFrom = skipFromChildren(fromEl, toEl); - var curToNodeChild = toEl.firstChild; - var curFromNodeChild = fromEl.firstChild; - var curToNodeKey; - var curFromNodeKey; - var fromNextSibling; - var toNextSibling; - var matchingFromEl; - outer: - while (curToNodeChild) { - toNextSibling = curToNodeChild.nextSibling; - curToNodeKey = getNodeKey(curToNodeChild); - while (!skipFrom && curFromNodeChild) { - fromNextSibling = curFromNodeChild.nextSibling; - if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) { - curToNodeChild = toNextSibling; - curFromNodeChild = fromNextSibling; - continue outer; - } - curFromNodeKey = getNodeKey(curFromNodeChild); - var curFromNodeType = curFromNodeChild.nodeType; - var isCompatible = void 0; - if (curFromNodeType === curToNodeChild.nodeType) { - if (curFromNodeType === ELEMENT_NODE) { - if (curToNodeKey) { - if (curToNodeKey !== curFromNodeKey) { - if (matchingFromEl = fromNodesLookup[curToNodeKey]) { - if (fromNextSibling === matchingFromEl) { - isCompatible = false; - } else { - fromEl.insertBefore(matchingFromEl, curFromNodeChild); - if (curFromNodeKey) { - addKeyedRemoval(curFromNodeKey); - } else { - removeNode( - curFromNodeChild, - fromEl, - true - /* skip keyed nodes */ - ); - } - curFromNodeChild = matchingFromEl; - curFromNodeKey = getNodeKey(curFromNodeChild); - } - } else { - isCompatible = false; - } - } - } else if (curFromNodeKey) { - isCompatible = false; - } - isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild); - if (isCompatible) { - morphEl(curFromNodeChild, curToNodeChild); - } - } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) { - isCompatible = true; - if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) { - curFromNodeChild.nodeValue = curToNodeChild.nodeValue; - } - } - } - if (isCompatible) { - curToNodeChild = toNextSibling; - curFromNodeChild = fromNextSibling; - continue outer; - } - if (curFromNodeKey) { - addKeyedRemoval(curFromNodeKey); - } else { - removeNode( - curFromNodeChild, - fromEl, - true - /* skip keyed nodes */ - ); - } - curFromNodeChild = fromNextSibling; - } - if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) { - if (!skipFrom) { - addChild(fromEl, matchingFromEl); - } - morphEl(matchingFromEl, curToNodeChild); - } else { - var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild); - if (onBeforeNodeAddedResult !== false) { - if (onBeforeNodeAddedResult) { - curToNodeChild = onBeforeNodeAddedResult; - } - if (curToNodeChild.actualize) { - curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc); - } - addChild(fromEl, curToNodeChild); - handleNodeAdded(curToNodeChild); - } - } - curToNodeChild = toNextSibling; - curFromNodeChild = fromNextSibling; - } - cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey); - var specialElHandler = specialElHandlers[fromEl.nodeName]; - if (specialElHandler) { - specialElHandler(fromEl, toEl); - } - } - var morphedNode = fromNode; - var morphedNodeType = morphedNode.nodeType; - var toNodeType = toNode.nodeType; - if (!childrenOnly) { - if (morphedNodeType === ELEMENT_NODE) { - if (toNodeType === ELEMENT_NODE) { - if (!compareNodeNames(fromNode, toNode)) { - onNodeDiscarded(fromNode); - morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); - } - } else { - morphedNode = toNode; - } - } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { - if (toNodeType === morphedNodeType) { - if (morphedNode.nodeValue !== toNode.nodeValue) { - morphedNode.nodeValue = toNode.nodeValue; - } - return morphedNode; - } else { - morphedNode = toNode; - } - } - } - if (morphedNode === toNode) { - onNodeDiscarded(fromNode); - } else { - if (toNode.isSameNode && toNode.isSameNode(morphedNode)) { - return; - } - morphEl(morphedNode, toNode, childrenOnly); - if (keyedRemovalList) { - for (var i = 0, len = keyedRemovalList.length; i < len; i++) { - var elToRemove = fromNodesLookup[keyedRemovalList[i]]; - if (elToRemove) { - removeNode(elToRemove, elToRemove.parentNode, false); - } - } - } - } - if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) { - if (morphedNode.actualize) { - morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc); - } - fromNode.parentNode.replaceChild(morphedNode, fromNode); - } - return morphedNode; - }; - } - var morphdom = morphdomFactory(morphAttrs); - var morphdom_esm_default = morphdom; - var DOMPatch = class { - constructor(view, container, id, html, streams, targetCID, opts = {}) { - this.view = view; - this.liveSocket = view.liveSocket; - this.container = container; - this.id = id; - this.rootID = view.root.id; - this.html = html; - this.streams = streams; - this.streamInserts = {}; - this.streamComponentRestore = {}; - this.targetCID = targetCID; - this.cidPatch = isCid(this.targetCID); - this.pendingRemoves = []; - this.phxRemove = this.liveSocket.binding("remove"); - this.targetContainer = this.isCIDPatch() ? this.targetCIDContainer(html) : container; - this.callbacks = { - beforeadded: [], - beforeupdated: [], - beforephxChildAdded: [], - afteradded: [], - afterupdated: [], - afterdiscarded: [], - afterphxChildAdded: [], - aftertransitionsDiscarded: [] - }; - this.withChildren = opts.withChildren || opts.undoRef || false; - this.undoRef = opts.undoRef; - } - before(kind, callback) { - this.callbacks[`before${kind}`].push(callback); - } - after(kind, callback) { - this.callbacks[`after${kind}`].push(callback); - } - trackBefore(kind, ...args) { - this.callbacks[`before${kind}`].forEach((callback) => callback(...args)); - } - trackAfter(kind, ...args) { - this.callbacks[`after${kind}`].forEach((callback) => callback(...args)); - } - markPrunableContentForRemoval() { - const phxUpdate = this.liveSocket.binding(PHX_UPDATE); - dom_default.all( - this.container, - `[${phxUpdate}=append] > *, [${phxUpdate}=prepend] > *`, - (el) => { - el.setAttribute(PHX_PRUNE, ""); - } - ); - } - perform(isJoinPatch) { - const { view, liveSocket: liveSocket2, html, container, targetContainer } = this; - if (this.isCIDPatch() && !targetContainer) { - return; - } - const focused = liveSocket2.getActiveElement(); - const { selectionStart, selectionEnd } = focused && dom_default.hasSelectionRange(focused) ? focused : {}; - const phxUpdate = liveSocket2.binding(PHX_UPDATE); - const phxViewportTop = liveSocket2.binding(PHX_VIEWPORT_TOP); - const phxViewportBottom = liveSocket2.binding(PHX_VIEWPORT_BOTTOM); - const phxTriggerExternal = liveSocket2.binding(PHX_TRIGGER_ACTION); - const added = []; - const updates = []; - const appendPrependUpdates = []; - const portalCallbacks = []; - let externalFormTriggered = null; - const morph = (targetContainer2, source, withChildren = this.withChildren) => { - const morphCallbacks = { - // normally, we are running with childrenOnly, as the patch HTML for a LV - // does not include the LV attrs (data-phx-session, etc.) - // when we are patching a live component, we do want to patch the root element as well; - // another case is the recursive patch of a stream item that was kept on reset (-> onBeforeNodeAdded) - childrenOnly: targetContainer2.getAttribute(PHX_COMPONENT) === null && !withChildren, - getNodeKey: (node) => { - if (dom_default.isPhxDestroyed(node)) { - return null; - } - if (isJoinPatch) { - return node.id; - } - return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID); - }, - // skip indexing from children when container is stream - skipFromChildren: (from) => { - return from.getAttribute(phxUpdate) === PHX_STREAM; - }, - // tell morphdom how to add a child - addChild: (parent, child) => { - const { ref, streamAt } = this.getStreamInsert(child); - if (ref === void 0) { - return parent.appendChild(child); - } - this.setStreamRef(child, ref); - if (streamAt === 0) { - parent.insertAdjacentElement("afterbegin", child); - } else if (streamAt === -1) { - const lastChild = parent.lastElementChild; - if (lastChild && !lastChild.hasAttribute(PHX_STREAM_REF)) { - const nonStreamChild = Array.from(parent.children).find( - (c) => !c.hasAttribute(PHX_STREAM_REF) - ); - parent.insertBefore(child, nonStreamChild); - } else { - parent.appendChild(child); - } - } else if (streamAt > 0) { - const sibling = Array.from(parent.children)[streamAt]; - parent.insertBefore(child, sibling); - } - }, - onBeforeNodeAdded: (el) => { - var _a; - if (((_a = this.getStreamInsert(el)) == null ? void 0 : _a.updateOnly) && !this.streamComponentRestore[el.id]) { - return false; - } - dom_default.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom); - this.trackBefore("added", el); - let morphedEl = el; - if (this.streamComponentRestore[el.id]) { - morphedEl = this.streamComponentRestore[el.id]; - delete this.streamComponentRestore[el.id]; - morph(morphedEl, el, true); - } - return morphedEl; - }, - onNodeAdded: (el) => { - if (el.getAttribute) { - this.maybeReOrderStream(el, true); - } - if (dom_default.isPortalTemplate(el)) { - portalCallbacks.push(() => this.teleport(el, morph)); - } - if (el instanceof HTMLImageElement && el.srcset) { - el.srcset = el.srcset; - } else if (el instanceof HTMLVideoElement && el.autoplay) { - el.play(); - } - if (dom_default.isNowTriggerFormExternal(el, phxTriggerExternal)) { - externalFormTriggered = el; - } - if (dom_default.isPhxChild(el) && view.ownsElement(el) || dom_default.isPhxSticky(el) && view.ownsElement(el.parentNode)) { - this.trackAfter("phxChildAdded", el); - } - if (el.nodeName === "SCRIPT" && el.hasAttribute(PHX_RUNTIME_HOOK)) { - this.handleRuntimeHook(el, source); - } - added.push(el); - }, - onNodeDiscarded: (el) => this.onNodeDiscarded(el), - onBeforeNodeDiscarded: (el) => { - if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) { - return true; - } - if (el.parentElement !== null && el.id && dom_default.isPhxUpdate(el.parentElement, phxUpdate, [ - PHX_STREAM, - "append", - "prepend" - ])) { - return false; - } - if (el.getAttribute && el.getAttribute(PHX_TELEPORTED_REF)) { - return false; - } - if (this.maybePendingRemove(el)) { - return false; - } - if (this.skipCIDSibling(el)) { - return false; - } - if (dom_default.isPortalTemplate(el)) { - const teleportedEl = document.getElementById( - el.content.firstElementChild.id - ); - if (teleportedEl) { - teleportedEl.remove(); - morphCallbacks.onNodeDiscarded(teleportedEl); - this.view.dropPortalElementId(teleportedEl.id); - } - } - return true; - }, - onElUpdated: (el) => { - if (dom_default.isNowTriggerFormExternal(el, phxTriggerExternal)) { - externalFormTriggered = el; - } - updates.push(el); - this.maybeReOrderStream(el, false); - }, - onBeforeElUpdated: (fromEl, toEl) => { - if (fromEl.id && fromEl.isSameNode(targetContainer2) && fromEl.id !== toEl.id) { - morphCallbacks.onNodeDiscarded(fromEl); - fromEl.replaceWith(toEl); - return morphCallbacks.onNodeAdded(toEl); - } - dom_default.syncPendingAttrs(fromEl, toEl); - dom_default.maintainPrivateHooks( - fromEl, - toEl, - phxViewportTop, - phxViewportBottom - ); - dom_default.cleanChildNodes(toEl, phxUpdate); - if (this.skipCIDSibling(toEl)) { - this.maybeReOrderStream(fromEl); - return false; - } - if (dom_default.isPhxSticky(fromEl)) { - [PHX_SESSION, PHX_STATIC, PHX_ROOT_ID].map((attr) => [ - attr, - fromEl.getAttribute(attr), - toEl.getAttribute(attr) - ]).forEach(([attr, fromVal, toVal]) => { - if (toVal && fromVal !== toVal) { - fromEl.setAttribute(attr, toVal); - } - }); - return false; - } - if (dom_default.isIgnored(fromEl, phxUpdate) || fromEl.form && fromEl.form.isSameNode(externalFormTriggered)) { - this.trackBefore("updated", fromEl, toEl); - dom_default.mergeAttrs(fromEl, toEl, { - isIgnored: dom_default.isIgnored(fromEl, phxUpdate) - }); - updates.push(fromEl); - dom_default.applyStickyOperations(fromEl); - return false; - } - if (fromEl.type === "number" && fromEl.validity && fromEl.validity.badInput) { - return false; - } - const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl); - const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl); - if (fromEl.hasAttribute(PHX_REF_SRC)) { - const ref = new ElementRef(fromEl); - if (ref.lockRef && (!this.undoRef || !ref.isLockUndoneBy(this.undoRef))) { - if (dom_default.isUploadInput(fromEl)) { - dom_default.mergeAttrs(fromEl, toEl, { isIgnored: true }); - this.trackBefore("updated", fromEl, toEl); - updates.push(fromEl); - } - dom_default.applyStickyOperations(fromEl); - const isLocked = fromEl.hasAttribute(PHX_REF_LOCK); - const clone2 = isLocked ? dom_default.private(fromEl, PHX_REF_LOCK) || fromEl.cloneNode(true) : null; - if (clone2) { - dom_default.putPrivate(fromEl, PHX_REF_LOCK, clone2); - if (!isFocusedFormEl) { - fromEl = clone2; - } - } - } - } - if (dom_default.isPhxChild(toEl)) { - const prevSession = fromEl.getAttribute(PHX_SESSION); - dom_default.mergeAttrs(fromEl, toEl, { exclude: [PHX_STATIC] }); - if (prevSession !== "") { - fromEl.setAttribute(PHX_SESSION, prevSession); - } - fromEl.setAttribute(PHX_ROOT_ID, this.rootID); - dom_default.applyStickyOperations(fromEl); - return false; - } - if (this.undoRef && dom_default.private(toEl, PHX_REF_LOCK)) { - dom_default.putPrivate( - fromEl, - PHX_REF_LOCK, - dom_default.private(toEl, PHX_REF_LOCK) - ); - } - dom_default.copyPrivates(toEl, fromEl); - if (dom_default.isPortalTemplate(toEl)) { - portalCallbacks.push(() => this.teleport(toEl, morph)); - return false; - } - if (isFocusedFormEl && fromEl.type !== "hidden" && !focusedSelectChanged) { - this.trackBefore("updated", fromEl, toEl); - dom_default.mergeFocusedInput(fromEl, toEl); - dom_default.syncAttrsToProps(fromEl); - updates.push(fromEl); - dom_default.applyStickyOperations(fromEl); - return false; - } else { - if (focusedSelectChanged) { - fromEl.blur(); - } - if (dom_default.isPhxUpdate(toEl, phxUpdate, ["append", "prepend"])) { - appendPrependUpdates.push( - new DOMPostMorphRestorer( - fromEl, - toEl, - toEl.getAttribute(phxUpdate) - ) - ); - } - dom_default.syncAttrsToProps(toEl); - dom_default.applyStickyOperations(toEl); - this.trackBefore("updated", fromEl, toEl); - return fromEl; - } - } - }; - morphdom_esm_default(targetContainer2, source, morphCallbacks); - }; - this.trackBefore("added", container); - this.trackBefore("updated", container, container); - liveSocket2.time("morphdom", () => { - this.streams.forEach(([ref, inserts, deleteIds, reset]) => { - inserts.forEach(([key, streamAt, limit, updateOnly]) => { - this.streamInserts[key] = { ref, streamAt, limit, reset, updateOnly }; - }); - if (reset !== void 0) { - dom_default.all(container, `[${PHX_STREAM_REF}="${ref}"]`, (child) => { - this.removeStreamChildElement(child); - }); - } - deleteIds.forEach((id) => { - const child = container.querySelector(`[id="${id}"]`); - if (child) { - this.removeStreamChildElement(child); - } - }); - }); - if (isJoinPatch) { - dom_default.all(this.container, `[${phxUpdate}=${PHX_STREAM}]`).filter((el) => this.view.ownsElement(el)).forEach((el) => { - Array.from(el.children).forEach((child) => { - this.removeStreamChildElement(child, true); - }); - }); - } - morph(targetContainer, html); - portalCallbacks.forEach((callback) => callback()); - this.view.portalElementIds.forEach((id) => { - const el = document.getElementById(id); - if (el) { - const source = document.getElementById( - el.getAttribute(PHX_TELEPORTED_SRC) - ); - if (!source) { - el.remove(); - this.onNodeDiscarded(el); - this.view.dropPortalElementId(id); - } - } - }); - }); - if (liveSocket2.isDebugEnabled()) { - detectDuplicateIds(); - detectInvalidStreamInserts(this.streamInserts); - Array.from(document.querySelectorAll("input[name=id]")).forEach( - (node) => { - if (node instanceof HTMLInputElement && node.form) { - console.error( - 'Detected an input with name="id" inside a form! This will cause problems when patching the DOM.\n', - node - ); - } - } - ); - } - if (appendPrependUpdates.length > 0) { - liveSocket2.time("post-morph append/prepend restoration", () => { - appendPrependUpdates.forEach((update) => update.perform()); - }); - } - liveSocket2.silenceEvents( - () => dom_default.restoreFocus(focused, selectionStart, selectionEnd) - ); - dom_default.dispatchEvent(document, "phx:update"); - added.forEach((el) => this.trackAfter("added", el)); - updates.forEach((el) => this.trackAfter("updated", el)); - this.transitionPendingRemoves(); - if (externalFormTriggered) { - liveSocket2.unload(); - const submitter = dom_default.private(externalFormTriggered, "submitter"); - if (submitter && submitter.name && targetContainer.contains(submitter)) { - const input = document.createElement("input"); - input.type = "hidden"; - const formId = submitter.getAttribute("form"); - if (formId) { - input.setAttribute("form", formId); - } - input.name = submitter.name; - input.value = submitter.value; - submitter.parentElement.insertBefore(input, submitter); - } - Object.getPrototypeOf(externalFormTriggered).submit.call( - externalFormTriggered - ); - } - return true; - } - onNodeDiscarded(el) { - if (dom_default.isPhxChild(el) || dom_default.isPhxSticky(el)) { - this.liveSocket.destroyViewByEl(el); - } - this.trackAfter("discarded", el); - } - maybePendingRemove(node) { - if (node.getAttribute && node.getAttribute(this.phxRemove) !== null) { - this.pendingRemoves.push(node); - return true; - } else { - return false; - } - } - removeStreamChildElement(child, force = false) { - if (!force && !this.view.ownsElement(child)) { - return; - } - if (this.streamInserts[child.id]) { - this.streamComponentRestore[child.id] = child; - child.remove(); - } else { - if (!this.maybePendingRemove(child)) { - child.remove(); - this.onNodeDiscarded(child); - } - } - } - getStreamInsert(el) { - const insert = el.id ? this.streamInserts[el.id] : {}; - return insert || {}; - } - setStreamRef(el, ref) { - dom_default.putSticky( - el, - PHX_STREAM_REF, - (el2) => el2.setAttribute(PHX_STREAM_REF, ref) - ); - } - maybeReOrderStream(el, isNew) { - const { ref, streamAt, reset } = this.getStreamInsert(el); - if (streamAt === void 0) { - return; - } - this.setStreamRef(el, ref); - if (!reset && !isNew) { - return; - } - if (!el.parentElement) { - return; - } - if (streamAt === 0) { - el.parentElement.insertBefore(el, el.parentElement.firstElementChild); - } else if (streamAt > 0) { - const children = Array.from(el.parentElement.children); - const oldIndex = children.indexOf(el); - if (streamAt >= children.length - 1) { - el.parentElement.appendChild(el); - } else { - const sibling = children[streamAt]; - if (oldIndex > streamAt) { - el.parentElement.insertBefore(el, sibling); - } else { - el.parentElement.insertBefore(el, sibling.nextElementSibling); - } - } - } - this.maybeLimitStream(el); - } - maybeLimitStream(el) { - const { limit } = this.getStreamInsert(el); - const children = limit !== null && Array.from(el.parentElement.children); - if (limit && limit < 0 && children.length > limit * -1) { - children.slice(0, children.length + limit).forEach((child) => this.removeStreamChildElement(child)); - } else if (limit && limit >= 0 && children.length > limit) { - children.slice(limit).forEach((child) => this.removeStreamChildElement(child)); - } - } - transitionPendingRemoves() { - const { pendingRemoves, liveSocket: liveSocket2 } = this; - if (pendingRemoves.length > 0) { - liveSocket2.transitionRemoves(pendingRemoves, () => { - pendingRemoves.forEach((el) => { - const child = dom_default.firstPhxChild(el); - if (child) { - liveSocket2.destroyViewByEl(child); - } - el.remove(); - }); - this.trackAfter("transitionsDiscarded", pendingRemoves); - }); - } - } - isChangedSelect(fromEl, toEl) { - if (!(fromEl instanceof HTMLSelectElement) || fromEl.multiple) { - return false; - } - if (fromEl.options.length !== toEl.options.length) { - return true; - } - toEl.value = fromEl.value; - return !fromEl.isEqualNode(toEl); - } - isCIDPatch() { - return this.cidPatch; - } - skipCIDSibling(el) { - return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP); - } - targetCIDContainer(html) { - if (!this.isCIDPatch()) { - return; - } - const [first, ...rest] = dom_default.findComponentNodeList( - this.view.id, - this.targetCID - ); - if (rest.length === 0 && dom_default.childNodeLength(html) === 1) { - return first; - } else { - return first && first.parentNode; - } - } - indexOf(parent, child) { - return Array.from(parent.children).indexOf(child); - } - teleport(el, morph) { - const targetSelector = el.getAttribute(PHX_PORTAL); - const portalContainer = document.querySelector(targetSelector); - if (!portalContainer) { - throw new Error( - "portal target with selector " + targetSelector + " not found" - ); - } - const toTeleport = el.content.firstElementChild; - if (this.skipCIDSibling(toTeleport)) { - return; - } - if (!(toTeleport == null ? void 0 : toTeleport.id)) { - throw new Error( - "phx-portal template must have a single root element with ID!" - ); - } - const existing = document.getElementById(toTeleport.id); - let portalTarget; - if (existing) { - if (!portalContainer.contains(existing)) { - portalContainer.appendChild(existing); - } - portalTarget = existing; - } else { - portalTarget = document.createElement(toTeleport.tagName); - portalContainer.appendChild(portalTarget); - } - toTeleport.setAttribute(PHX_TELEPORTED_REF, this.view.id); - toTeleport.setAttribute(PHX_TELEPORTED_SRC, el.id); - morph(portalTarget, toTeleport, true); - toTeleport.removeAttribute(PHX_TELEPORTED_REF); - toTeleport.removeAttribute(PHX_TELEPORTED_SRC); - this.view.pushPortalElementId(toTeleport.id); - } - handleRuntimeHook(el, source) { - const name = el.getAttribute(PHX_RUNTIME_HOOK); - let nonce = el.hasAttribute("nonce") ? el.getAttribute("nonce") : null; - if (el.hasAttribute("nonce")) { - const template = document.createElement("template"); - template.innerHTML = source; - nonce = template.content.querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`).getAttribute("nonce"); - } - const script = document.createElement("script"); - script.textContent = el.textContent; - dom_default.mergeAttrs(script, el, { isIgnored: false }); - if (nonce) { - script.nonce = nonce; - } - el.replaceWith(script); - el = script; - } - }; - var VOID_TAGS = /* @__PURE__ */ new Set([ - "area", - "base", - "br", - "col", - "command", - "embed", - "hr", - "img", - "input", - "keygen", - "link", - "meta", - "param", - "source", - "track", - "wbr" - ]); - var quoteChars = /* @__PURE__ */ new Set(["'", '"']); - var modifyRoot = (html, attrs, clearInnerHTML) => { - let i = 0; - let insideComment = false; - let beforeTag, afterTag, tag, tagNameEndsAt, id, newHTML; - const lookahead = html.match(/^(\s*(?:\s*)*)<([^\s\/>]+)/); - if (lookahead === null) { - throw new Error(`malformed html ${html}`); - } - i = lookahead[0].length; - beforeTag = lookahead[1]; - tag = lookahead[2]; - tagNameEndsAt = i; - for (i; i < html.length; i++) { - if (html.charAt(i) === ">") { - break; - } - if (html.charAt(i) === "=") { - const isId = html.slice(i - 3, i) === " id"; - i++; - const char = html.charAt(i); - if (quoteChars.has(char)) { - const attrStartsAt = i; - i++; - for (i; i < html.length; i++) { - if (html.charAt(i) === char) { - break; - } - } - if (isId) { - id = html.slice(attrStartsAt + 1, i); - break; - } - } - } - } - let closeAt = html.length - 1; - insideComment = false; - while (closeAt >= beforeTag.length + tag.length) { - const char = html.charAt(closeAt); - if (insideComment) { - if (char === "-" && html.slice(closeAt - 3, closeAt) === "" && html.slice(closeAt - 2, closeAt) === "--") { - insideComment = true; - closeAt -= 3; - } else if (char === ">") { - break; - } else { - closeAt -= 1; - } - } - afterTag = html.slice(closeAt + 1, html.length); - const attrsStr = Object.keys(attrs).map((attr) => attrs[attr] === true ? attr : `${attr}="${attrs[attr]}"`).join(" "); - if (clearInnerHTML) { - const idAttrStr = id ? ` id="${id}"` : ""; - if (VOID_TAGS.has(tag)) { - newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}/>`; - } else { - newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}>`; - } - } else { - const rest = html.slice(tagNameEndsAt, closeAt + 1); - newHTML = `<${tag}${attrsStr === "" ? "" : " "}${attrsStr}${rest}`; - } - return [newHTML, beforeTag, afterTag]; - }; - var Rendered = class { - static extract(diff) { - const { [REPLY]: reply, [EVENTS]: events, [TITLE]: title } = diff; - delete diff[REPLY]; - delete diff[EVENTS]; - delete diff[TITLE]; - return { diff, title, reply: reply || null, events: events || [] }; - } - constructor(viewId, rendered) { - this.viewId = viewId; - this.rendered = {}; - this.magicId = 0; - this.mergeDiff(rendered); - } - parentViewId() { - return this.viewId; - } - toString(onlyCids) { - const { buffer: str, streams } = this.recursiveToString( - this.rendered, - this.rendered[COMPONENTS], - onlyCids, - true, - {} - ); - return { buffer: str, streams }; - } - recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids, changeTracking, rootAttrs) { - onlyCids = onlyCids ? new Set(onlyCids) : null; - const output = { - buffer: "", - components, - onlyCids, - streams: /* @__PURE__ */ new Set() - }; - this.toOutputBuffer(rendered, null, output, changeTracking, rootAttrs); - return { buffer: output.buffer, streams: output.streams }; - } - componentCIDs(diff) { - return Object.keys(diff[COMPONENTS] || {}).map((i) => parseInt(i)); - } - isComponentOnlyDiff(diff) { - if (!diff[COMPONENTS]) { - return false; - } - return Object.keys(diff).length === 1; - } - getComponent(diff, cid) { - return diff[COMPONENTS][cid]; - } - resetRender(cid) { - if (this.rendered[COMPONENTS][cid]) { - this.rendered[COMPONENTS][cid].reset = true; - } - } - mergeDiff(diff) { - const newc = diff[COMPONENTS]; - const cache = {}; - delete diff[COMPONENTS]; - this.rendered = this.mutableMerge(this.rendered, diff); - this.rendered[COMPONENTS] = this.rendered[COMPONENTS] || {}; - if (newc) { - const oldc = this.rendered[COMPONENTS]; - for (const cid in newc) { - newc[cid] = this.cachedFindComponent(cid, newc[cid], oldc, newc, cache); - } - for (const cid in newc) { - oldc[cid] = newc[cid]; - } - diff[COMPONENTS] = newc; - } - } - cachedFindComponent(cid, cdiff, oldc, newc, cache) { - if (cache[cid]) { - return cache[cid]; - } else { - let ndiff, stat, scid = cdiff[STATIC]; - if (isCid(scid)) { - let tdiff; - if (scid > 0) { - tdiff = this.cachedFindComponent(scid, newc[scid], oldc, newc, cache); - } else { - tdiff = oldc[-scid]; - } - stat = tdiff[STATIC]; - ndiff = this.cloneMerge(tdiff, cdiff, true); - ndiff[STATIC] = stat; - } else { - ndiff = cdiff[STATIC] !== void 0 || oldc[cid] === void 0 ? cdiff : this.cloneMerge(oldc[cid], cdiff, false); - } - cache[cid] = ndiff; - return ndiff; - } - } - mutableMerge(target, source) { - if (source[STATIC] !== void 0) { - return source; - } else { - this.doMutableMerge(target, source); - return target; - } - } - doMutableMerge(target, source) { - if (source[KEYED]) { - this.mergeKeyed(target, source); - } else { - for (const key in source) { - const val = source[key]; - const targetVal = target[key]; - const isObjVal = isObject(val); - if (isObjVal && val[STATIC] === void 0 && isObject(targetVal)) { - this.doMutableMerge(targetVal, val); - } else { - target[key] = val; - } - } - } - if (target[ROOT]) { - target.newRender = true; - } - } - clone(diff) { - if ("structuredClone" in window) { - return structuredClone(diff); - } else { - return JSON.parse(JSON.stringify(diff)); - } - } - // keyed comprehensions - mergeKeyed(target, source) { - const clonedTarget = this.clone(target); - Object.entries(source[KEYED]).forEach(([i, entry]) => { - if (i === KEYED_COUNT) { - return; - } - if (Array.isArray(entry)) { - const [old_idx, diff] = entry; - target[KEYED][i] = clonedTarget[KEYED][old_idx]; - this.doMutableMerge(target[KEYED][i], diff); - } else if (typeof entry === "number") { - const old_idx = entry; - target[KEYED][i] = clonedTarget[KEYED][old_idx]; - } else if (typeof entry === "object") { - if (!target[KEYED][i]) { - target[KEYED][i] = {}; - } - this.doMutableMerge(target[KEYED][i], entry); - } - }); - if (source[KEYED][KEYED_COUNT] < target[KEYED][KEYED_COUNT]) { - for (let i = source[KEYED][KEYED_COUNT]; i < target[KEYED][KEYED_COUNT]; i++) { - delete target[KEYED][i]; - } - } - target[KEYED][KEYED_COUNT] = source[KEYED][KEYED_COUNT]; - if (source[STREAM]) { - target[STREAM] = source[STREAM]; - } - if (source[TEMPLATES]) { - target[TEMPLATES] = source[TEMPLATES]; - } - } - // Merges cid trees together, copying statics from source tree. - // - // The `pruneMagicId` is passed to control pruning the magicId of the - // target. We must always prune the magicId when we are sharing statics - // from another component. If not pruning, we replicate the logic from - // mutableMerge, where we set newRender to true if there is a root - // (effectively forcing the new version to be rendered instead of skipped) - // - cloneMerge(target, source, pruneMagicId) { - const merged = __spreadValues(__spreadValues({}, target), source); - for (const key in merged) { - const val = source[key]; - const targetVal = target[key]; - if (isObject(val) && val[STATIC] === void 0 && isObject(targetVal)) { - merged[key] = this.cloneMerge(targetVal, val, pruneMagicId); - } else if (val === void 0 && isObject(targetVal)) { - merged[key] = this.cloneMerge(targetVal, {}, pruneMagicId); - } - } - if (pruneMagicId) { - delete merged.magicId; - delete merged.newRender; - } else if (target[ROOT]) { - merged.newRender = true; - } - return merged; - } - componentToString(cid) { - const { buffer: str, streams } = this.recursiveCIDToString( - this.rendered[COMPONENTS], - cid, - null - ); - const [strippedHTML, _before, _after] = modifyRoot(str, {}); - return { buffer: strippedHTML, streams }; - } - pruneCIDs(cids) { - cids.forEach((cid) => delete this.rendered[COMPONENTS][cid]); - } - // private - get() { - return this.rendered; - } - isNewFingerprint(diff = {}) { - return !!diff[STATIC]; - } - templateStatic(part, templates) { - if (typeof part === "number") { - return templates[part]; - } else { - return part; - } - } - nextMagicID() { - this.magicId++; - return `m${this.magicId}-${this.parentViewId()}`; - } - // Converts rendered tree to output buffer. - // - // changeTracking controls if we can apply the PHX_SKIP optimization. - toOutputBuffer(rendered, templates, output, changeTracking, rootAttrs = {}) { - if (rendered[KEYED]) { - return this.comprehensionToBuffer( - rendered, - templates, - output, - changeTracking - ); - } - if (rendered[TEMPLATES]) { - templates = rendered[TEMPLATES]; - delete rendered[TEMPLATES]; - } - let { [STATIC]: statics } = rendered; - statics = this.templateStatic(statics, templates); - rendered[STATIC] = statics; - const isRoot = rendered[ROOT]; - const prevBuffer = output.buffer; - if (isRoot) { - output.buffer = ""; - } - if (changeTracking && isRoot && !rendered.magicId) { - rendered.newRender = true; - rendered.magicId = this.nextMagicID(); - } - output.buffer += statics[0]; - for (let i = 1; i < statics.length; i++) { - this.dynamicToBuffer(rendered[i - 1], templates, output, changeTracking); - output.buffer += statics[i]; - } - if (isRoot) { - let skip = false; - let attrs; - if (changeTracking || rendered.magicId) { - skip = changeTracking && !rendered.newRender; - attrs = __spreadValues({ [PHX_MAGIC_ID]: rendered.magicId }, rootAttrs); - } else { - attrs = rootAttrs; - } - if (skip) { - attrs[PHX_SKIP] = true; - } - const [newRoot, commentBefore, commentAfter] = modifyRoot( - output.buffer, - attrs, - skip - ); - rendered.newRender = false; - output.buffer = prevBuffer + commentBefore + newRoot + commentAfter; - } - } - comprehensionToBuffer(rendered, templates, output, changeTracking) { - const keyedTemplates = templates || rendered[TEMPLATES]; - const statics = this.templateStatic(rendered[STATIC], templates); - rendered[STATIC] = statics; - delete rendered[TEMPLATES]; - for (let i = 0; i < rendered[KEYED][KEYED_COUNT]; i++) { - output.buffer += statics[0]; - for (let j = 1; j < statics.length; j++) { - this.dynamicToBuffer( - rendered[KEYED][i][j - 1], - keyedTemplates, - output, - changeTracking - ); - output.buffer += statics[j]; - } - } - if (rendered[STREAM]) { - const stream = rendered[STREAM]; - const [_ref, _inserts, deleteIds, reset] = stream || [null, {}, [], null]; - if (stream !== void 0 && (rendered[KEYED][KEYED_COUNT] > 0 || deleteIds.length > 0 || reset)) { - delete rendered[STREAM]; - rendered[KEYED] = { - [KEYED_COUNT]: 0 - }; - output.streams.add(stream); - } - } - } - dynamicToBuffer(rendered, templates, output, changeTracking) { - if (typeof rendered === "number") { - const { buffer: str, streams } = this.recursiveCIDToString( - output.components, - rendered, - output.onlyCids - ); - output.buffer += str; - output.streams = /* @__PURE__ */ new Set([...output.streams, ...streams]); - } else if (isObject(rendered)) { - this.toOutputBuffer(rendered, templates, output, changeTracking, {}); - } else { - output.buffer += rendered; - } - } - recursiveCIDToString(components, cid, onlyCids) { - const component = components[cid] || logError(`no component for CID ${cid}`, components); - const attrs = { [PHX_COMPONENT]: cid, [PHX_VIEW_REF]: this.viewId }; - const skip = onlyCids && !onlyCids.has(cid); - component.newRender = !skip; - component.magicId = `c${cid}-${this.parentViewId()}`; - const changeTracking = !component.reset; - const { buffer: html, streams } = this.recursiveToString( - component, - components, - onlyCids, - changeTracking, - attrs - ); - delete component.reset; - return { buffer: html, streams }; - } - }; - var focusStack = []; - var default_transition_time = 200; - var JS = { - // private - exec(e, eventType, phxEvent, view, sourceEl, defaults) { - const [defaultKind, defaultArgs] = defaults || [ - null, - { callback: defaults && defaults.callback } - ]; - const commands = phxEvent.charAt(0) === "[" ? JSON.parse(phxEvent) : [[defaultKind, defaultArgs]]; - commands.forEach(([kind, args]) => { - if (kind === defaultKind) { - args = __spreadValues(__spreadValues({}, defaultArgs), args); - args.callback = args.callback || defaultArgs.callback; - } - this.filterToEls(view.liveSocket, sourceEl, args).forEach((el) => { - this[`exec_${kind}`](e, eventType, phxEvent, view, sourceEl, el, args); - }); - }); - }, - isVisible(el) { - return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length > 0); - }, - // returns true if any part of the element is inside the viewport - isInViewport(el) { - const rect = el.getBoundingClientRect(); - const windowHeight = window.innerHeight || document.documentElement.clientHeight; - const windowWidth = window.innerWidth || document.documentElement.clientWidth; - return rect.right > 0 && rect.bottom > 0 && rect.left < windowWidth && rect.top < windowHeight; - }, - // private - // commands - exec_exec(e, eventType, phxEvent, view, sourceEl, el, { attr, to }) { - const encodedJS = el.getAttribute(attr); - if (!encodedJS) { - throw new Error(`expected ${attr} to contain JS command on "${to}"`); - } - view.liveSocket.execJS(el, encodedJS, eventType); - }, - exec_dispatch(e, eventType, phxEvent, view, sourceEl, el, { event, detail, bubbles, blocking }) { - detail = detail || {}; - detail.dispatcher = sourceEl; - if (blocking) { - const promise = new Promise((resolve, _reject) => { - detail.done = resolve; - }); - view.liveSocket.asyncTransition(promise); - } - dom_default.dispatchEvent(el, event, { detail, bubbles }); - }, - exec_push(e, eventType, phxEvent, view, sourceEl, el, args) { - const { - event, - data, - target, - page_loading, - loading, - value, - dispatcher, - callback - } = args; - const pushOpts = { - loading, - value, - target, - page_loading: !!page_loading, - originalEvent: e - }; - const targetSrc = eventType === "change" && dispatcher ? dispatcher : sourceEl; - const phxTarget = target || targetSrc.getAttribute(view.binding("target")) || targetSrc; - const handler = (targetView, targetCtx) => { - if (!targetView.isConnected()) { - return; - } - if (eventType === "change") { - let { newCid, _target } = args; - _target = _target || (dom_default.isFormInput(sourceEl) ? sourceEl.name : void 0); - if (_target) { - pushOpts._target = _target; - } - targetView.pushInput( - sourceEl, - targetCtx, - newCid, - event || phxEvent, - pushOpts, - callback - ); - } else if (eventType === "submit") { - const { submitter } = args; - targetView.submitForm( - sourceEl, - targetCtx, - event || phxEvent, - submitter, - pushOpts, - callback - ); - } else { - targetView.pushEvent( - eventType, - sourceEl, - targetCtx, - event || phxEvent, - data, - pushOpts, - callback - ); - } - }; - if (args.targetView && args.targetCtx) { - handler(args.targetView, args.targetCtx); - } else { - view.withinTargets(phxTarget, handler); - } - }, - exec_navigate(e, eventType, phxEvent, view, sourceEl, el, { href, replace }) { - view.liveSocket.historyRedirect( - e, - href, - replace ? "replace" : "push", - null, - sourceEl - ); - }, - exec_patch(e, eventType, phxEvent, view, sourceEl, el, { href, replace }) { - view.liveSocket.pushHistoryPatch( - e, - href, - replace ? "replace" : "push", - sourceEl - ); - }, - exec_focus(e, eventType, phxEvent, view, sourceEl, el) { - aria_default.attemptFocus(el); - window.requestAnimationFrame(() => { - window.requestAnimationFrame(() => aria_default.attemptFocus(el)); - }); - }, - exec_focus_first(e, eventType, phxEvent, view, sourceEl, el) { - aria_default.focusFirstInteractive(el) || aria_default.focusFirst(el); - window.requestAnimationFrame(() => { - window.requestAnimationFrame( - () => aria_default.focusFirstInteractive(el) || aria_default.focusFirst(el) - ); - }); - }, - exec_push_focus(e, eventType, phxEvent, view, sourceEl, el) { - focusStack.push(el || sourceEl); - }, - exec_pop_focus(_e, _eventType, _phxEvent, _view, _sourceEl, _el) { - const el = focusStack.pop(); - if (el) { - el.focus(); - window.requestAnimationFrame(() => { - window.requestAnimationFrame(() => el.focus()); - }); - } - }, - exec_add_class(e, eventType, phxEvent, view, sourceEl, el, { names, transition, time, blocking }) { - this.addOrRemoveClasses(el, names, [], transition, time, view, blocking); - }, - exec_remove_class(e, eventType, phxEvent, view, sourceEl, el, { names, transition, time, blocking }) { - this.addOrRemoveClasses(el, [], names, transition, time, view, blocking); - }, - exec_toggle_class(e, eventType, phxEvent, view, sourceEl, el, { names, transition, time, blocking }) { - this.toggleClasses(el, names, transition, time, view, blocking); - }, - exec_toggle_attr(e, eventType, phxEvent, view, sourceEl, el, { attr: [attr, val1, val2] }) { - this.toggleAttr(el, attr, val1, val2); - }, - exec_ignore_attrs(e, eventType, phxEvent, view, sourceEl, el, { attrs }) { - this.ignoreAttrs(el, attrs); - }, - exec_transition(e, eventType, phxEvent, view, sourceEl, el, { time, transition, blocking }) { - this.addOrRemoveClasses(el, [], [], transition, time, view, blocking); - }, - exec_toggle(e, eventType, phxEvent, view, sourceEl, el, { display, ins, outs, time, blocking }) { - this.toggle(eventType, view, el, display, ins, outs, time, blocking); - }, - exec_show(e, eventType, phxEvent, view, sourceEl, el, { display, transition, time, blocking }) { - this.show(eventType, view, el, display, transition, time, blocking); - }, - exec_hide(e, eventType, phxEvent, view, sourceEl, el, { display, transition, time, blocking }) { - this.hide(eventType, view, el, display, transition, time, blocking); - }, - exec_set_attr(e, eventType, phxEvent, view, sourceEl, el, { attr: [attr, val] }) { - this.setOrRemoveAttrs(el, [[attr, val]], []); - }, - exec_remove_attr(e, eventType, phxEvent, view, sourceEl, el, { attr }) { - this.setOrRemoveAttrs(el, [], [attr]); - }, - ignoreAttrs(el, attrs) { - dom_default.putPrivate(el, "JS:ignore_attrs", { - apply: (fromEl, toEl) => { - Array.from(fromEl.attributes).forEach((attr) => { - if (attrs.some( - (toIgnore) => attr.name == toIgnore || toIgnore.includes("*") && attr.name.match(toIgnore) != null - )) { - toEl.setAttribute(attr.name, attr.value); - } - }); - } - }); - }, - onBeforeElUpdated(fromEl, toEl) { - const ignoreAttrs = dom_default.private(fromEl, "JS:ignore_attrs"); - if (ignoreAttrs) { - ignoreAttrs.apply(fromEl, toEl); - } - }, - // utils for commands - show(eventType, view, el, display, transition, time, blocking) { - if (!this.isVisible(el)) { - this.toggle( - eventType, - view, - el, - display, - transition, - null, - time, - blocking - ); - } - }, - hide(eventType, view, el, display, transition, time, blocking) { - if (this.isVisible(el)) { - this.toggle( - eventType, - view, - el, - display, - null, - transition, - time, - blocking - ); - } - }, - toggle(eventType, view, el, display, ins, outs, time, blocking) { - time = time || default_transition_time; - const [inClasses, inStartClasses, inEndClasses] = ins || [[], [], []]; - const [outClasses, outStartClasses, outEndClasses] = outs || [[], [], []]; - if (inClasses.length > 0 || outClasses.length > 0) { - if (this.isVisible(el)) { - const onStart = () => { - this.addOrRemoveClasses( - el, - outStartClasses, - inClasses.concat(inStartClasses).concat(inEndClasses) - ); - window.requestAnimationFrame(() => { - this.addOrRemoveClasses(el, outClasses, []); - window.requestAnimationFrame( - () => this.addOrRemoveClasses(el, outEndClasses, outStartClasses) - ); - }); - }; - const onEnd = () => { - this.addOrRemoveClasses(el, [], outClasses.concat(outEndClasses)); - dom_default.putSticky( - el, - "toggle", - (currentEl) => currentEl.style.display = "none" - ); - el.dispatchEvent(new Event("phx:hide-end")); - }; - el.dispatchEvent(new Event("phx:hide-start")); - if (blocking === false) { - onStart(); - setTimeout(onEnd, time); - } else { - view.transition(time, onStart, onEnd); - } - } else { - if (eventType === "remove") { - return; - } - const onStart = () => { - this.addOrRemoveClasses( - el, - inStartClasses, - outClasses.concat(outStartClasses).concat(outEndClasses) - ); - const stickyDisplay = display || this.defaultDisplay(el); - window.requestAnimationFrame(() => { - this.addOrRemoveClasses(el, inClasses, []); - window.requestAnimationFrame(() => { - dom_default.putSticky( - el, - "toggle", - (currentEl) => currentEl.style.display = stickyDisplay - ); - this.addOrRemoveClasses(el, inEndClasses, inStartClasses); - }); - }); - }; - const onEnd = () => { - this.addOrRemoveClasses(el, [], inClasses.concat(inEndClasses)); - el.dispatchEvent(new Event("phx:show-end")); - }; - el.dispatchEvent(new Event("phx:show-start")); - if (blocking === false) { - onStart(); - setTimeout(onEnd, time); - } else { - view.transition(time, onStart, onEnd); - } - } - } else { - if (this.isVisible(el)) { - window.requestAnimationFrame(() => { - el.dispatchEvent(new Event("phx:hide-start")); - dom_default.putSticky( - el, - "toggle", - (currentEl) => currentEl.style.display = "none" - ); - el.dispatchEvent(new Event("phx:hide-end")); - }); - } else { - window.requestAnimationFrame(() => { - el.dispatchEvent(new Event("phx:show-start")); - const stickyDisplay = display || this.defaultDisplay(el); - dom_default.putSticky( - el, - "toggle", - (currentEl) => currentEl.style.display = stickyDisplay - ); - el.dispatchEvent(new Event("phx:show-end")); - }); - } - } - }, - toggleClasses(el, classes, transition, time, view, blocking) { - window.requestAnimationFrame(() => { - const [prevAdds, prevRemoves] = dom_default.getSticky(el, "classes", [[], []]); - const newAdds = classes.filter( - (name) => prevAdds.indexOf(name) < 0 && !el.classList.contains(name) - ); - const newRemoves = classes.filter( - (name) => prevRemoves.indexOf(name) < 0 && el.classList.contains(name) - ); - this.addOrRemoveClasses( - el, - newAdds, - newRemoves, - transition, - time, - view, - blocking - ); - }); - }, - toggleAttr(el, attr, val1, val2) { - if (el.hasAttribute(attr)) { - if (val2 !== void 0) { - if (el.getAttribute(attr) === val1) { - this.setOrRemoveAttrs(el, [[attr, val2]], []); - } else { - this.setOrRemoveAttrs(el, [[attr, val1]], []); - } - } else { - this.setOrRemoveAttrs(el, [], [attr]); - } - } else { - this.setOrRemoveAttrs(el, [[attr, val1]], []); - } - }, - addOrRemoveClasses(el, adds, removes, transition, time, view, blocking) { - time = time || default_transition_time; - const [transitionRun, transitionStart, transitionEnd] = transition || [ - [], - [], - [] - ]; - if (transitionRun.length > 0) { - const onStart = () => { - this.addOrRemoveClasses( - el, - transitionStart, - [].concat(transitionRun).concat(transitionEnd) - ); - window.requestAnimationFrame(() => { - this.addOrRemoveClasses(el, transitionRun, []); - window.requestAnimationFrame( - () => this.addOrRemoveClasses(el, transitionEnd, transitionStart) - ); - }); - }; - const onDone = () => this.addOrRemoveClasses( - el, - adds.concat(transitionEnd), - removes.concat(transitionRun).concat(transitionStart) - ); - if (blocking === false) { - onStart(); - setTimeout(onDone, time); - } else { - view.transition(time, onStart, onDone); - } - return; - } - window.requestAnimationFrame(() => { - const [prevAdds, prevRemoves] = dom_default.getSticky(el, "classes", [[], []]); - const keepAdds = adds.filter( - (name) => prevAdds.indexOf(name) < 0 && !el.classList.contains(name) - ); - const keepRemoves = removes.filter( - (name) => prevRemoves.indexOf(name) < 0 && el.classList.contains(name) - ); - const newAdds = prevAdds.filter((name) => removes.indexOf(name) < 0).concat(keepAdds); - const newRemoves = prevRemoves.filter((name) => adds.indexOf(name) < 0).concat(keepRemoves); - dom_default.putSticky(el, "classes", (currentEl) => { - currentEl.classList.remove(...newRemoves); - currentEl.classList.add(...newAdds); - return [newAdds, newRemoves]; - }); - }); - }, - setOrRemoveAttrs(el, sets, removes) { - const [prevSets, prevRemoves] = dom_default.getSticky(el, "attrs", [[], []]); - const alteredAttrs = sets.map(([attr, _val]) => attr).concat(removes); - const newSets = prevSets.filter(([attr, _val]) => !alteredAttrs.includes(attr)).concat(sets); - const newRemoves = prevRemoves.filter((attr) => !alteredAttrs.includes(attr)).concat(removes); - dom_default.putSticky(el, "attrs", (currentEl) => { - newRemoves.forEach((attr) => currentEl.removeAttribute(attr)); - newSets.forEach(([attr, val]) => currentEl.setAttribute(attr, val)); - return [newSets, newRemoves]; - }); - }, - hasAllClasses(el, classes) { - return classes.every((name) => el.classList.contains(name)); - }, - isToggledOut(el, outClasses) { - return !this.isVisible(el) || this.hasAllClasses(el, outClasses); - }, - filterToEls(liveSocket2, sourceEl, { to }) { - const defaultQuery = () => { - if (typeof to === "string") { - return document.querySelectorAll(to); - } else if (to.closest) { - const toEl = sourceEl.closest(to.closest); - return toEl ? [toEl] : []; - } else if (to.inner) { - return sourceEl.querySelectorAll(to.inner); - } - }; - return to ? liveSocket2.jsQuerySelectorAll(sourceEl, to, defaultQuery) : [sourceEl]; - }, - defaultDisplay(el) { - return { tr: "table-row", td: "table-cell" }[el.tagName.toLowerCase()] || "block"; - }, - transitionClasses(val) { - if (!val) { - return null; - } - let [trans, tStart, tEnd] = Array.isArray(val) ? val : [val.split(" "), [], []]; - trans = Array.isArray(trans) ? trans : trans.split(" "); - tStart = Array.isArray(tStart) ? tStart : tStart.split(" "); - tEnd = Array.isArray(tEnd) ? tEnd : tEnd.split(" "); - return [trans, tStart, tEnd]; - } - }; - var js_default = JS; - var js_commands_default = (liveSocket2, eventType) => { - return { - exec(el, encodedJS) { - liveSocket2.execJS(el, encodedJS, eventType); - }, - show(el, opts = {}) { - const owner = liveSocket2.owner(el); - js_default.show( - eventType, - owner, - el, - opts.display, - js_default.transitionClasses(opts.transition), - opts.time, - opts.blocking - ); - }, - hide(el, opts = {}) { - const owner = liveSocket2.owner(el); - js_default.hide( - eventType, - owner, - el, - null, - js_default.transitionClasses(opts.transition), - opts.time, - opts.blocking - ); - }, - toggle(el, opts = {}) { - const owner = liveSocket2.owner(el); - const inTransition = js_default.transitionClasses(opts.in); - const outTransition = js_default.transitionClasses(opts.out); - js_default.toggle( - eventType, - owner, - el, - opts.display, - inTransition, - outTransition, - opts.time, - opts.blocking - ); - }, - addClass(el, names, opts = {}) { - const classNames = Array.isArray(names) ? names : names.split(" "); - const owner = liveSocket2.owner(el); - js_default.addOrRemoveClasses( - el, - classNames, - [], - js_default.transitionClasses(opts.transition), - opts.time, - owner, - opts.blocking - ); - }, - removeClass(el, names, opts = {}) { - const classNames = Array.isArray(names) ? names : names.split(" "); - const owner = liveSocket2.owner(el); - js_default.addOrRemoveClasses( - el, - [], - classNames, - js_default.transitionClasses(opts.transition), - opts.time, - owner, - opts.blocking - ); - }, - toggleClass(el, names, opts = {}) { - const classNames = Array.isArray(names) ? names : names.split(" "); - const owner = liveSocket2.owner(el); - js_default.toggleClasses( - el, - classNames, - js_default.transitionClasses(opts.transition), - opts.time, - owner, - opts.blocking - ); - }, - transition(el, transition, opts = {}) { - const owner = liveSocket2.owner(el); - js_default.addOrRemoveClasses( - el, - [], - [], - js_default.transitionClasses(transition), - opts.time, - owner, - opts.blocking - ); - }, - setAttribute(el, attr, val) { - js_default.setOrRemoveAttrs(el, [[attr, val]], []); - }, - removeAttribute(el, attr) { - js_default.setOrRemoveAttrs(el, [], [attr]); - }, - toggleAttribute(el, attr, val1, val2) { - js_default.toggleAttr(el, attr, val1, val2); - }, - push(el, type, opts = {}) { - liveSocket2.withinOwners(el, (view) => { - const data = opts.value || {}; - delete opts.value; - let e = new CustomEvent("phx:exec", { detail: { sourceElement: el } }); - js_default.exec(e, eventType, type, view, el, ["push", __spreadValues({ data }, opts)]); - }); - }, - navigate(href, opts = {}) { - const customEvent = new CustomEvent("phx:exec"); - liveSocket2.historyRedirect( - customEvent, - href, - opts.replace ? "replace" : "push", - null, - null - ); - }, - patch(href, opts = {}) { - const customEvent = new CustomEvent("phx:exec"); - liveSocket2.pushHistoryPatch( - customEvent, - href, - opts.replace ? "replace" : "push", - null - ); - }, - ignoreAttributes(el, attrs) { - js_default.ignoreAttrs(el, Array.isArray(attrs) ? attrs : [attrs]); - } - }; - }; - var HOOK_ID = "hookId"; - var viewHookID = 1; - var ViewHook = class _ViewHook { - static makeID() { - return viewHookID++; - } - static elementID(el) { - return dom_default.private(el, HOOK_ID); - } - constructor(view, el, callbacks) { - this.el = el; - this.__attachView(view); - this.__listeners = /* @__PURE__ */ new Set(); - this.__isDisconnected = false; - dom_default.putPrivate(this.el, HOOK_ID, _ViewHook.makeID()); - if (callbacks) { - const protectedProps = /* @__PURE__ */ new Set([ - "el", - "liveSocket", - "__view", - "__listeners", - "__isDisconnected", - "constructor", - // Standard object properties - // Core ViewHook API methods - "js", - "pushEvent", - "pushEventTo", - "handleEvent", - "removeHandleEvent", - "upload", - "uploadTo", - // Internal lifecycle callers - "__mounted", - "__updated", - "__beforeUpdate", - "__destroyed", - "__reconnected", - "__disconnected", - "__cleanup__" - ]); - for (const key in callbacks) { - if (Object.prototype.hasOwnProperty.call(callbacks, key)) { - this[key] = callbacks[key]; - if (protectedProps.has(key)) { - console.warn( - `Hook object for element #${el.id} overwrites core property '${key}'!` - ); - } - } - } - const lifecycleMethods = [ - "mounted", - "beforeUpdate", - "updated", - "destroyed", - "disconnected", - "reconnected" - ]; - lifecycleMethods.forEach((methodName) => { - if (callbacks[methodName] && typeof callbacks[methodName] === "function") { - this[methodName] = callbacks[methodName]; - } - }); - } - } - /** @internal */ - __attachView(view) { - if (view) { - this.__view = () => view; - this.liveSocket = view.liveSocket; - } else { - this.__view = () => { - throw new Error( - `hook not yet attached to a live view: ${this.el.outerHTML}` - ); - }; - this.liveSocket = null; - } - } - // Default lifecycle methods - mounted() { - } - beforeUpdate() { - } - updated() { - } - destroyed() { - } - disconnected() { - } - reconnected() { - } - // Internal lifecycle callers - called by the View - /** @internal */ - __mounted() { - this.mounted(); - } - /** @internal */ - __updated() { - this.updated(); - } - /** @internal */ - __beforeUpdate() { - this.beforeUpdate(); - } - /** @internal */ - __destroyed() { - this.destroyed(); - dom_default.deletePrivate(this.el, HOOK_ID); - } - /** @internal */ - __reconnected() { - if (this.__isDisconnected) { - this.__isDisconnected = false; - this.reconnected(); - } - } - /** @internal */ - __disconnected() { - this.__isDisconnected = true; - this.disconnected(); - } - js() { - return __spreadProps(__spreadValues({}, js_commands_default(this.__view().liveSocket, "hook")), { - exec: (encodedJS) => { - this.__view().liveSocket.execJS(this.el, encodedJS, "hook"); - } - }); - } - pushEvent(event, payload, onReply) { - const promise = this.__view().pushHookEvent( - this.el, - null, - event, - payload || {} - ); - if (onReply === void 0) { - return promise.then(({ reply }) => reply); - } - promise.then(({ reply, ref }) => onReply(reply, ref)).catch(() => { - }); - return; - } - pushEventTo(selectorOrTarget, event, payload, onReply) { - if (onReply === void 0) { - const targetPair = []; - this.__view().withinTargets(selectorOrTarget, (view, targetCtx) => { - targetPair.push({ view, targetCtx }); - }); - const promises = targetPair.map(({ view, targetCtx }) => { - return view.pushHookEvent(this.el, targetCtx, event, payload || {}); - }); - return Promise.allSettled(promises); - } - this.__view().withinTargets(selectorOrTarget, (view, targetCtx) => { - view.pushHookEvent(this.el, targetCtx, event, payload || {}).then(({ reply, ref }) => onReply(reply, ref)).catch(() => { - }); - }); - return; - } - handleEvent(event, callback) { - const callbackRef = { - event, - callback: (customEvent) => callback(customEvent.detail) - }; - window.addEventListener( - `phx:${event}`, - callbackRef.callback - ); - this.__listeners.add(callbackRef); - return callbackRef; - } - removeHandleEvent(ref) { - window.removeEventListener( - `phx:${ref.event}`, - ref.callback - ); - this.__listeners.delete(ref); - } - upload(name, files) { - return this.__view().dispatchUploads(null, name, files); - } - uploadTo(selectorOrTarget, name, files) { - return this.__view().withinTargets(selectorOrTarget, (view, targetCtx) => { - view.dispatchUploads(targetCtx, name, files); - }); - } - /** @internal */ - __cleanup__() { - this.__listeners.forEach( - (callbackRef) => this.removeHandleEvent(callbackRef) - ); - } - }; - var prependFormDataKey = (key, prefix) => { - const isArray = key.endsWith("[]"); - let baseKey = isArray ? key.slice(0, -2) : key; - baseKey = baseKey.replace(/([^\[\]]+)(\]?$)/, `${prefix}$1$2`); - if (isArray) { - baseKey += "[]"; - } - return baseKey; - }; - var serializeForm = (form, opts, onlyNames = []) => { - const { submitter } = opts; - let injectedElement; - if (submitter && submitter.name) { - const input = document.createElement("input"); - input.type = "hidden"; - const formId = submitter.getAttribute("form"); - if (formId) { - input.setAttribute("form", formId); - } - input.name = submitter.name; - input.value = submitter.value; - submitter.parentElement.insertBefore(input, submitter); - injectedElement = input; - } - const formData = new FormData(form); - const toRemove = []; - formData.forEach((val, key, _index) => { - if (val instanceof File) { - toRemove.push(key); - } - }); - toRemove.forEach((key) => formData.delete(key)); - const params2 = new URLSearchParams(); - const { inputsUnused, onlyHiddenInputs } = Array.from(form.elements).reduce( - (acc, input) => { - const { inputsUnused: inputsUnused2, onlyHiddenInputs: onlyHiddenInputs2 } = acc; - const key = input.name; - if (!key) { - return acc; - } - if (inputsUnused2[key] === void 0) { - inputsUnused2[key] = true; - } - if (onlyHiddenInputs2[key] === void 0) { - onlyHiddenInputs2[key] = true; - } - const isUsed = dom_default.private(input, PHX_HAS_FOCUSED) || dom_default.private(input, PHX_HAS_SUBMITTED); - const isHidden = input.type === "hidden"; - inputsUnused2[key] = inputsUnused2[key] && !isUsed; - onlyHiddenInputs2[key] = onlyHiddenInputs2[key] && isHidden; - return acc; - }, - { inputsUnused: {}, onlyHiddenInputs: {} } - ); - for (const [key, val] of formData.entries()) { - if (onlyNames.length === 0 || onlyNames.indexOf(key) >= 0) { - const isUnused = inputsUnused[key]; - const hidden = onlyHiddenInputs[key]; - if (isUnused && !(submitter && submitter.name == key) && !hidden) { - params2.append(prependFormDataKey(key, "_unused_"), ""); - } - if (typeof val === "string") { - params2.append(key, val); - } - } - } - if (submitter && injectedElement) { - submitter.parentElement.removeChild(injectedElement); - } - return params2.toString(); - }; - var View = class _View { - static closestView(el) { - const liveViewEl = el.closest(PHX_VIEW_SELECTOR); - return liveViewEl ? dom_default.private(liveViewEl, "view") : null; - } - constructor(el, liveSocket2, parentView, flash, liveReferer) { - this.isDead = false; - this.liveSocket = liveSocket2; - this.flash = flash; - this.parent = parentView; - this.root = parentView ? parentView.root : this; - this.el = el; - const boundView = dom_default.private(this.el, "view"); - if (boundView !== void 0 && boundView.isDead !== true) { - logError( - `The DOM element for this view has already been bound to a view. - - An element can only ever be associated with a single view! - Please ensure that you are not trying to initialize multiple LiveSockets on the same page. - This could happen if you're accidentally trying to render your root layout more than once. - Ensure that the template set on the LiveView is different than the root layout. - `, - { view: boundView } - ); - throw new Error("Cannot bind multiple views to the same DOM element."); - } - dom_default.putPrivate(this.el, "view", this); - this.id = this.el.id; - this.ref = 0; - this.lastAckRef = null; - this.childJoins = 0; - this.loaderTimer = null; - this.disconnectedTimer = null; - this.pendingDiffs = []; - this.pendingForms = /* @__PURE__ */ new Set(); - this.redirect = false; - this.href = null; - this.joinCount = this.parent ? this.parent.joinCount - 1 : 0; - this.joinAttempts = 0; - this.joinPending = true; - this.destroyed = false; - this.joinCallback = function(onDone) { - onDone && onDone(); - }; - this.stopCallback = function() { - }; - this.pendingJoinOps = this.parent ? null : []; - this.viewHooks = {}; - this.formSubmits = []; - this.children = this.parent ? null : {}; - this.root.children[this.id] = {}; - this.formsForRecovery = {}; - this.channel = this.liveSocket.channel(`lv:${this.id}`, () => { - const url = this.href && this.expandURL(this.href); - return { - redirect: this.redirect ? url : void 0, - url: this.redirect ? void 0 : url || void 0, - params: this.connectParams(liveReferer), - session: this.getSession(), - static: this.getStatic(), - flash: this.flash, - sticky: this.el.hasAttribute(PHX_STICKY) - }; - }); - this.portalElementIds = /* @__PURE__ */ new Set(); - } - setHref(href) { - this.href = href; - } - setRedirect(href) { - this.redirect = true; - this.href = href; - } - isMain() { - return this.el.hasAttribute(PHX_MAIN); - } - connectParams(liveReferer) { - const params2 = this.liveSocket.params(this.el); - const manifest = dom_default.all(document, `[${this.binding(PHX_TRACK_STATIC)}]`).map((node) => node.src || node.href).filter((url) => typeof url === "string"); - if (manifest.length > 0) { - params2["_track_static"] = manifest; - } - params2["_mounts"] = this.joinCount; - params2["_mount_attempts"] = this.joinAttempts; - params2["_live_referer"] = liveReferer; - this.joinAttempts++; - return params2; - } - isConnected() { - return this.channel.canPush(); - } - getSession() { - return this.el.getAttribute(PHX_SESSION); - } - getStatic() { - const val = this.el.getAttribute(PHX_STATIC); - return val === "" ? null : val; - } - destroy(callback = function() { - }) { - this.destroyAllChildren(); - this.destroyPortalElements(); - this.destroyed = true; - dom_default.deletePrivate(this.el, "view"); - delete this.root.children[this.id]; - if (this.parent) { - delete this.root.children[this.parent.id][this.id]; - } - clearTimeout(this.loaderTimer); - const onFinished = () => { - callback(); - for (const id in this.viewHooks) { - this.destroyHook(this.viewHooks[id]); - } - }; - dom_default.markPhxChildDestroyed(this.el); - this.log("destroyed", () => ["the child has been removed from the parent"]); - this.channel.leave().receive("ok", onFinished).receive("error", onFinished).receive("timeout", onFinished); - } - setContainerClasses(...classes) { - this.el.classList.remove( - PHX_CONNECTED_CLASS, - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_CLIENT_ERROR_CLASS, - PHX_SERVER_ERROR_CLASS - ); - this.el.classList.add(...classes); - } - showLoader(timeout) { - clearTimeout(this.loaderTimer); - if (timeout) { - this.loaderTimer = setTimeout(() => this.showLoader(), timeout); - } else { - for (const id in this.viewHooks) { - this.viewHooks[id].__disconnected(); - } - this.setContainerClasses(PHX_LOADING_CLASS); - } - } - execAll(binding) { - dom_default.all( - this.el, - `[${binding}]`, - (el) => this.liveSocket.execJS(el, el.getAttribute(binding)) - ); - } - hideLoader() { - clearTimeout(this.loaderTimer); - clearTimeout(this.disconnectedTimer); - this.setContainerClasses(PHX_CONNECTED_CLASS); - this.execAll(this.binding("connected")); - } - triggerReconnected() { - for (const id in this.viewHooks) { - this.viewHooks[id].__reconnected(); - } - } - log(kind, msgCallback) { - this.liveSocket.log(this, kind, msgCallback); - } - transition(time, onStart, onDone = function() { - }) { - this.liveSocket.transition(time, onStart, onDone); - } - // calls the callback with the view and target element for the given phxTarget - // targets can be: - // * an element itself, then it is simply passed to liveSocket.owner; - // * a CID (Component ID), then we first search the component's element in the DOM - // * a selector, then we search the selector in the DOM and call the callback - // for each element found with the corresponding owner view - withinTargets(phxTarget, callback, dom = document) { - if (phxTarget instanceof HTMLElement || phxTarget instanceof SVGElement) { - return this.liveSocket.owner( - phxTarget, - (view) => callback(view, phxTarget) - ); - } - if (isCid(phxTarget)) { - const targets = dom_default.findComponentNodeList(this.id, phxTarget, dom); - if (targets.length === 0) { - logError(`no component found matching phx-target of ${phxTarget}`); - } else { - callback(this, parseInt(phxTarget)); - } - } else { - const targets = Array.from(dom.querySelectorAll(phxTarget)); - if (targets.length === 0) { - logError( - `nothing found matching the phx-target selector "${phxTarget}"` - ); - } - targets.forEach( - (target) => this.liveSocket.owner(target, (view) => callback(view, target)) - ); - } - } - applyDiff(type, rawDiff, callback) { - this.log(type, () => ["", clone(rawDiff)]); - const { diff, reply, events, title } = Rendered.extract(rawDiff); - callback({ diff, reply, events }); - if (typeof title === "string" || type == "mount") { - window.requestAnimationFrame(() => dom_default.putTitle(title)); - } - } - onJoin(resp) { - const { rendered, container, liveview_version } = resp; - if (container) { - const [tag, attrs] = container; - this.el = dom_default.replaceRootContainer(this.el, tag, attrs); - } - this.childJoins = 0; - this.joinPending = true; - this.flash = null; - if (this.root === this) { - this.formsForRecovery = this.getFormsForRecovery(); - } - if (this.isMain() && window.history.state === null) { - browser_default.pushState("replace", { - type: "patch", - id: this.id, - position: this.liveSocket.currentHistoryPosition - }); - } - if (liveview_version !== this.liveSocket.version()) { - console.error( - `LiveView asset version mismatch. JavaScript version ${this.liveSocket.version()} vs. server ${liveview_version}. To avoid issues, please ensure that your assets use the same version as the server.` - ); - } - browser_default.dropLocal( - this.liveSocket.localStorage, - window.location.pathname, - CONSECUTIVE_RELOADS - ); - this.applyDiff("mount", rendered, ({ diff, events }) => { - this.rendered = new Rendered(this.id, diff); - const [html, streams] = this.renderContainer(null, "join"); - this.dropPendingRefs(); - this.joinCount++; - this.joinAttempts = 0; - this.maybeRecoverForms(html, () => { - this.onJoinComplete(resp, html, streams, events); - }); - }); - } - dropPendingRefs() { - dom_default.all(document, `[${PHX_REF_SRC}="${this.refSrc()}"]`, (el) => { - el.removeAttribute(PHX_REF_LOADING); - el.removeAttribute(PHX_REF_SRC); - el.removeAttribute(PHX_REF_LOCK); - }); - } - onJoinComplete({ live_patch }, html, streams, events) { - if (this.joinCount > 1 || this.parent && !this.parent.isJoinPending()) { - return this.applyJoinPatch(live_patch, html, streams, events); - } - const newChildren = dom_default.findPhxChildrenInFragment(html, this.id).filter( - (toEl) => { - const fromEl = toEl.id && this.el.querySelector(`[id="${toEl.id}"]`); - const phxStatic = fromEl && fromEl.getAttribute(PHX_STATIC); - if (phxStatic) { - toEl.setAttribute(PHX_STATIC, phxStatic); - } - if (fromEl) { - fromEl.setAttribute(PHX_ROOT_ID, this.root.id); - } - return this.joinChild(toEl); - } - ); - if (newChildren.length === 0) { - if (this.parent) { - this.root.pendingJoinOps.push([ - this, - () => this.applyJoinPatch(live_patch, html, streams, events) - ]); - this.parent.ackJoin(this); - } else { - this.onAllChildJoinsComplete(); - this.applyJoinPatch(live_patch, html, streams, events); - } - } else { - this.root.pendingJoinOps.push([ - this, - () => this.applyJoinPatch(live_patch, html, streams, events) - ]); - } - } - attachTrueDocEl() { - this.el = dom_default.byId(this.id); - this.el.setAttribute(PHX_ROOT_ID, this.root.id); - } - // this is invoked for dead and live views, so we must filter by - // by owner to ensure we aren't duplicating hooks across disconnect - // and connected states. This also handles cases where hooks exist - // in a root layout with a LV in the body - execNewMounted(parent = document) { - let phxViewportTop = this.binding(PHX_VIEWPORT_TOP); - let phxViewportBottom = this.binding(PHX_VIEWPORT_BOTTOM); - this.all( - parent, - `[${phxViewportTop}], [${phxViewportBottom}]`, - (hookEl) => { - dom_default.maintainPrivateHooks( - hookEl, - hookEl, - phxViewportTop, - phxViewportBottom - ); - this.maybeAddNewHook(hookEl); - } - ); - this.all( - parent, - `[${this.binding(PHX_HOOK)}], [data-phx-${PHX_HOOK}]`, - (hookEl) => { - this.maybeAddNewHook(hookEl); - } - ); - this.all(parent, `[${this.binding(PHX_MOUNTED)}]`, (el) => { - this.maybeMounted(el); - }); - } - all(parent, selector, callback) { - dom_default.all(parent, selector, (el) => { - if (this.ownsElement(el)) { - callback(el); - } - }); - } - applyJoinPatch(live_patch, html, streams, events) { - this.attachTrueDocEl(); - const patch = new DOMPatch(this, this.el, this.id, html, streams, null); - patch.markPrunableContentForRemoval(); - this.performPatch(patch, false, true); - this.joinNewChildren(); - this.execNewMounted(); - this.joinPending = false; - this.liveSocket.dispatchEvents(events); - this.applyPendingUpdates(); - if (live_patch) { - const { kind, to } = live_patch; - this.liveSocket.historyPatch(to, kind); - } - this.hideLoader(); - if (this.joinCount > 1) { - this.triggerReconnected(); - } - this.stopCallback(); - } - triggerBeforeUpdateHook(fromEl, toEl) { - this.liveSocket.triggerDOM("onBeforeElUpdated", [fromEl, toEl]); - const hook = this.getHook(fromEl); - const isIgnored = hook && dom_default.isIgnored(fromEl, this.binding(PHX_UPDATE)); - if (hook && !fromEl.isEqualNode(toEl) && !(isIgnored && isEqualObj(fromEl.dataset, toEl.dataset))) { - hook.__beforeUpdate(); - return hook; - } - } - maybeMounted(el) { - const phxMounted = el.getAttribute(this.binding(PHX_MOUNTED)); - const hasBeenInvoked = phxMounted && dom_default.private(el, "mounted"); - if (phxMounted && !hasBeenInvoked) { - this.liveSocket.execJS(el, phxMounted); - dom_default.putPrivate(el, "mounted", true); - } - } - maybeAddNewHook(el) { - const newHook = this.addHook(el); - if (newHook) { - newHook.__mounted(); - } - } - performPatch(patch, pruneCids, isJoinPatch = false) { - const removedEls = []; - let phxChildrenAdded = false; - const updatedHookIds = /* @__PURE__ */ new Set(); - this.liveSocket.triggerDOM("onPatchStart", [patch.targetContainer]); - patch.after("added", (el) => { - this.liveSocket.triggerDOM("onNodeAdded", [el]); - const phxViewportTop = this.binding(PHX_VIEWPORT_TOP); - const phxViewportBottom = this.binding(PHX_VIEWPORT_BOTTOM); - dom_default.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom); - this.maybeAddNewHook(el); - if (el.getAttribute) { - this.maybeMounted(el); - } - }); - patch.after("phxChildAdded", (el) => { - if (dom_default.isPhxSticky(el)) { - this.liveSocket.joinRootViews(); - } else { - phxChildrenAdded = true; - } - }); - patch.before("updated", (fromEl, toEl) => { - const hook = this.triggerBeforeUpdateHook(fromEl, toEl); - if (hook) { - updatedHookIds.add(fromEl.id); - } - js_default.onBeforeElUpdated(fromEl, toEl); - }); - patch.after("updated", (el) => { - if (updatedHookIds.has(el.id)) { - this.getHook(el).__updated(); - } - }); - patch.after("discarded", (el) => { - if (el.nodeType === Node.ELEMENT_NODE) { - removedEls.push(el); - } - }); - patch.after( - "transitionsDiscarded", - (els) => this.afterElementsRemoved(els, pruneCids) - ); - patch.perform(isJoinPatch); - this.afterElementsRemoved(removedEls, pruneCids); - this.liveSocket.triggerDOM("onPatchEnd", [patch.targetContainer]); - return phxChildrenAdded; - } - afterElementsRemoved(elements, pruneCids) { - const destroyedCIDs = []; - elements.forEach((parent) => { - const components = dom_default.all(parent, `[${PHX_COMPONENT}]`); - const hooks = dom_default.all( - parent, - `[${this.binding(PHX_HOOK)}], [data-phx-hook]` - ); - components.concat(parent).forEach((el) => { - const cid = this.componentID(el); - if (isCid(cid) && destroyedCIDs.indexOf(cid) === -1) { - destroyedCIDs.push(cid); - } - }); - hooks.concat(parent).forEach((hookEl) => { - const hook = this.getHook(hookEl); - hook && this.destroyHook(hook); - }); - }); - if (pruneCids) { - this.maybePushComponentsDestroyed(destroyedCIDs); - } - } - joinNewChildren() { - dom_default.findPhxChildren(document, this.id).forEach((el) => this.joinChild(el)); - } - maybeRecoverForms(html, callback) { - const phxChange = this.binding("change"); - const oldForms = this.root.formsForRecovery; - const template = document.createElement("template"); - template.innerHTML = html; - const rootEl = template.content.firstElementChild; - rootEl.id = this.id; - rootEl.setAttribute(PHX_ROOT_ID, this.root.id); - rootEl.setAttribute(PHX_SESSION, this.getSession()); - rootEl.setAttribute(PHX_STATIC, this.getStatic()); - rootEl.setAttribute(PHX_PARENT_ID, this.parent ? this.parent.id : null); - const formsToRecover = ( - // we go over all forms in the new DOM; because this is only the HTML for the current - // view, we can be sure that all forms are owned by this view: - dom_default.all(template.content, "form").filter((newForm) => newForm.id && oldForms[newForm.id]).filter((newForm) => !this.pendingForms.has(newForm.id)).filter( - (newForm) => oldForms[newForm.id].getAttribute(phxChange) === newForm.getAttribute(phxChange) - ).map((newForm) => { - return [oldForms[newForm.id], newForm]; - }) - ); - if (formsToRecover.length === 0) { - return callback(); - } - formsToRecover.forEach(([oldForm, newForm], i) => { - this.pendingForms.add(newForm.id); - this.pushFormRecovery( - oldForm, - newForm, - template.content.firstElementChild, - () => { - this.pendingForms.delete(newForm.id); - if (i === formsToRecover.length - 1) { - callback(); - } - } - ); - }); - } - getChildById(id) { - return this.root.children[this.id][id]; - } - getDescendentByEl(el) { - var _a; - if (el.id === this.id) { - return this; - } else { - return (_a = this.children[el.getAttribute(PHX_PARENT_ID)]) == null ? void 0 : _a[el.id]; - } - } - destroyDescendent(id) { - for (const parentId in this.root.children) { - for (const childId in this.root.children[parentId]) { - if (childId === id) { - return this.root.children[parentId][childId].destroy(); - } - } - } - } - joinChild(el) { - const child = this.getChildById(el.id); - if (!child) { - const view = new _View(el, this.liveSocket, this); - this.root.children[this.id][view.id] = view; - view.join(); - this.childJoins++; - return true; - } - } - isJoinPending() { - return this.joinPending; - } - ackJoin(_child) { - this.childJoins--; - if (this.childJoins === 0) { - if (this.parent) { - this.parent.ackJoin(this); - } else { - this.onAllChildJoinsComplete(); - } - } - } - onAllChildJoinsComplete() { - this.pendingForms.clear(); - this.formsForRecovery = {}; - this.joinCallback(() => { - this.pendingJoinOps.forEach(([view, op]) => { - if (!view.isDestroyed()) { - op(); - } - }); - this.pendingJoinOps = []; - }); - } - update(diff, events) { - if (this.isJoinPending() || this.liveSocket.hasPendingLink() && this.root.isMain()) { - return this.pendingDiffs.push({ diff, events }); - } - this.rendered.mergeDiff(diff); - let phxChildrenAdded = false; - if (this.rendered.isComponentOnlyDiff(diff)) { - this.liveSocket.time("component patch complete", () => { - const parentCids = dom_default.findExistingParentCIDs( - this.id, - this.rendered.componentCIDs(diff) - ); - parentCids.forEach((parentCID) => { - if (this.componentPatch( - this.rendered.getComponent(diff, parentCID), - parentCID - )) { - phxChildrenAdded = true; - } - }); - }); - } else if (!isEmpty(diff)) { - this.liveSocket.time("full patch complete", () => { - const [html, streams] = this.renderContainer(diff, "update"); - const patch = new DOMPatch(this, this.el, this.id, html, streams, null); - phxChildrenAdded = this.performPatch(patch, true); - }); - } - this.liveSocket.dispatchEvents(events); - if (phxChildrenAdded) { - this.joinNewChildren(); - } - } - renderContainer(diff, kind) { - return this.liveSocket.time(`toString diff (${kind})`, () => { - const tag = this.el.tagName; - const cids = diff ? this.rendered.componentCIDs(diff) : null; - const { buffer: html, streams } = this.rendered.toString(cids); - return [`<${tag}>${html}`, streams]; - }); - } - componentPatch(diff, cid) { - if (isEmpty(diff)) - return false; - const { buffer: html, streams } = this.rendered.componentToString(cid); - const patch = new DOMPatch(this, this.el, this.id, html, streams, cid); - const childrenAdded = this.performPatch(patch, true); - return childrenAdded; - } - getHook(el) { - return this.viewHooks[ViewHook.elementID(el)]; - } - addHook(el) { - const hookElId = ViewHook.elementID(el); - if (el.getAttribute && !this.ownsElement(el)) { - return; - } - if (hookElId && !this.viewHooks[hookElId]) { - const hook = dom_default.getCustomElHook(el) || logError(`no hook found for custom element: ${el.id}`); - this.viewHooks[hookElId] = hook; - hook.__attachView(this); - return hook; - } else if (hookElId || !el.getAttribute) { - return; - } else { - const hookName = el.getAttribute(`data-phx-${PHX_HOOK}`) || el.getAttribute(this.binding(PHX_HOOK)); - if (!hookName) { - return; - } - const hookDefinition = this.liveSocket.getHookDefinition(hookName); - if (hookDefinition) { - if (!el.id) { - logError( - `no DOM ID for hook "${hookName}". Hooks require a unique ID on each element.`, - el - ); - return; - } - let hookInstance; - try { - if (typeof hookDefinition === "function" && hookDefinition.prototype instanceof ViewHook) { - hookInstance = new hookDefinition(this, el); - } else if (typeof hookDefinition === "object" && hookDefinition !== null) { - hookInstance = new ViewHook(this, el, hookDefinition); - } else { - logError( - `Invalid hook definition for "${hookName}". Expected a class extending ViewHook or an object definition.`, - el - ); - return; - } - } catch (e) { - const errorMessage = e instanceof Error ? e.message : String(e); - logError(`Failed to create hook "${hookName}": ${errorMessage}`, el); - return; - } - this.viewHooks[ViewHook.elementID(hookInstance.el)] = hookInstance; - return hookInstance; - } else if (hookName !== null) { - logError(`unknown hook found for "${hookName}"`, el); - } - } - } - destroyHook(hook) { - const hookId = ViewHook.elementID(hook.el); - hook.__destroyed(); - hook.__cleanup__(); - delete this.viewHooks[hookId]; - } - applyPendingUpdates() { - if (this.liveSocket.hasPendingLink() && this.root.isMain()) { - return; - } - this.pendingDiffs.forEach(({ diff, events }) => this.update(diff, events)); - this.pendingDiffs = []; - this.eachChild((child) => child.applyPendingUpdates()); - } - eachChild(callback) { - const children = this.root.children[this.id] || {}; - for (const id in children) { - callback(this.getChildById(id)); - } - } - onChannel(event, cb) { - this.liveSocket.onChannel(this.channel, event, (resp) => { - if (this.isJoinPending()) { - this.root.pendingJoinOps.push([this, () => cb(resp)]); - } else { - this.liveSocket.requestDOMUpdate(() => cb(resp)); - } - }); - } - bindChannel() { - this.liveSocket.onChannel(this.channel, "diff", (rawDiff) => { - this.liveSocket.requestDOMUpdate(() => { - this.applyDiff( - "update", - rawDiff, - ({ diff, events }) => this.update(diff, events) - ); - }); - }); - this.onChannel( - "redirect", - ({ to, flash }) => this.onRedirect({ to, flash }) - ); - this.onChannel("live_patch", (redir) => this.onLivePatch(redir)); - this.onChannel("live_redirect", (redir) => this.onLiveRedirect(redir)); - this.channel.onError((reason) => this.onError(reason)); - this.channel.onClose((reason) => this.onClose(reason)); - } - destroyAllChildren() { - this.eachChild((child) => child.destroy()); - } - onLiveRedirect(redir) { - const { to, kind, flash } = redir; - const url = this.expandURL(to); - const e = new CustomEvent("phx:server-navigate", { - detail: { to, kind, flash } - }); - this.liveSocket.historyRedirect(e, url, kind, flash); - } - onLivePatch(redir) { - const { to, kind } = redir; - this.href = this.expandURL(to); - this.liveSocket.historyPatch(to, kind); - } - expandURL(to) { - return to.startsWith("/") ? `${window.location.protocol}//${window.location.host}${to}` : to; - } - /** - * @param {{to: string, flash?: string, reloadToken?: string}} redirect - */ - onRedirect({ to, flash, reloadToken }) { - this.liveSocket.redirect(to, flash, reloadToken); - } - isDestroyed() { - return this.destroyed; - } - joinDead() { - this.isDead = true; - } - joinPush() { - this.joinPush = this.joinPush || this.channel.join(); - return this.joinPush; - } - join(callback) { - this.showLoader(this.liveSocket.loaderTimeout); - this.bindChannel(); - if (this.isMain()) { - this.stopCallback = this.liveSocket.withPageLoading({ - to: this.href, - kind: "initial" - }); - } - this.joinCallback = (onDone) => { - onDone = onDone || function() { - }; - callback ? callback(this.joinCount, onDone) : onDone(); - }; - this.wrapPush(() => this.channel.join(), { - ok: (resp) => this.liveSocket.requestDOMUpdate(() => this.onJoin(resp)), - error: (error) => this.onJoinError(error), - timeout: () => this.onJoinError({ reason: "timeout" }) - }); - } - onJoinError(resp) { - if (resp.reason === "reload") { - this.log("error", () => [ - `failed mount with ${resp.status}. Falling back to page reload`, - resp - ]); - this.onRedirect({ to: this.root.href, reloadToken: resp.token }); - return; - } else if (resp.reason === "unauthorized" || resp.reason === "stale") { - this.log("error", () => [ - "unauthorized live_redirect. Falling back to page request", - resp - ]); - this.onRedirect({ to: this.root.href, flash: this.flash }); - return; - } - if (resp.redirect || resp.live_redirect) { - this.joinPending = false; - this.channel.leave(); - } - if (resp.redirect) { - return this.onRedirect(resp.redirect); - } - if (resp.live_redirect) { - return this.onLiveRedirect(resp.live_redirect); - } - this.log("error", () => ["unable to join", resp]); - if (this.isMain()) { - this.displayError([ - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_SERVER_ERROR_CLASS - ]); - if (this.liveSocket.isConnected()) { - this.liveSocket.reloadWithJitter(this); - } - } else { - if (this.joinAttempts >= MAX_CHILD_JOIN_ATTEMPTS) { - this.root.displayError([ - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_SERVER_ERROR_CLASS - ]); - this.log("error", () => [ - `giving up trying to mount after ${MAX_CHILD_JOIN_ATTEMPTS} tries`, - resp - ]); - this.destroy(); - } - const trueChildEl = dom_default.byId(this.el.id); - if (trueChildEl) { - dom_default.mergeAttrs(trueChildEl, this.el); - this.displayError([ - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_SERVER_ERROR_CLASS - ]); - this.el = trueChildEl; - } else { - this.destroy(); - } - } - } - onClose(reason) { - if (this.isDestroyed()) { - return; - } - if (this.isMain() && this.liveSocket.hasPendingLink() && reason !== "leave") { - return this.liveSocket.reloadWithJitter(this); - } - this.destroyAllChildren(); - this.liveSocket.dropActiveElement(this); - if (this.liveSocket.isUnloaded()) { - this.showLoader(BEFORE_UNLOAD_LOADER_TIMEOUT); - } - } - onError(reason) { - this.onClose(reason); - if (this.liveSocket.isConnected()) { - this.log("error", () => ["view crashed", reason]); - } - if (!this.liveSocket.isUnloaded()) { - if (this.liveSocket.isConnected()) { - this.displayError([ - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_SERVER_ERROR_CLASS - ]); - } else { - this.displayError([ - PHX_LOADING_CLASS, - PHX_ERROR_CLASS, - PHX_CLIENT_ERROR_CLASS - ]); - } - } - } - displayError(classes) { - if (this.isMain()) { - dom_default.dispatchEvent(window, "phx:page-loading-start", { - detail: { to: this.href, kind: "error" } - }); - } - this.showLoader(); - this.setContainerClasses(...classes); - this.delayedDisconnected(); - } - delayedDisconnected() { - this.disconnectedTimer = setTimeout(() => { - this.execAll(this.binding("disconnected")); - }, this.liveSocket.disconnectedTimeout); - } - wrapPush(callerPush, receives) { - const latency = this.liveSocket.getLatencySim(); - const withLatency = latency ? (cb) => setTimeout(() => !this.isDestroyed() && cb(), latency) : (cb) => !this.isDestroyed() && cb(); - withLatency(() => { - callerPush().receive( - "ok", - (resp) => withLatency(() => receives.ok && receives.ok(resp)) - ).receive( - "error", - (reason) => withLatency(() => receives.error && receives.error(reason)) - ).receive( - "timeout", - () => withLatency(() => receives.timeout && receives.timeout()) - ); - }); - } - pushWithReply(refGenerator, event, payload) { - if (!this.isConnected()) { - return Promise.reject(new Error("no connection")); - } - const [ref, [el], opts] = refGenerator ? refGenerator({ payload }) : [null, [], {}]; - const oldJoinCount = this.joinCount; - let onLoadingDone = function() { - }; - if (opts.page_loading) { - onLoadingDone = this.liveSocket.withPageLoading({ - kind: "element", - target: el - }); - } - if (typeof payload.cid !== "number") { - delete payload.cid; - } - return new Promise((resolve, reject) => { - this.wrapPush(() => this.channel.push(event, payload, PUSH_TIMEOUT), { - ok: (resp) => { - if (ref !== null) { - this.lastAckRef = ref; - } - const finish = (hookReply) => { - if (resp.redirect) { - this.onRedirect(resp.redirect); - } - if (resp.live_patch) { - this.onLivePatch(resp.live_patch); - } - if (resp.live_redirect) { - this.onLiveRedirect(resp.live_redirect); - } - onLoadingDone(); - resolve({ resp, reply: hookReply, ref }); - }; - if (resp.diff) { - this.liveSocket.requestDOMUpdate(() => { - this.applyDiff("update", resp.diff, ({ diff, reply, events }) => { - if (ref !== null) { - this.undoRefs(ref, payload.event); - } - this.update(diff, events); - finish(reply); - }); - }); - } else { - if (ref !== null) { - this.undoRefs(ref, payload.event); - } - finish(null); - } - }, - error: (reason) => reject(new Error(`failed with reason: ${reason}`)), - timeout: () => { - reject(new Error("timeout")); - if (this.joinCount === oldJoinCount) { - this.liveSocket.reloadWithJitter(this, () => { - this.log("timeout", () => [ - "received timeout while communicating with server. Falling back to hard refresh for recovery" - ]); - }); - } - } - }); - }); - } - undoRefs(ref, phxEvent, onlyEls) { - if (!this.isConnected()) { - return; - } - const selector = `[${PHX_REF_SRC}="${this.refSrc()}"]`; - if (onlyEls) { - onlyEls = new Set(onlyEls); - dom_default.all(document, selector, (parent) => { - if (onlyEls && !onlyEls.has(parent)) { - return; - } - dom_default.all( - parent, - selector, - (child) => this.undoElRef(child, ref, phxEvent) - ); - this.undoElRef(parent, ref, phxEvent); - }); - } else { - dom_default.all(document, selector, (el) => this.undoElRef(el, ref, phxEvent)); - } - } - undoElRef(el, ref, phxEvent) { - const elRef = new ElementRef(el); - elRef.maybeUndo(ref, phxEvent, (clonedTree) => { - const patch = new DOMPatch(this, el, this.id, clonedTree, [], null, { - undoRef: ref - }); - const phxChildrenAdded = this.performPatch(patch, true); - dom_default.all( - el, - `[${PHX_REF_SRC}="${this.refSrc()}"]`, - (child) => this.undoElRef(child, ref, phxEvent) - ); - if (phxChildrenAdded) { - this.joinNewChildren(); - } - }); - } - refSrc() { - return this.el.id; - } - putRef(elements, phxEvent, eventType, opts = {}) { - const newRef = this.ref++; - const disableWith = this.binding(PHX_DISABLE_WITH); - if (opts.loading) { - const loadingEls = dom_default.all(document, opts.loading).map((el) => { - return { el, lock: true, loading: true }; - }); - elements = elements.concat(loadingEls); - } - for (const { el, lock, loading } of elements) { - if (!lock && !loading) { - throw new Error("putRef requires lock or loading"); - } - el.setAttribute(PHX_REF_SRC, this.refSrc()); - if (loading) { - el.setAttribute(PHX_REF_LOADING, newRef); - } - if (lock) { - el.setAttribute(PHX_REF_LOCK, newRef); - } - if (!loading || opts.submitter && !(el === opts.submitter || el === opts.form)) { - continue; - } - const lockCompletePromise = new Promise((resolve) => { - el.addEventListener(`phx:undo-lock:${newRef}`, () => resolve(detail), { - once: true - }); - }); - const loadingCompletePromise = new Promise((resolve) => { - el.addEventListener( - `phx:undo-loading:${newRef}`, - () => resolve(detail), - { once: true } - ); - }); - el.classList.add(`phx-${eventType}-loading`); - const disableText = el.getAttribute(disableWith); - if (disableText !== null) { - if (!el.getAttribute(PHX_DISABLE_WITH_RESTORE)) { - el.setAttribute(PHX_DISABLE_WITH_RESTORE, el.innerText); - } - if (disableText !== "") { - el.innerText = disableText; - } - el.setAttribute( - PHX_DISABLED, - el.getAttribute(PHX_DISABLED) || el.disabled - ); - el.setAttribute("disabled", ""); - } - const detail = { - event: phxEvent, - eventType, - ref: newRef, - isLoading: loading, - isLocked: lock, - lockElements: elements.filter(({ lock: lock2 }) => lock2).map(({ el: el2 }) => el2), - loadingElements: elements.filter(({ loading: loading2 }) => loading2).map(({ el: el2 }) => el2), - unlock: (els) => { - els = Array.isArray(els) ? els : [els]; - this.undoRefs(newRef, phxEvent, els); - }, - lockComplete: lockCompletePromise, - loadingComplete: loadingCompletePromise, - lock: (lockEl) => { - return new Promise((resolve) => { - if (this.isAcked(newRef)) { - return resolve(detail); - } - lockEl.setAttribute(PHX_REF_LOCK, newRef); - lockEl.setAttribute(PHX_REF_SRC, this.refSrc()); - lockEl.addEventListener( - `phx:lock-stop:${newRef}`, - () => resolve(detail), - { once: true } - ); - }); - } - }; - if (opts.payload) { - detail["payload"] = opts.payload; - } - if (opts.target) { - detail["target"] = opts.target; - } - if (opts.originalEvent) { - detail["originalEvent"] = opts.originalEvent; - } - el.dispatchEvent( - new CustomEvent("phx:push", { - detail, - bubbles: true, - cancelable: false - }) - ); - if (phxEvent) { - el.dispatchEvent( - new CustomEvent(`phx:push:${phxEvent}`, { - detail, - bubbles: true, - cancelable: false - }) - ); - } - } - return [newRef, elements.map(({ el }) => el), opts]; - } - isAcked(ref) { - return this.lastAckRef !== null && this.lastAckRef >= ref; - } - componentID(el) { - const cid = el.getAttribute && el.getAttribute(PHX_COMPONENT); - return cid ? parseInt(cid) : null; - } - targetComponentID(target, targetCtx, opts = {}) { - if (isCid(targetCtx)) { - return targetCtx; - } - const cidOrSelector = opts.target || target.getAttribute(this.binding("target")); - if (isCid(cidOrSelector)) { - return parseInt(cidOrSelector); - } else if (targetCtx && (cidOrSelector !== null || opts.target)) { - return this.closestComponentID(targetCtx); - } else { - return null; - } - } - closestComponentID(targetCtx) { - if (isCid(targetCtx)) { - return targetCtx; - } else if (targetCtx) { - return maybe( - targetCtx.closest(`[${PHX_COMPONENT}]`), - (el) => this.ownsElement(el) && this.componentID(el) - ); - } else { - return null; - } - } - pushHookEvent(el, targetCtx, event, payload) { - if (!this.isConnected()) { - this.log("hook", () => [ - "unable to push hook event. LiveView not connected", - event, - payload - ]); - return Promise.reject( - new Error("unable to push hook event. LiveView not connected") - ); - } - const refGenerator = () => this.putRef([{ el, loading: true, lock: true }], event, "hook", { - payload, - target: targetCtx - }); - return this.pushWithReply(refGenerator, "event", { - type: "hook", - event, - value: payload, - cid: this.closestComponentID(targetCtx) - }).then(({ resp: _resp, reply, ref }) => ({ reply, ref })); - } - extractMeta(el, meta, value) { - const prefix = this.binding("value-"); - for (let i = 0; i < el.attributes.length; i++) { - if (!meta) { - meta = {}; - } - const name = el.attributes[i].name; - if (name.startsWith(prefix)) { - meta[name.replace(prefix, "")] = el.getAttribute(name); - } - } - if (el.value !== void 0 && !(el instanceof HTMLFormElement)) { - if (!meta) { - meta = {}; - } - meta.value = el.value; - if (el.tagName === "INPUT" && CHECKABLE_INPUTS.indexOf(el.type) >= 0 && !el.checked) { - delete meta.value; - } - } - if (value) { - if (!meta) { - meta = {}; - } - for (const key in value) { - meta[key] = value[key]; - } - } - return meta; - } - pushEvent(type, el, targetCtx, phxEvent, meta, opts = {}, onReply) { - this.pushWithReply( - (maybePayload) => this.putRef([{ el, loading: true, lock: true }], phxEvent, type, __spreadProps(__spreadValues({}, opts), { - payload: maybePayload == null ? void 0 : maybePayload.payload - })), - "event", - { - type, - event: phxEvent, - value: this.extractMeta(el, meta, opts.value), - cid: this.targetComponentID(el, targetCtx, opts) - } - ).then(({ reply }) => onReply && onReply(reply)).catch((error) => logError("Failed to push event", error)); - } - pushFileProgress(fileEl, entryRef, progress, onReply = function() { - }) { - this.liveSocket.withinOwners(fileEl.form, (view, targetCtx) => { - view.pushWithReply(null, "progress", { - event: fileEl.getAttribute(view.binding(PHX_PROGRESS)), - ref: fileEl.getAttribute(PHX_UPLOAD_REF), - entry_ref: entryRef, - progress, - cid: view.targetComponentID(fileEl.form, targetCtx) - }).then(() => onReply()).catch((error) => logError("Failed to push file progress", error)); - }); - } - pushInput(inputEl, targetCtx, forceCid, phxEvent, opts, callback) { - if (!inputEl.form) { - throw new Error("form events require the input to be inside a form"); - } - let uploads; - const cid = isCid(forceCid) ? forceCid : this.targetComponentID(inputEl.form, targetCtx, opts); - const refGenerator = (maybePayload) => { - return this.putRef( - [ - { el: inputEl, loading: true, lock: true }, - { el: inputEl.form, loading: true, lock: true } - ], - phxEvent, - "change", - __spreadProps(__spreadValues({}, opts), { payload: maybePayload == null ? void 0 : maybePayload.payload }) - ); - }; - let formData; - const meta = this.extractMeta(inputEl.form, {}, opts.value); - const serializeOpts = {}; - if (inputEl instanceof HTMLButtonElement) { - serializeOpts.submitter = inputEl; - } - if (inputEl.getAttribute(this.binding("change"))) { - formData = serializeForm(inputEl.form, serializeOpts, [inputEl.name]); - } else { - formData = serializeForm(inputEl.form, serializeOpts); - } - if (dom_default.isUploadInput(inputEl) && inputEl.files && inputEl.files.length > 0) { - LiveUploader.trackFiles(inputEl, Array.from(inputEl.files)); - } - uploads = LiveUploader.serializeUploads(inputEl); - const event = { - type: "form", - event: phxEvent, - value: formData, - meta: __spreadValues({ - // no target was implicitly sent as "undefined" in LV <= 1.0.5, therefore - // we have to keep it. In 1.0.6 we switched from passing meta as URL encoded data - // to passing it directly in the event, but the JSON encode would drop keys with - // undefined values. - _target: opts._target || "undefined" - }, meta), - uploads, - cid - }; - this.pushWithReply(refGenerator, "event", event).then(({ resp }) => { - if (dom_default.isUploadInput(inputEl) && dom_default.isAutoUpload(inputEl)) { - ElementRef.onUnlock(inputEl, () => { - if (LiveUploader.filesAwaitingPreflight(inputEl).length > 0) { - const [ref, _els] = refGenerator(); - this.undoRefs(ref, phxEvent, [inputEl.form]); - this.uploadFiles( - inputEl.form, - phxEvent, - targetCtx, - ref, - cid, - (_uploads) => { - callback && callback(resp); - this.triggerAwaitingSubmit(inputEl.form, phxEvent); - this.undoRefs(ref, phxEvent); - } - ); - } - }); - } else { - callback && callback(resp); - } - }).catch((error) => logError("Failed to push input event", error)); - } - triggerAwaitingSubmit(formEl, phxEvent) { - const awaitingSubmit = this.getScheduledSubmit(formEl); - if (awaitingSubmit) { - const [_el, _ref, _opts, callback] = awaitingSubmit; - this.cancelSubmit(formEl, phxEvent); - callback(); - } - } - getScheduledSubmit(formEl) { - return this.formSubmits.find( - ([el, _ref, _opts, _callback]) => el.isSameNode(formEl) - ); - } - scheduleSubmit(formEl, ref, opts, callback) { - if (this.getScheduledSubmit(formEl)) { - return true; - } - this.formSubmits.push([formEl, ref, opts, callback]); - } - cancelSubmit(formEl, phxEvent) { - this.formSubmits = this.formSubmits.filter( - ([el, ref, _opts, _callback]) => { - if (el.isSameNode(formEl)) { - this.undoRefs(ref, phxEvent); - return false; - } else { - return true; - } - } - ); - } - disableForm(formEl, phxEvent, opts = {}) { - const filterIgnored = (el) => { - const userIgnored = closestPhxBinding( - el, - `${this.binding(PHX_UPDATE)}=ignore`, - el.form - ); - return !(userIgnored || closestPhxBinding(el, "data-phx-update=ignore", el.form)); - }; - const filterDisables = (el) => { - return el.hasAttribute(this.binding(PHX_DISABLE_WITH)); - }; - const filterButton = (el) => el.tagName == "BUTTON"; - const filterInput = (el) => ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName); - const formElements = Array.from(formEl.elements); - const disables = formElements.filter(filterDisables); - const buttons = formElements.filter(filterButton).filter(filterIgnored); - const inputs = formElements.filter(filterInput).filter(filterIgnored); - buttons.forEach((button) => { - button.setAttribute(PHX_DISABLED, button.disabled); - button.disabled = true; - }); - inputs.forEach((input) => { - input.setAttribute(PHX_READONLY, input.readOnly); - input.readOnly = true; - if (input.files) { - input.setAttribute(PHX_DISABLED, input.disabled); - input.disabled = true; - } - }); - const formEls = disables.concat(buttons).concat(inputs).map((el) => { - return { el, loading: true, lock: true }; - }); - const els = [{ el: formEl, loading: true, lock: false }].concat(formEls).reverse(); - return this.putRef(els, phxEvent, "submit", opts); - } - pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply) { - const refGenerator = (maybePayload) => this.disableForm(formEl, phxEvent, __spreadProps(__spreadValues({}, opts), { - form: formEl, - payload: maybePayload == null ? void 0 : maybePayload.payload, - submitter - })); - dom_default.putPrivate(formEl, "submitter", submitter); - const cid = this.targetComponentID(formEl, targetCtx); - if (LiveUploader.hasUploadsInProgress(formEl)) { - const [ref, _els] = refGenerator(); - const push = () => this.pushFormSubmit( - formEl, - targetCtx, - phxEvent, - submitter, - opts, - onReply - ); - return this.scheduleSubmit(formEl, ref, opts, push); - } else if (LiveUploader.inputsAwaitingPreflight(formEl).length > 0) { - const [ref, els] = refGenerator(); - const proxyRefGen = () => [ref, els, opts]; - this.uploadFiles(formEl, phxEvent, targetCtx, ref, cid, (_uploads) => { - if (LiveUploader.inputsAwaitingPreflight(formEl).length > 0) { - return this.undoRefs(ref, phxEvent); - } - const meta = this.extractMeta(formEl, {}, opts.value); - const formData = serializeForm(formEl, { submitter }); - this.pushWithReply(proxyRefGen, "event", { - type: "form", - event: phxEvent, - value: formData, - meta, - cid - }).then(({ resp }) => onReply(resp)).catch((error) => logError("Failed to push form submit", error)); - }); - } else if (!(formEl.hasAttribute(PHX_REF_SRC) && formEl.classList.contains("phx-submit-loading"))) { - const meta = this.extractMeta(formEl, {}, opts.value); - const formData = serializeForm(formEl, { submitter }); - this.pushWithReply(refGenerator, "event", { - type: "form", - event: phxEvent, - value: formData, - meta, - cid - }).then(({ resp }) => onReply(resp)).catch((error) => logError("Failed to push form submit", error)); - } - } - uploadFiles(formEl, phxEvent, targetCtx, ref, cid, onComplete) { - const joinCountAtUpload = this.joinCount; - const inputEls = LiveUploader.activeFileInputs(formEl); - let numFileInputsInProgress = inputEls.length; - inputEls.forEach((inputEl) => { - const uploader = new LiveUploader(inputEl, this, () => { - numFileInputsInProgress--; - if (numFileInputsInProgress === 0) { - onComplete(); - } - }); - const entries = uploader.entries().map((entry) => entry.toPreflightPayload()); - if (entries.length === 0) { - numFileInputsInProgress--; - return; - } - const payload = { - ref: inputEl.getAttribute(PHX_UPLOAD_REF), - entries, - cid: this.targetComponentID(inputEl.form, targetCtx) - }; - this.log("upload", () => ["sending preflight request", payload]); - this.pushWithReply(null, "allow_upload", payload).then(({ resp }) => { - this.log("upload", () => ["got preflight response", resp]); - uploader.entries().forEach((entry) => { - if (resp.entries && !resp.entries[entry.ref]) { - this.handleFailedEntryPreflight( - entry.ref, - "failed preflight", - uploader - ); - } - }); - if (resp.error || Object.keys(resp.entries).length === 0) { - this.undoRefs(ref, phxEvent); - const errors = resp.error || []; - errors.map(([entry_ref, reason]) => { - this.handleFailedEntryPreflight(entry_ref, reason, uploader); - }); - } else { - const onError = (callback) => { - this.channel.onError(() => { - if (this.joinCount === joinCountAtUpload) { - callback(); - } - }); - }; - uploader.initAdapterUpload(resp, onError, this.liveSocket); - } - }).catch((error) => logError("Failed to push upload", error)); - }); - } - handleFailedEntryPreflight(uploadRef, reason, uploader) { - if (uploader.isAutoUpload()) { - const entry = uploader.entries().find((entry2) => entry2.ref === uploadRef.toString()); - if (entry) { - entry.cancel(); - } - } else { - uploader.entries().map((entry) => entry.cancel()); - } - this.log("upload", () => [`error for entry ${uploadRef}`, reason]); - } - dispatchUploads(targetCtx, name, filesOrBlobs) { - const targetElement = this.targetCtxElement(targetCtx) || this.el; - const inputs = dom_default.findUploadInputs(targetElement).filter( - (el) => el.name === name - ); - if (inputs.length === 0) { - logError(`no live file inputs found matching the name "${name}"`); - } else if (inputs.length > 1) { - logError(`duplicate live file inputs found matching the name "${name}"`); - } else { - dom_default.dispatchEvent(inputs[0], PHX_TRACK_UPLOADS, { - detail: { files: filesOrBlobs } - }); - } - } - targetCtxElement(targetCtx) { - if (isCid(targetCtx)) { - const [target] = dom_default.findComponentNodeList(this.id, targetCtx); - return target; - } else if (targetCtx) { - return targetCtx; - } else { - return null; - } - } - pushFormRecovery(oldForm, newForm, templateDom, callback) { - const phxChange = this.binding("change"); - const phxTarget = newForm.getAttribute(this.binding("target")) || newForm; - const phxEvent = newForm.getAttribute(this.binding(PHX_AUTO_RECOVER)) || newForm.getAttribute(this.binding("change")); - const inputs = Array.from(oldForm.elements).filter( - (el) => dom_default.isFormInput(el) && el.name && !el.hasAttribute(phxChange) - ); - if (inputs.length === 0) { - callback(); - return; - } - inputs.forEach( - (input2) => input2.hasAttribute(PHX_UPLOAD_REF) && LiveUploader.clearFiles(input2) - ); - const input = inputs.find((el) => el.type !== "hidden") || inputs[0]; - let pending = 0; - this.withinTargets( - phxTarget, - (targetView, targetCtx) => { - const cid = this.targetComponentID(newForm, targetCtx); - pending++; - let e = new CustomEvent("phx:form-recovery", { - detail: { sourceElement: oldForm } - }); - js_default.exec(e, "change", phxEvent, this, input, [ - "push", - { - _target: input.name, - targetView, - targetCtx, - newCid: cid, - callback: () => { - pending--; - if (pending === 0) { - callback(); - } - } - } - ]); - }, - templateDom - ); - } - pushLinkPatch(e, href, targetEl, callback) { - const linkRef = this.liveSocket.setPendingLink(href); - const loading = e.isTrusted && e.type !== "popstate"; - const refGen = targetEl ? () => this.putRef( - [{ el: targetEl, loading, lock: true }], - null, - "click" - ) : null; - const fallback = () => this.liveSocket.redirect(window.location.href); - const url = href.startsWith("/") ? `${location.protocol}//${location.host}${href}` : href; - this.pushWithReply(refGen, "live_patch", { url }).then( - ({ resp }) => { - this.liveSocket.requestDOMUpdate(() => { - if (resp.link_redirect) { - this.liveSocket.replaceMain(href, null, callback, linkRef); - } else { - if (this.liveSocket.commitPendingLink(linkRef)) { - this.href = href; - } - this.applyPendingUpdates(); - callback && callback(linkRef); - } - }); - }, - ({ error: _error, timeout: _timeout }) => fallback() - ); - } - getFormsForRecovery() { - if (this.joinCount === 0) { - return {}; - } - const phxChange = this.binding("change"); - return dom_default.all(this.el, `form[${phxChange}]`).filter((form) => form.id).filter((form) => form.elements.length > 0).filter( - (form) => form.getAttribute(this.binding(PHX_AUTO_RECOVER)) !== "ignore" - ).map((form) => { - const clonedForm = form.cloneNode(false); - dom_default.copyPrivates(clonedForm, form); - Array.from(form.elements).forEach((el) => { - const clonedEl = el.cloneNode(true); - morphdom_esm_default(clonedEl, el); - dom_default.copyPrivates(clonedEl, el); - clonedForm.appendChild(clonedEl); - }); - return clonedForm; - }).reduce((acc, form) => { - acc[form.id] = form; - return acc; - }, {}); - } - maybePushComponentsDestroyed(destroyedCIDs) { - let willDestroyCIDs = destroyedCIDs.filter((cid) => { - return dom_default.findComponentNodeList(this.el, cid).length === 0; - }); - const onError = (error) => { - if (!this.isDestroyed()) { - logError("Failed to push components destroyed", error); - } - }; - if (willDestroyCIDs.length > 0) { - willDestroyCIDs.forEach((cid) => this.rendered.resetRender(cid)); - this.pushWithReply(null, "cids_will_destroy", { cids: willDestroyCIDs }).then(() => { - this.liveSocket.requestDOMUpdate(() => { - let completelyDestroyCIDs = willDestroyCIDs.filter((cid) => { - return dom_default.findComponentNodeList(this.el, cid).length === 0; - }); - if (completelyDestroyCIDs.length > 0) { - this.pushWithReply(null, "cids_destroyed", { - cids: completelyDestroyCIDs - }).then(({ resp }) => { - this.rendered.pruneCIDs(resp.cids); - }).catch(onError); - } - }); - }).catch(onError); - } - } - ownsElement(el) { - let parentViewEl = el.closest(PHX_VIEW_SELECTOR); - return el.getAttribute(PHX_PARENT_ID) === this.id || parentViewEl && parentViewEl.id === this.id || !parentViewEl && this.isDead; - } - submitForm(form, targetCtx, phxEvent, submitter, opts = {}) { - dom_default.putPrivate(form, PHX_HAS_SUBMITTED, true); - const inputs = Array.from(form.elements); - inputs.forEach((input) => dom_default.putPrivate(input, PHX_HAS_SUBMITTED, true)); - this.liveSocket.blurActiveElement(this); - this.pushFormSubmit(form, targetCtx, phxEvent, submitter, opts, () => { - this.liveSocket.restorePreviouslyActiveFocus(); - }); - } - binding(kind) { - return this.liveSocket.binding(kind); - } - // phx-portal - pushPortalElementId(id) { - this.portalElementIds.add(id); - } - dropPortalElementId(id) { - this.portalElementIds.delete(id); - } - destroyPortalElements() { - this.portalElementIds.forEach((id) => { - const el = document.getElementById(id); - if (el) { - el.remove(); - } - }); - } - }; - var LiveSocket = class { - constructor(url, phxSocket, opts = {}) { - this.unloaded = false; - if (!phxSocket || phxSocket.constructor.name === "Object") { - throw new Error(` - a phoenix Socket must be provided as the second argument to the LiveSocket constructor. For example: - - import {Socket} from "phoenix" - import {LiveSocket} from "phoenix_live_view" - let liveSocket = new LiveSocket("/live", Socket, {...}) - `); - } - this.socket = new phxSocket(url, opts); - this.bindingPrefix = opts.bindingPrefix || BINDING_PREFIX; - this.opts = opts; - this.params = closure2(opts.params || {}); - this.viewLogger = opts.viewLogger; - this.metadataCallbacks = opts.metadata || {}; - this.defaults = Object.assign(clone(DEFAULTS), opts.defaults || {}); - this.prevActive = null; - this.silenced = false; - this.main = null; - this.outgoingMainEl = null; - this.clickStartedAtTarget = null; - this.linkRef = 1; - this.roots = {}; - this.href = window.location.href; - this.pendingLink = null; - this.currentLocation = clone(window.location); - this.hooks = opts.hooks || {}; - this.uploaders = opts.uploaders || {}; - this.loaderTimeout = opts.loaderTimeout || LOADER_TIMEOUT; - this.disconnectedTimeout = opts.disconnectedTimeout || DISCONNECTED_TIMEOUT; - this.reloadWithJitterTimer = null; - this.maxReloads = opts.maxReloads || MAX_RELOADS; - this.reloadJitterMin = opts.reloadJitterMin || RELOAD_JITTER_MIN; - this.reloadJitterMax = opts.reloadJitterMax || RELOAD_JITTER_MAX; - this.failsafeJitter = opts.failsafeJitter || FAILSAFE_JITTER; - this.localStorage = opts.localStorage || window.localStorage; - this.sessionStorage = opts.sessionStorage || window.sessionStorage; - this.boundTopLevelEvents = false; - this.boundEventNames = /* @__PURE__ */ new Set(); - this.blockPhxChangeWhileComposing = opts.blockPhxChangeWhileComposing || false; - this.serverCloseRef = null; - this.domCallbacks = Object.assign( - { - jsQuerySelectorAll: null, - onPatchStart: closure2(), - onPatchEnd: closure2(), - onNodeAdded: closure2(), - onBeforeElUpdated: closure2() - }, - opts.dom || {} - ); - this.transitions = new TransitionSet(); - this.currentHistoryPosition = parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION)) || 0; - window.addEventListener("pagehide", (_e) => { - this.unloaded = true; - }); - this.socket.onOpen(() => { - if (this.isUnloaded()) { - window.location.reload(); - } - }); - } - // public - version() { - return "1.1.0-rc.3"; - } - isProfileEnabled() { - return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true"; - } - isDebugEnabled() { - return this.sessionStorage.getItem(PHX_LV_DEBUG) === "true"; - } - isDebugDisabled() { - return this.sessionStorage.getItem(PHX_LV_DEBUG) === "false"; - } - enableDebug() { - this.sessionStorage.setItem(PHX_LV_DEBUG, "true"); - } - enableProfiling() { - this.sessionStorage.setItem(PHX_LV_PROFILE, "true"); - } - disableDebug() { - this.sessionStorage.setItem(PHX_LV_DEBUG, "false"); - } - disableProfiling() { - this.sessionStorage.removeItem(PHX_LV_PROFILE); - } - enableLatencySim(upperBoundMs) { - this.enableDebug(); - console.log( - "latency simulator enabled for the duration of this browser session. Call disableLatencySim() to disable" - ); - this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs); - } - disableLatencySim() { - this.sessionStorage.removeItem(PHX_LV_LATENCY_SIM); - } - getLatencySim() { - const str = this.sessionStorage.getItem(PHX_LV_LATENCY_SIM); - return str ? parseInt(str) : null; - } - getSocket() { - return this.socket; - } - connect() { - if (window.location.hostname === "localhost" && !this.isDebugDisabled()) { - this.enableDebug(); - } - const doConnect = () => { - this.resetReloadStatus(); - if (this.joinRootViews()) { - this.bindTopLevelEvents(); - this.socket.connect(); - } else if (this.main) { - this.socket.connect(); - } else { - this.bindTopLevelEvents({ dead: true }); - } - this.joinDeadView(); - }; - if (["complete", "loaded", "interactive"].indexOf(document.readyState) >= 0) { - doConnect(); - } else { - document.addEventListener("DOMContentLoaded", () => doConnect()); - } - } - disconnect(callback) { - clearTimeout(this.reloadWithJitterTimer); - if (this.serverCloseRef) { - this.socket.off(this.serverCloseRef); - this.serverCloseRef = null; - } - this.socket.disconnect(callback); - } - replaceTransport(transport) { - clearTimeout(this.reloadWithJitterTimer); - this.socket.replaceTransport(transport); - this.connect(); - } - execJS(el, encodedJS, eventType = null) { - const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } }); - this.owner(el, (view) => js_default.exec(e, eventType, encodedJS, view, el)); - } - /** - * Returns an object with methods to manipluate the DOM and execute JavaScript. - * The applied changes integrate with server DOM patching. - * - * @returns {import("./js_commands").LiveSocketJSCommands} - */ - js() { - return js_commands_default(this, "js"); - } - // private - unload() { - if (this.unloaded) { - return; - } - if (this.main && this.isConnected()) { - this.log(this.main, "socket", () => ["disconnect for page nav"]); - } - this.unloaded = true; - this.destroyAllViews(); - this.disconnect(); - } - triggerDOM(kind, args) { - this.domCallbacks[kind](...args); - } - time(name, func) { - if (!this.isProfileEnabled() || !console.time) { - return func(); - } - console.time(name); - const result = func(); - console.timeEnd(name); - return result; - } - log(view, kind, msgCallback) { - if (this.viewLogger) { - const [msg, obj] = msgCallback(); - this.viewLogger(view, kind, msg, obj); - } else if (this.isDebugEnabled()) { - const [msg, obj] = msgCallback(); - debug(view, kind, msg, obj); - } - } - requestDOMUpdate(callback) { - this.transitions.after(callback); - } - asyncTransition(promise) { - this.transitions.addAsyncTransition(promise); - } - transition(time, onStart, onDone = function() { - }) { - this.transitions.addTransition(time, onStart, onDone); - } - onChannel(channel, event, cb) { - channel.on(event, (data) => { - const latency = this.getLatencySim(); - if (!latency) { - cb(data); - } else { - setTimeout(() => cb(data), latency); - } - }); - } - reloadWithJitter(view, log) { - clearTimeout(this.reloadWithJitterTimer); - this.disconnect(); - const minMs = this.reloadJitterMin; - const maxMs = this.reloadJitterMax; - let afterMs = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs; - const tries = browser_default.updateLocal( - this.localStorage, - window.location.pathname, - CONSECUTIVE_RELOADS, - 0, - (count) => count + 1 - ); - if (tries >= this.maxReloads) { - afterMs = this.failsafeJitter; - } - this.reloadWithJitterTimer = setTimeout(() => { - if (view.isDestroyed() || view.isConnected()) { - return; - } - view.destroy(); - log ? log() : this.log(view, "join", () => [ - `encountered ${tries} consecutive reloads` - ]); - if (tries >= this.maxReloads) { - this.log(view, "join", () => [ - `exceeded ${this.maxReloads} consecutive reloads. Entering failsafe mode` - ]); - } - if (this.hasPendingLink()) { - window.location = this.pendingLink; - } else { - window.location.reload(); - } - }, afterMs); - } - getHookDefinition(name) { - if (!name) { - return; - } - return this.maybeInternalHook(name) || this.hooks[name] || this.maybeRuntimeHook(name); - } - maybeInternalHook(name) { - return name && name.startsWith("Phoenix.") && hooks_default[name.split(".")[1]]; - } - maybeRuntimeHook(name) { - const runtimeHook = document.querySelector( - `script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]` - ); - if (!runtimeHook) { - return; - } - let callbacks = window[`phx_hook_${name}`]; - if (!callbacks || typeof callbacks !== "function") { - logError("a runtime hook must be a function", runtimeHook); - return; - } - const hookDefiniton = callbacks(); - if (hookDefiniton && (typeof hookDefiniton === "object" || typeof hookDefiniton === "function")) { - return hookDefiniton; - } - logError( - "runtime hook must return an object with hook callbacks or an instance of ViewHook", - runtimeHook - ); - } - isUnloaded() { - return this.unloaded; - } - isConnected() { - return this.socket.isConnected(); - } - getBindingPrefix() { - return this.bindingPrefix; - } - binding(kind) { - return `${this.getBindingPrefix()}${kind}`; - } - channel(topic, params2) { - return this.socket.channel(topic, params2); - } - joinDeadView() { - const body = document.body; - if (body && !this.isPhxView(body) && !this.isPhxView(document.firstElementChild)) { - const view = this.newRootView(body); - view.setHref(this.getHref()); - view.joinDead(); - if (!this.main) { - this.main = view; - } - window.requestAnimationFrame(() => { - var _a; - view.execNewMounted(); - this.maybeScroll((_a = history.state) == null ? void 0 : _a.scroll); - }); - } - } - joinRootViews() { - let rootsFound = false; - dom_default.all( - document, - `${PHX_VIEW_SELECTOR}:not([${PHX_PARENT_ID}])`, - (rootEl) => { - if (!this.getRootById(rootEl.id)) { - const view = this.newRootView(rootEl); - if (!dom_default.isPhxSticky(rootEl)) { - view.setHref(this.getHref()); - } - view.join(); - if (rootEl.hasAttribute(PHX_MAIN)) { - this.main = view; - } - } - rootsFound = true; - } - ); - return rootsFound; - } - redirect(to, flash, reloadToken) { - if (reloadToken) { - browser_default.setCookie(PHX_RELOAD_STATUS, reloadToken, 60); - } - this.unload(); - browser_default.redirect(to, flash); - } - replaceMain(href, flash, callback = null, linkRef = this.setPendingLink(href)) { - const liveReferer = this.currentLocation.href; - this.outgoingMainEl = this.outgoingMainEl || this.main.el; - const stickies = dom_default.findPhxSticky(document) || []; - const removeEls = dom_default.all( - this.outgoingMainEl, - `[${this.binding("remove")}]` - ).filter((el) => !dom_default.isChildOfAny(el, stickies)); - const newMainEl = dom_default.cloneNode(this.outgoingMainEl, ""); - this.main.showLoader(this.loaderTimeout); - this.main.destroy(); - this.main = this.newRootView(newMainEl, flash, liveReferer); - this.main.setRedirect(href); - this.transitionRemoves(removeEls); - this.main.join((joinCount, onDone) => { - if (joinCount === 1 && this.commitPendingLink(linkRef)) { - this.requestDOMUpdate(() => { - removeEls.forEach((el) => el.remove()); - stickies.forEach((el) => newMainEl.appendChild(el)); - this.outgoingMainEl.replaceWith(newMainEl); - this.outgoingMainEl = null; - callback && callback(linkRef); - onDone(); - }); - } - }); - } - transitionRemoves(elements, callback) { - const removeAttr = this.binding("remove"); - const silenceEvents = (e) => { - e.preventDefault(); - e.stopImmediatePropagation(); - }; - elements.forEach((el) => { - for (const event of this.boundEventNames) { - el.addEventListener(event, silenceEvents, true); - } - this.execJS(el, el.getAttribute(removeAttr), "remove"); - }); - this.requestDOMUpdate(() => { - elements.forEach((el) => { - for (const event of this.boundEventNames) { - el.removeEventListener(event, silenceEvents, true); - } - }); - callback && callback(); - }); - } - isPhxView(el) { - return el.getAttribute && el.getAttribute(PHX_SESSION) !== null; - } - newRootView(el, flash, liveReferer) { - const view = new View(el, this, null, flash, liveReferer); - this.roots[view.id] = view; - return view; - } - owner(childEl, callback) { - let view; - const viewEl = dom_default.closestViewEl(childEl); - if (viewEl) { - view = this.getViewByEl(viewEl); - } else { - view = this.main; - } - return view && callback ? callback(view) : view; - } - withinOwners(childEl, callback) { - this.owner(childEl, (view) => callback(view, childEl)); - } - getViewByEl(el) { - const rootId = el.getAttribute(PHX_ROOT_ID); - return maybe( - this.getRootById(rootId), - (root) => root.getDescendentByEl(el) - ); - } - getRootById(id) { - return this.roots[id]; - } - destroyAllViews() { - for (const id in this.roots) { - this.roots[id].destroy(); - delete this.roots[id]; - } - this.main = null; - } - destroyViewByEl(el) { - const root = this.getRootById(el.getAttribute(PHX_ROOT_ID)); - if (root && root.id === el.id) { - root.destroy(); - delete this.roots[root.id]; - } else if (root) { - root.destroyDescendent(el.id); - } - } - getActiveElement() { - return document.activeElement; - } - dropActiveElement(view) { - if (this.prevActive && view.ownsElement(this.prevActive)) { - this.prevActive = null; - } - } - restorePreviouslyActiveFocus() { - if (this.prevActive && this.prevActive !== document.body && this.prevActive instanceof HTMLElement) { - this.prevActive.focus(); - } - } - blurActiveElement() { - this.prevActive = this.getActiveElement(); - if (this.prevActive !== document.body && this.prevActive instanceof HTMLElement) { - this.prevActive.blur(); - } - } - /** - * @param {{dead?: boolean}} [options={}] - */ - bindTopLevelEvents({ dead } = {}) { - if (this.boundTopLevelEvents) { - return; - } - this.boundTopLevelEvents = true; - this.serverCloseRef = this.socket.onClose((event) => { - if (event && event.code === 1e3 && this.main) { - return this.reloadWithJitter(this.main); - } - }); - document.body.addEventListener("click", function() { - }); - window.addEventListener( - "pageshow", - (e) => { - if (e.persisted) { - this.getSocket().disconnect(); - this.withPageLoading({ to: window.location.href, kind: "redirect" }); - window.location.reload(); - } - }, - true - ); - if (!dead) { - this.bindNav(); - } - this.bindClicks(); - if (!dead) { - this.bindForms(); - } - this.bind( - { keyup: "keyup", keydown: "keydown" }, - (e, type, view, targetEl, phxEvent, _phxTarget) => { - const matchKey = targetEl.getAttribute(this.binding(PHX_KEY)); - const pressedKey = e.key && e.key.toLowerCase(); - if (matchKey && matchKey.toLowerCase() !== pressedKey) { - return; - } - const data = __spreadValues({ key: e.key }, this.eventMeta(type, e, targetEl)); - js_default.exec(e, type, phxEvent, view, targetEl, ["push", { data }]); - } - ); - this.bind( - { blur: "focusout", focus: "focusin" }, - (e, type, view, targetEl, phxEvent, phxTarget) => { - if (!phxTarget) { - const data = __spreadValues({ key: e.key }, this.eventMeta(type, e, targetEl)); - js_default.exec(e, type, phxEvent, view, targetEl, ["push", { data }]); - } - } - ); - this.bind( - { blur: "blur", focus: "focus" }, - (e, type, view, targetEl, phxEvent, phxTarget) => { - if (phxTarget === "window") { - const data = this.eventMeta(type, e, targetEl); - js_default.exec(e, type, phxEvent, view, targetEl, ["push", { data }]); - } - } - ); - this.on("dragover", (e) => e.preventDefault()); - this.on("drop", (e) => { - e.preventDefault(); - const dropTargetId = maybe( - closestPhxBinding(e.target, this.binding(PHX_DROP_TARGET)), - (trueTarget) => { - return trueTarget.getAttribute(this.binding(PHX_DROP_TARGET)); - } - ); - const dropTarget = dropTargetId && document.getElementById(dropTargetId); - const files = Array.from(e.dataTransfer.files || []); - if (!dropTarget || !(dropTarget instanceof HTMLInputElement) || dropTarget.disabled || files.length === 0 || !(dropTarget.files instanceof FileList)) { - return; - } - LiveUploader.trackFiles(dropTarget, files, e.dataTransfer); - dropTarget.dispatchEvent(new Event("input", { bubbles: true })); - }); - this.on(PHX_TRACK_UPLOADS, (e) => { - const uploadTarget = e.target; - if (!dom_default.isUploadInput(uploadTarget)) { - return; - } - const files = Array.from(e.detail.files || []).filter( - (f) => f instanceof File || f instanceof Blob - ); - LiveUploader.trackFiles(uploadTarget, files); - uploadTarget.dispatchEvent(new Event("input", { bubbles: true })); - }); - } - eventMeta(eventName, e, targetEl) { - const callback = this.metadataCallbacks[eventName]; - return callback ? callback(e, targetEl) : {}; - } - setPendingLink(href) { - this.linkRef++; - this.pendingLink = href; - this.resetReloadStatus(); - return this.linkRef; - } - // anytime we are navigating or connecting, drop reload cookie in case - // we issue the cookie but the next request was interrupted and the server never dropped it - resetReloadStatus() { - browser_default.deleteCookie(PHX_RELOAD_STATUS); - } - commitPendingLink(linkRef) { - if (this.linkRef !== linkRef) { - return false; - } else { - this.href = this.pendingLink; - this.pendingLink = null; - return true; - } - } - getHref() { - return this.href; - } - hasPendingLink() { - return !!this.pendingLink; - } - bind(events, callback) { - for (const event in events) { - const browserEventName = events[event]; - this.on(browserEventName, (e) => { - const binding = this.binding(event); - const windowBinding = this.binding(`window-${event}`); - const targetPhxEvent = e.target.getAttribute && e.target.getAttribute(binding); - if (targetPhxEvent) { - this.debounce(e.target, e, browserEventName, () => { - this.withinOwners(e.target, (view) => { - callback(e, event, view, e.target, targetPhxEvent, null); - }); - }); - } else { - dom_default.all(document, `[${windowBinding}]`, (el) => { - const phxEvent = el.getAttribute(windowBinding); - this.debounce(el, e, browserEventName, () => { - this.withinOwners(el, (view) => { - callback(e, event, view, el, phxEvent, "window"); - }); - }); - }); - } - }); - } - } - bindClicks() { - this.on("mousedown", (e) => this.clickStartedAtTarget = e.target); - this.bindClick("click", "click"); - } - bindClick(eventName, bindingName) { - const click = this.binding(bindingName); - window.addEventListener( - eventName, - (e) => { - let target = null; - if (e.detail === 0) - this.clickStartedAtTarget = e.target; - const clickStartedAtTarget = this.clickStartedAtTarget || e.target; - target = closestPhxBinding(e.target, click); - this.dispatchClickAway(e, clickStartedAtTarget); - this.clickStartedAtTarget = null; - const phxEvent = target && target.getAttribute(click); - if (!phxEvent) { - if (dom_default.isNewPageClick(e, window.location)) { - this.unload(); - } - return; - } - if (target.getAttribute("href") === "#") { - e.preventDefault(); - } - if (target.hasAttribute(PHX_REF_SRC)) { - return; - } - this.debounce(target, e, "click", () => { - this.withinOwners(target, (view) => { - js_default.exec(e, "click", phxEvent, view, target, [ - "push", - { data: this.eventMeta("click", e, target) } - ]); - }); - }); - }, - false - ); - } - dispatchClickAway(e, clickStartedAt) { - const phxClickAway = this.binding("click-away"); - dom_default.all(document, `[${phxClickAway}]`, (el) => { - if (!(el.isSameNode(clickStartedAt) || el.contains(clickStartedAt))) { - this.withinOwners(el, (view) => { - const phxEvent = el.getAttribute(phxClickAway); - if (js_default.isVisible(el) && js_default.isInViewport(el)) { - js_default.exec(e, "click", phxEvent, view, el, [ - "push", - { data: this.eventMeta("click", e, e.target) } - ]); - } - }); - } - }); - } - bindNav() { - if (!browser_default.canPushState()) { - return; - } - if (history.scrollRestoration) { - history.scrollRestoration = "manual"; - } - let scrollTimer = null; - window.addEventListener("scroll", (_e) => { - clearTimeout(scrollTimer); - scrollTimer = setTimeout(() => { - browser_default.updateCurrentState( - (state) => Object.assign(state, { scroll: window.scrollY }) - ); - }, 100); - }); - window.addEventListener( - "popstate", - (event) => { - if (!this.registerNewLocation(window.location)) { - return; - } - const { type, backType, id, scroll, position } = event.state || {}; - const href = window.location.href; - const isForward = position > this.currentHistoryPosition; - const navType = isForward ? type : backType || type; - this.currentHistoryPosition = position || 0; - this.sessionStorage.setItem( - PHX_LV_HISTORY_POSITION, - this.currentHistoryPosition.toString() - ); - dom_default.dispatchEvent(window, "phx:navigate", { - detail: { - href, - patch: navType === "patch", - pop: true, - direction: isForward ? "forward" : "backward" - } - }); - this.requestDOMUpdate(() => { - const callback = () => { - this.maybeScroll(scroll); - }; - if (this.main.isConnected() && navType === "patch" && id === this.main.id) { - this.main.pushLinkPatch(event, href, null, callback); - } else { - this.replaceMain(href, null, callback); - } - }); - }, - false - ); - window.addEventListener( - "click", - (e) => { - const target = closestPhxBinding(e.target, PHX_LIVE_LINK); - const type = target && target.getAttribute(PHX_LIVE_LINK); - if (!type || !this.isConnected() || !this.main || dom_default.wantsNewTab(e)) { - return; - } - const href = target.href instanceof SVGAnimatedString ? target.href.baseVal : target.href; - const linkState = target.getAttribute(PHX_LINK_STATE); - e.preventDefault(); - e.stopImmediatePropagation(); - if (this.pendingLink === href) { - return; - } - this.requestDOMUpdate(() => { - if (type === "patch") { - this.pushHistoryPatch(e, href, linkState, target); - } else if (type === "redirect") { - this.historyRedirect(e, href, linkState, null, target); - } else { - throw new Error( - `expected ${PHX_LIVE_LINK} to be "patch" or "redirect", got: ${type}` - ); - } - const phxClick = target.getAttribute(this.binding("click")); - if (phxClick) { - this.requestDOMUpdate(() => this.execJS(target, phxClick, "click")); - } - }); - }, - false - ); - } - maybeScroll(scroll) { - if (typeof scroll === "number") { - requestAnimationFrame(() => { - window.scrollTo(0, scroll); - }); - } - } - dispatchEvent(event, payload = {}) { - dom_default.dispatchEvent(window, `phx:${event}`, { detail: payload }); - } - dispatchEvents(events) { - events.forEach(([event, payload]) => this.dispatchEvent(event, payload)); - } - withPageLoading(info, callback) { - dom_default.dispatchEvent(window, "phx:page-loading-start", { detail: info }); - const done = () => dom_default.dispatchEvent(window, "phx:page-loading-stop", { detail: info }); - return callback ? callback(done) : done; - } - pushHistoryPatch(e, href, linkState, targetEl) { - if (!this.isConnected() || !this.main.isMain()) { - return browser_default.redirect(href); - } - this.withPageLoading({ to: href, kind: "patch" }, (done) => { - this.main.pushLinkPatch(e, href, targetEl, (linkRef) => { - this.historyPatch(href, linkState, linkRef); - done(); - }); - }); - } - historyPatch(href, linkState, linkRef = this.setPendingLink(href)) { - if (!this.commitPendingLink(linkRef)) { - return; - } - this.currentHistoryPosition++; - this.sessionStorage.setItem( - PHX_LV_HISTORY_POSITION, - this.currentHistoryPosition.toString() - ); - browser_default.updateCurrentState((state) => __spreadProps(__spreadValues({}, state), { backType: "patch" })); - browser_default.pushState( - linkState, - { - type: "patch", - id: this.main.id, - position: this.currentHistoryPosition - }, - href - ); - dom_default.dispatchEvent(window, "phx:navigate", { - detail: { patch: true, href, pop: false, direction: "forward" } - }); - this.registerNewLocation(window.location); - } - historyRedirect(e, href, linkState, flash, targetEl) { - const clickLoading = targetEl && e.isTrusted && e.type !== "popstate"; - if (clickLoading) { - targetEl.classList.add("phx-click-loading"); - } - if (!this.isConnected() || !this.main.isMain()) { - return browser_default.redirect(href, flash); - } - if (/^\/$|^\/[^\/]+.*$/.test(href)) { - const { protocol, host } = window.location; - href = `${protocol}//${host}${href}`; - } - const scroll = window.scrollY; - this.withPageLoading({ to: href, kind: "redirect" }, (done) => { - this.replaceMain(href, flash, (linkRef) => { - if (linkRef === this.linkRef) { - this.currentHistoryPosition++; - this.sessionStorage.setItem( - PHX_LV_HISTORY_POSITION, - this.currentHistoryPosition.toString() - ); - browser_default.updateCurrentState((state) => __spreadProps(__spreadValues({}, state), { - backType: "redirect" - })); - browser_default.pushState( - linkState, - { - type: "redirect", - id: this.main.id, - scroll, - position: this.currentHistoryPosition - }, - href - ); - dom_default.dispatchEvent(window, "phx:navigate", { - detail: { href, patch: false, pop: false, direction: "forward" } - }); - this.registerNewLocation(window.location); - } - if (clickLoading) { - targetEl.classList.remove("phx-click-loading"); - } - done(); - }); - }); - } - registerNewLocation(newLocation) { - const { pathname, search } = this.currentLocation; - if (pathname + search === newLocation.pathname + newLocation.search) { - return false; - } else { - this.currentLocation = clone(newLocation); - return true; - } - } - bindForms() { - let iterations = 0; - let externalFormSubmitted = false; - this.on("submit", (e) => { - const phxSubmit = e.target.getAttribute(this.binding("submit")); - const phxChange = e.target.getAttribute(this.binding("change")); - if (!externalFormSubmitted && phxChange && !phxSubmit) { - externalFormSubmitted = true; - e.preventDefault(); - this.withinOwners(e.target, (view) => { - view.disableForm(e.target); - window.requestAnimationFrame(() => { - if (dom_default.isUnloadableFormSubmit(e)) { - this.unload(); - } - e.target.submit(); - }); - }); - } - }); - this.on("submit", (e) => { - const phxEvent = e.target.getAttribute(this.binding("submit")); - if (!phxEvent) { - if (dom_default.isUnloadableFormSubmit(e)) { - this.unload(); - } - return; - } - e.preventDefault(); - e.target.disabled = true; - this.withinOwners(e.target, (view) => { - js_default.exec(e, "submit", phxEvent, view, e.target, [ - "push", - { submitter: e.submitter } - ]); - }); - }); - for (const type of ["change", "input"]) { - this.on(type, (e) => { - if (e instanceof CustomEvent && (e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement || e.target instanceof HTMLTextAreaElement) && e.target.form === void 0) { - if (e.detail && e.detail.dispatcher) { - throw new Error( - `dispatching a custom ${type} event is only supported on input elements inside a form` - ); - } - return; - } - const phxChange = this.binding("change"); - const input = e.target; - if (this.blockPhxChangeWhileComposing && e.isComposing) { - const key = `composition-listener-${type}`; - if (!dom_default.private(input, key)) { - dom_default.putPrivate(input, key, true); - input.addEventListener( - "compositionend", - () => { - input.dispatchEvent(new Event(type, { bubbles: true })); - dom_default.deletePrivate(input, key); - }, - { once: true } - ); - } - return; - } - const inputEvent = input.getAttribute(phxChange); - const formEvent = input.form && input.form.getAttribute(phxChange); - const phxEvent = inputEvent || formEvent; - if (!phxEvent) { - return; - } - if (input.type === "number" && input.validity && input.validity.badInput) { - return; - } - const dispatcher = inputEvent ? input : input.form; - const currentIterations = iterations; - iterations++; - const { at, type: lastType } = dom_default.private(input, "prev-iteration") || {}; - if (at === currentIterations - 1 && type === "change" && lastType === "input") { - return; - } - dom_default.putPrivate(input, "prev-iteration", { - at: currentIterations, - type - }); - this.debounce(input, e, type, () => { - this.withinOwners(dispatcher, (view) => { - dom_default.putPrivate(input, PHX_HAS_FOCUSED, true); - js_default.exec(e, "change", phxEvent, view, input, [ - "push", - { _target: e.target.name, dispatcher } - ]); - }); - }); - }); - } - this.on("reset", (e) => { - const form = e.target; - dom_default.resetForm(form); - const input = Array.from(form.elements).find((el) => el.type === "reset"); - if (input) { - window.requestAnimationFrame(() => { - input.dispatchEvent( - new Event("input", { bubbles: true, cancelable: false }) - ); - }); - } - }); - } - debounce(el, event, eventType, callback) { - if (eventType === "blur" || eventType === "focusout") { - return callback(); - } - const phxDebounce = this.binding(PHX_DEBOUNCE); - const phxThrottle = this.binding(PHX_THROTTLE); - const defaultDebounce = this.defaults.debounce.toString(); - const defaultThrottle = this.defaults.throttle.toString(); - this.withinOwners(el, (view) => { - const asyncFilter = () => !view.isDestroyed() && document.body.contains(el); - dom_default.debounce( - el, - event, - phxDebounce, - defaultDebounce, - phxThrottle, - defaultThrottle, - asyncFilter, - () => { - callback(); - } - ); - }); - } - silenceEvents(callback) { - this.silenced = true; - callback(); - this.silenced = false; - } - on(event, callback) { - this.boundEventNames.add(event); - window.addEventListener(event, (e) => { - if (!this.silenced) { - callback(e); - } - }); - } - jsQuerySelectorAll(sourceEl, query, defaultQuery) { - const all = this.domCallbacks.jsQuerySelectorAll; - return all ? all(sourceEl, query, defaultQuery) : defaultQuery(); - } - }; - var TransitionSet = class { - constructor() { - this.transitions = /* @__PURE__ */ new Set(); - this.promises = /* @__PURE__ */ new Set(); - this.pendingOps = []; - } - reset() { - this.transitions.forEach((timer) => { - clearTimeout(timer); - this.transitions.delete(timer); - }); - this.promises.clear(); - this.flushPendingOps(); - } - after(callback) { - if (this.size() === 0) { - callback(); - } else { - this.pushPendingOp(callback); - } - } - addTransition(time, onStart, onDone) { - onStart(); - const timer = setTimeout(() => { - this.transitions.delete(timer); - onDone(); - this.flushPendingOps(); - }, time); - this.transitions.add(timer); - } - addAsyncTransition(promise) { - this.promises.add(promise); - promise.then(() => { - this.promises.delete(promise); - this.flushPendingOps(); - }); - } - pushPendingOp(op) { - this.pendingOps.push(op); - } - size() { - return this.transitions.size + this.promises.size; - } - flushPendingOps() { - if (this.size() > 0) { - return; - } - const op = this.pendingOps.shift(); - if (op) { - op(); - this.flushPendingOps(); - } - } - }; - var LiveSocket2 = LiveSocket; - - // js/app.js - var socketPath = document.querySelector("html").getAttribute("phx-socket") || "/live"; - var csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content"); - var Hooks2 = {}; - var editors = {}; - Hooks2.JsonEditor = { - mounted() { - const inputId = this.el.getAttribute("data-input-id"); - const hook = this; - this.editor = new JSONEditor( - this.el, - { - onChangeText: (json2) => { - const target = document.getElementById(inputId); - try { - JSON.parse(json2); - target.value = json2; - target.dispatchEvent( - new Event("change", { bubbles: true, target: this.el.name }) - ); - } catch (_e) { - } - }, - onChange: () => { - try { - const target = document.getElementById(inputId); - json = hook.editor.get(); - target.value = JSON.stringify(json); - target.dispatchEvent( - new Event("change", { bubbles: true, target: this.el.name }) - ); - } catch (_e) { - } - }, - onModeChange: (newMode) => { - hook.mode = newMode; - }, - modes: ["text", "tree"] - }, - JSON.parse(document.getElementById(inputId).value) - ); - editors[this.el.id] = this.editor; - } - }; - Hooks2.JsonEditorSource = { - updated() { - try { - let editor = editors[this.el.getAttribute("data-editor-id")]; - if (editor.getMode() === "tree") { - editor.update(JSON.parse(this.el.value)); - } else { - if (editor.get() !== JSON.parse(this.el.value)) { - editor.setText(this.el.value); - } else { - } - } - } catch (_e) { - } - } - }; - Hooks2.JsonView = { - updated() { - const json2 = JSON.parse(this.el.getAttribute("data-json")); - this.editor = new JSONEditor( - this.el, - { - mode: "preview" - }, - json2 - ); - }, - mounted() { - const json2 = JSON.parse(this.el.getAttribute("data-json")); - this.editor = new JSONEditor( - this.el, - { - mode: "preview" - }, - json2 - ); - } - }; - var init = (element) => new EasyMDE({ - element, - initialValue: element.getAttribute("value") - }); - Hooks2.MarkdownEditor = { - mounted() { - const id = this.el.getAttribute("data-target-id"); - const el = document.getElementById(id); - const easyMDE = init(el); - easyMDE.codemirror.on("change", () => { - el.value = easyMDE.value(); - el.dispatchEvent(new Event("change", { bubbles: true })); - }); - } - }; - Hooks2.Actor = { - mounted() { - this.handleEvent("set_actor", (payload) => { - document.cookie = "actor_resource=" + encodeURIComponent(payload.resource) + ";path=/"; - document.cookie = "actor_primary_key=" + encodeURIComponent(payload.primary_key) + ";path=/"; - document.cookie = "actor_action=" + encodeURIComponent(payload.action) + ";path=/"; - document.cookie = "actor_domain=" + encodeURIComponent(payload.domain) + ";path=/"; - document.cookie = "actor_tenant=" + encodeURIComponent(payload.tenant) + ";path=/"; - }); - this.handleEvent("clear_actor", () => { - document.cookie = "actor_resource=;path=/"; - document.cookie = "actor_primary_key=;path=/"; - document.cookie = "actor_action=;path=/"; - document.cookie = "actor_tenant=;path=/"; - document.cookie = "actor_domain=;path=/"; - document.cookie = "actor_authorizing=false;path=/"; - document.cookie = "actor_paused=true;path=/"; - }); - this.handleEvent("toggle_authorizing", (payload) => { - document.cookie = "actor_authorizing=" + payload.authorizing + ";path=/"; - }); - this.handleEvent("toggle_actor_paused", (payload) => { - document.cookie = "actor_paused=" + payload.actor_paused + ";path=/"; - }); - } - }; - Hooks2.Tenant = { - mounted() { - this.handleEvent("set_tenant", (payload) => { - document.cookie = "tenant=" + payload.tenant + ";path=/"; - }); - this.handleEvent("clear_tenant", () => { - document.cookie = "tenant=;path=/"; - }); - } - }; - Hooks2.MaintainAttrs = { - attrs() { - return this.el.getAttribute("data-attrs").split(", "); - }, - beforeUpdate() { - this.prevAttrs = this.attrs().map((name) => [ - name, - this.el.getAttribute(name) - ]); - }, - updated() { - this.prevAttrs.forEach(([name, val]) => this.el.setAttribute(name, val)); - } - }; - Hooks2.Typeahead = { - mounted() { - this.aborter = new AbortController(); - const signal = this.aborter.signal; - const target_id = this.el.getAttribute("data-target-id"); - const target_el = document.getElementById(target_id); - switch (this.el.tagName) { - case "INPUT": - this.el.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - e.preventDefault(); - } - }, { signal }); - this.el.addEventListener("keyup", (e) => { - switch (e.key) { - case "Enter": - case "Escape": - this.el.blur(); - window.setTimeout(function() { - target_el.dispatchEvent(new Event("input", { bubbles: true })); - }, 750); - break; - } - }, { signal }); - break; - case "LI": - this.el.addEventListener("click", (e) => { - window.setTimeout(function() { - target_el.dispatchEvent(new Event("input", { bubbles: true })); - }, 750); - }, { signal }); - break; - } - }, - updated() { - if (this.el.tagName === "INPUT" && this.el.name.match(/suggest$/) && this.el.value.length === 0) { - this.el.focus(); - } - }, - beforeDestroy() { - if (this.aborter) { - this.aborter.abort(); - } - } - }; - function getCookie(name) { - var re = new RegExp(name + "=([^;]+)"); - var value = re.exec(document.cookie); - return value != null ? decodeURIComponent(value[1]) : null; - } - var params = () => { - return { - _csrf_token: csrfToken, - tenant: getCookie("tenant"), - actor_resource: getCookie("actor_resource"), - actor_primary_key: getCookie("actor_primary_key"), - actor_tenant: getCookie("actor_tenant"), - actor_action: getCookie("actor_action"), - actor_domain: getCookie("actor_domain"), - actor_authorizing: getCookie("actor_authorizing"), - actor_paused: getCookie("actor_paused") - }; - }; - var liveSocket = new LiveSocket2(socketPath, Socket, { - params, - hooks: Hooks2, - dom: { - onBeforeElUpdated(from, to) { - if (from._x_dataStack) { - window.Alpine.clone(from, to); - } - } - } - }); - import_topbar.default.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }); - window.addEventListener("phx:page-loading-start", (_info) => import_topbar.default.show(300)); - window.addEventListener("phx:page-loading-stop", (_info) => import_topbar.default.hide()); - liveSocket.connect(); - liveSocket.enableDebug(); - window.liveSocket = liveSocket; -})(); +(()=>{var N=Object.create;var y=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,O=Object.prototype.hasOwnProperty;var L=(e,a)=>()=>(a||e((a={exports:{}}).exports,a),a.exports);var q=(e,a,n,o)=>{if(a&&typeof a=="object"||typeof a=="function")for(let c of x(a))!O.call(e,c)&&c!==n&&y(e,c,{get:()=>a[c],enumerable:!(o=F(a,c))||o.enumerable});return e};var R=(e,a,n)=>(n=e!=null?N(J(e)):{},q(a||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e));var T=L((A,g)=>{(function(e,a){"use strict";(function(){for(var t=0,i=["ms","moz","webkit","o"],s=0;sl.show(),t)}else c=!0,m!==null&&e.cancelAnimationFrame(m),n||S(),n.style.opacity=1,n.style.display="block",l.progress(0),r.autoRun&&function i(){h=e.requestAnimationFrame(i),l.progress("+"+.05*Math.pow(1-Math.sqrt(o),2))}()},progress:function(t){return typeof t=="undefined"||(typeof t=="string"&&(t=(t.indexOf("+")>=0||t.indexOf("-")>=0?o:0)+parseFloat(t)),o=t>1?1:t,E()),o},hide:function(){clearTimeout(p),p=null,c&&(c=!1,h!=null&&(e.cancelAnimationFrame(h),h=null),function t(){if(l.progress("+.1")>=1&&(n.style.opacity-=.05,n.style.opacity<=.05)){n.style.display="none",m=null;return}m=e.requestAnimationFrame(t)}())}};typeof g=="object"&&typeof g.exports=="object"?g.exports=l:typeof define=="function"&&define.amd?define(function(){return l}):this.topbar=l}).call(A,window,document)});var f=R(T()),M=document.querySelector("html").getAttribute("phx-socket")||"/live",U=document.querySelector("meta[name='csrf-token']").getAttribute("content"),u={},C={};u.JsonEditor={mounted(){let e=this.el.getAttribute("data-input-id"),a=this;this.editor=new JSONEditor(this.el,{onChangeText:n=>{let o=document.getElementById(e);try{JSON.parse(n),o.value=n,o.dispatchEvent(new Event("change",{bubbles:!0,target:this.el.name}))}catch(c){}},onChange:()=>{try{let n=document.getElementById(e);json=a.editor.get(),n.value=JSON.stringify(json),n.dispatchEvent(new Event("change",{bubbles:!0,target:this.el.name}))}catch(n){}},onModeChange:n=>{a.mode=n},modes:["text","tree"]},JSON.parse(document.getElementById(e).value)),C[this.el.id]=this.editor}};u.JsonEditorSource={updated(){try{let e=C[this.el.getAttribute("data-editor-id")];e.getMode()==="tree"?e.update(JSON.parse(this.el.value)):e.get()!==JSON.parse(this.el.value)&&e.setText(this.el.value)}catch(e){}}};u.JsonView={updated(){let e=JSON.parse(this.el.getAttribute("data-json"));this.editor=new JSONEditor(this.el,{mode:"preview"},e)},mounted(){let e=JSON.parse(this.el.getAttribute("data-json"));this.editor=new JSONEditor(this.el,{mode:"preview"},e)}};var z=e=>new EasyMDE({element:e,initialValue:e.getAttribute("value")});u.MarkdownEditor={mounted(){let e=this.el.getAttribute("data-target-id"),a=document.getElementById(e),n=z(a);n.codemirror.on("change",()=>{a.value=n.value(),a.dispatchEvent(new Event("change",{bubbles:!0}))})}};u.Actor={mounted(){this.handleEvent("set_actor",e=>{document.cookie="actor_resource="+encodeURIComponent(e.resource)+";path=/",document.cookie="actor_primary_key="+encodeURIComponent(e.primary_key)+";path=/",document.cookie="actor_action="+encodeURIComponent(e.action)+";path=/",document.cookie="actor_domain="+encodeURIComponent(e.domain)+";path=/",document.cookie="actor_tenant="+encodeURIComponent(e.tenant)+";path=/"}),this.handleEvent("clear_actor",()=>{document.cookie="actor_resource=;path=/",document.cookie="actor_primary_key=;path=/",document.cookie="actor_action=;path=/",document.cookie="actor_tenant=;path=/",document.cookie="actor_domain=;path=/",document.cookie="actor_authorizing=false;path=/",document.cookie="actor_paused=true;path=/"}),this.handleEvent("toggle_authorizing",e=>{document.cookie="actor_authorizing="+e.authorizing+";path=/"}),this.handleEvent("toggle_actor_paused",e=>{document.cookie="actor_paused="+e.actor_paused+";path=/"})}};u.Tenant={mounted(){this.handleEvent("set_tenant",e=>{document.cookie="tenant="+e.tenant+";path=/"}),this.handleEvent("clear_tenant",()=>{document.cookie="tenant=;path=/"})}};u.MaintainAttrs={attrs(){return this.el.getAttribute("data-attrs").split(", ")},beforeUpdate(){this.prevAttrs=this.attrs().map(e=>[e,this.el.getAttribute(e)])},updated(){this.prevAttrs.forEach(([e,a])=>this.el.setAttribute(e,a))}};u.Typeahead={mounted(){this.aborter=new AbortController;let e=this.aborter.signal,a=this.el.getAttribute("data-target-id"),n=document.getElementById(a);switch(this.el.tagName){case"INPUT":this.el.addEventListener("keydown",o=>{o.key==="Enter"&&o.preventDefault()},{signal:e}),this.el.addEventListener("keyup",o=>{switch(o.key){case"Enter":case"Escape":this.el.blur(),window.setTimeout(function(){n.dispatchEvent(new Event("input",{bubbles:!0}))},750);break}},{signal:e});break;case"LI":this.el.addEventListener("click",o=>{window.setTimeout(function(){n.dispatchEvent(new Event("input",{bubbles:!0}))},750)},{signal:e});break}},updated(){this.el.tagName==="INPUT"&&this.el.name.match(/suggest$/)&&this.el.value.length===0&&this.el.focus()},beforeDestroy(){this.aborter&&this.aborter.abort()}};function d(e){var a=new RegExp(e+"=([^;]+)"),n=a.exec(document.cookie);return n!=null?decodeURIComponent(n[1]):null}var B=()=>({_csrf_token:U,tenant:d("tenant"),actor_resource:d("actor_resource"),actor_primary_key:d("actor_primary_key"),actor_tenant:d("actor_tenant"),actor_action:d("actor_action"),actor_domain:d("actor_domain"),actor_authorizing:d("actor_authorizing"),actor_paused:d("actor_paused")}),_=new LiveView.LiveSocket(M,Phoenix.Socket,{params:B,hooks:u,dom:{onBeforeElUpdated(e,a){e._x_dataStack&&window.Alpine.clone(e,a)}}});f.default.config({barColors:{0:"#29d"},shadowColor:"rgba(0, 0, 0, .3)"});window.addEventListener("phx:page-loading-start",e=>f.default.show(300));window.addEventListener("phx:page-loading-stop",e=>f.default.hide());_.connect();_.enableDebug();window.liveSocket=_;})(); /** * @license MIT * topbar 2.0.0, 2023-02-04 diff --git a/priv/static/assets/app.js.gz b/priv/static/assets/app.js.gz index b565ad323ddf96b69ea933e16b38fdb9be1b96d8..439fe070f6a02702608c3aca2cc3cafc59a86463 100644 GIT binary patch literal 2940 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 literal 42130 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 diff --git a/priv/static/cache_manifest.json b/priv/static/cache_manifest.json index 3a571db2..3af22158 100644 --- a/priv/static/cache_manifest.json +++ b/priv/static/cache_manifest.json @@ -1,6 +1,6 @@ { "!comment!":"This file was auto-generated by `mix phx.digest`. Remove it and all generated artefacts with `mix phx.digest.clean --all`", "version":1, - "latest":{"assets/app.css":"assets/app-527045e61d86a087f76190b77eb572db.css","assets/app.js":"assets/app-24cb6821b8636501c2644f581a96fe0b.js"}, - "digests":{"assets/app-24cb6821b8636501c2644f581a96fe0b.js":{"size":136085,"sha512":"mFBN6hBYIWPMIkeHD2DsB4YiiqS91H7on6I5BcqxHQAoehGsK1QUXp1BQjVBkxIgc8hM9dzyz/F86x7ft+AFfQ==","mtime":63919980878,"digest":"24cb6821b8636501c2644f581a96fe0b","logical_path":"assets/app.js"},"assets/app-527045e61d86a087f76190b77eb572db.css":{"size":38897,"sha512":"3v8ZyYm5MjgU1AZ554TugdyTaFuJO9a6DA4thXbMTcLwoZbkmEkY4Al2wjzaE16sDxUTWg238BB81rWhJ6w7Xg==","mtime":63919980878,"digest":"527045e61d86a087f76190b77eb572db","logical_path":"assets/app.css"},"assets/app-5278a18515691be5786defb2425e6b76.js":{"digest":"5278a18515691be5786defb2425e6b76","logical_path":"assets/app.js","mtime":63919980409,"sha512":"3VfzmE1ZogLjV+6fHoWq9JKQfRf0LewpBprHrH7sumjEyif9aUv9acfr3WSNFII223jDKcdi2IcYdVCCq7lIgA==","size":129556}} + "latest":{"assets/app.css":"assets/app-838eaf8d64c5abca998a381a599c8faa.css","assets/app.js":"assets/app-de4d120e56f06df177eefb5ebee931d0.js","assets/favicon.ico":"assets/favicon-d41d8cd98f00b204e9800998ecf8427e.ico"}, + "digests":{"assets/app-838eaf8d64c5abca998a381a599c8faa.css":{"size":69748,"sha512":"l1yFCPlgYowO4IA9setB/EBgYPEDuuQwpfO1d1jOumd2KbImkOXfLV7t2eF0WGf8fVLypQIEpn2CKQRr2EKz0Q==","mtime":63921448643,"digest":"838eaf8d64c5abca998a381a599c8faa","logical_path":"assets/app.css"},"assets/app-de4d120e56f06df177eefb5ebee931d0.js":{"size":7598,"sha512":"MKAmQyiM8SAnodCSsdo+esQhnKvu68zj+xWF2gAQLnkj6jl0ZJTyqOZA+9DeS+vrySOlJM3pT5AJu7U7uNcAJw==","mtime":63921448643,"digest":"de4d120e56f06df177eefb5ebee931d0","logical_path":"assets/app.js"},"assets/favicon-d41d8cd98f00b204e9800998ecf8427e.ico":{"size":0,"sha512":"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==","mtime":63921448643,"digest":"d41d8cd98f00b204e9800998ecf8427e","logical_path":"assets/favicon.ico"}} } From bfeafcf63e18b6edad28a0552304061736f3df34 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sun, 3 Aug 2025 22:15:40 +0800 Subject: [PATCH 5/8] Use the AshAdminTheme as the default Cinder theme --- config/config.exs | 2 +- lib/ash_admin/components/resource/data_table.ex | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config/config.exs b/config/config.exs index 7584f898..bdceaa3e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -45,7 +45,7 @@ config :tailwind, config :logger, level: :debug config :phoenix, :serve_endpoints, true -config :cinder, default_theme: "modern" +config :cinder, default_theme: AshAdmin.Themes.AshAdminTheme if config_env() == :dev do config :ash_admin, diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index a0c8b38a..e52c6f39 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -4,7 +4,6 @@ defmodule AshAdmin.Components.Resource.DataTable do import AshAdmin.Helpers alias AshAdmin.Components.Resource.Table - alias AshAdmin.Themes.AshAdminTheme attr :resource, :atom attr :domain, :atom @@ -109,7 +108,6 @@ defmodule AshAdmin.Components.Resource.DataTable do :if={@ash_query && match?({:ok, _data}, @data)} query={@ash_query} actor={@actor} - theme={AshAdminTheme} id={"cinder-table-#{@resource}"} > From 733fbd34095a5d2e3df2e76912ff785b31bcf946 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sun, 3 Aug 2025 22:24:49 +0800 Subject: [PATCH 6/8] Use a released version of Cinder --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index f973d026..2df13a65 100644 --- a/mix.exs +++ b/mix.exs @@ -122,7 +122,7 @@ defmodule AshAdmin.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:cinder, github: "sevenseacat/cinder"}, + {:cinder, "~> 0.5"}, {:ash, "~> 3.0 and >= 3.4.63"}, {:ash_phoenix, "~> 2.1 and >= 2.1.8"}, {:phoenix_view, "~> 2.0"}, diff --git a/mix.lock b/mix.lock index d9f1302a..cbd80a8d 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "cinder": {:git, "https://github.com/sevenseacat/cinder.git", "51ae167876d3370dc5a531aa5b73a5d4efdf07b6", []}, + "cinder": {:hex, :cinder, "0.5.1", "ad02f1342536863001c9a90ee027374d1a64c62778c51bd0c8f7f8dd6192828f", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, "~> 2.3", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "33617d43e0cbb3be2af05faf31f1c34ffdd62f6e78ce58430ed0112218570507"}, "cowboy": {:hex, :cowboy, "2.13.0", "09d770dd5f6a22cc60c071f432cd7cb87776164527f205c5a6b0f24ff6b38990", [:make, :rebar3], [{:cowlib, ">= 2.14.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "e724d3a70995025d654c1992c7b11dbfea95205c047d86ff9bf1cda92ddc5614"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.15.0", "3c97a318a933962d1c12b96ab7c1d728267d2c523c25a5b57b0f93392b6e9e25", [:make, :rebar3], [], "hexpm", "4f00c879a64b4fe7c8fcb42a4281925e9ffdb928820b03c3ad325a617e857532"}, From d3e667068ddda5bd12f28dd34bd8162fc16a1b27 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sun, 3 Aug 2025 22:43:13 +0800 Subject: [PATCH 7/8] Fix Spark cheat sheets, formatting, Credo issues, etc. --- .formatter.exs | 2 ++ documentation/dsls/DSL-AshAdmin.Resource.md | 2 ++ lib/ash_admin/components/resource/data_table.ex | 12 ++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.formatter.exs b/.formatter.exs index 853c82b8..d1e484c7 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -24,6 +24,8 @@ spark_locals_without_parens = [ show_resources: 1, show_sensitive_fields: 1, table_columns: 1, + table_filterable_columns: 1, + table_sortable_columns: 1, type: 1, update_actions: 1 ] diff --git a/documentation/dsls/DSL-AshAdmin.Resource.md b/documentation/dsls/DSL-AshAdmin.Resource.md index 38a788c6..8e2d4f18 100644 --- a/documentation/dsls/DSL-AshAdmin.Resource.md +++ b/documentation/dsls/DSL-AshAdmin.Resource.md @@ -32,6 +32,8 @@ Configure the admin dashboard for a given resource. | [`polymorphic_tables`](#admin-polymorphic_tables){: #admin-polymorphic_tables } | `list(String.t)` | | For resources that use ash_postgres' polymorphism capabilities, you can provide a list of tables that should be available to select. These will be added to the list of derivable tables based on scanning all domains and resources provided to ash_admin. | | [`polymorphic_actions`](#admin-polymorphic_actions){: #admin-polymorphic_actions } | `list(atom)` | | For resources that use ash_postgres' polymorphism capabilities, you can provide a list of actions that should require a table to be set. If this is not set, then *all* actions will require tables. | | [`table_columns`](#admin-table_columns){: #admin-table_columns } | `list(atom)` | | The list of attributes to render on the table view. | +| [`table_sortable_columns`](#admin-table_sortable_columns){: #admin-table_sortable_columns } | `list(atom)` | | The list of columns that can be sorted. If not specified, no columns are sortable. | +| [`table_filterable_columns`](#admin-table_filterable_columns){: #admin-table_filterable_columns } | `list(atom)` | | The list of columns that can be filtered. If not specified, no columns are filterable. | | [`format_fields`](#admin-format_fields){: #admin-format_fields } | `list(any)` | | The list of fields and their formats represented as a MFA. For example: `updated_at: {Timex, :format!, ["{0D}-{0M}-{YYYY} {h12}:{m} {AM}"]}`. Datatable pages format all given fields. Show and Update pages format given read-only fields of types `Ash.Type.Date`, `Ash.Type.DateTime`, `Ash.Type.Time`, `Ash.Type.NaiveDatetime`, `Ash.Type.UtcDatetime` and `Ash.Type.UtcDatetimeUsec`. | | [`relationship_display_fields`](#admin-relationship_display_fields){: #admin-relationship_display_fields } | `list(atom)` | | The list of attributes to render when this resource is shown as a relationship on another resource's datatable. | | [`resource_group`](#admin-resource_group){: #admin-resource_group } | `atom` | | The group in the top resource dropdown that the resource appears in. | diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index e52c6f39..a3be1174 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -116,13 +116,13 @@ defmodule AshAdmin.Components.Resource.DataTable do :for={field_name <- AshAdmin.Resource.table_columns(@resource)} field={to_string(field_name)} label={to_name(field_name)} - filter={is_filterable?(@resource, field_name)} - sort={is_sortable?(@resource, field_name)} + filter={filterable?(@resource, field_name)} + sort={sortable?(@resource, field_name)} > {render_field_value(record, field_name, assigns)} - - + + <:col :let={record} :if={actions?(@resource)} label="Actions">

@@ -472,7 +472,7 @@ defmodule AshAdmin.Components.Resource.DataTable do end # Check if a field should be sortable - defp is_sortable?(resource, field_name) do + defp sortable?(resource, field_name) do sortable_columns = AshAdmin.Resource.table_sortable_columns(resource) case sortable_columns do @@ -483,7 +483,7 @@ defmodule AshAdmin.Components.Resource.DataTable do end # Check if a field should be filterable - defp is_filterable?(resource, field_name) do + defp filterable?(resource, field_name) do filterable_columns = AshAdmin.Resource.table_filterable_columns(resource) case filterable_columns do From 2e00d52e65d730e6a5e145246e29c3d73f56e6c1 Mon Sep 17 00:00:00 2001 From: Rebecca Le Date: Sun, 3 Aug 2025 23:18:29 +0800 Subject: [PATCH 8/8] Revert "Use the AshAdminTheme as the default Cinder theme" This reverts commit bfeafcf63e18b6edad28a0552304061736f3df34. --- config/config.exs | 2 -- lib/ash_admin/components/resource/data_table.ex | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index bdceaa3e..53cd51f1 100644 --- a/config/config.exs +++ b/config/config.exs @@ -45,8 +45,6 @@ config :tailwind, config :logger, level: :debug config :phoenix, :serve_endpoints, true -config :cinder, default_theme: AshAdmin.Themes.AshAdminTheme - if config_env() == :dev do config :ash_admin, ash_domains: [ diff --git a/lib/ash_admin/components/resource/data_table.ex b/lib/ash_admin/components/resource/data_table.ex index a3be1174..99312a26 100644 --- a/lib/ash_admin/components/resource/data_table.ex +++ b/lib/ash_admin/components/resource/data_table.ex @@ -4,6 +4,7 @@ defmodule AshAdmin.Components.Resource.DataTable do import AshAdmin.Helpers alias AshAdmin.Components.Resource.Table + alias AshAdmin.Themes.AshAdminTheme attr :resource, :atom attr :domain, :atom @@ -108,6 +109,7 @@ defmodule AshAdmin.Components.Resource.DataTable do :if={@ash_query && match?({:ok, _data}, @data)} query={@ash_query} actor={@actor} + theme={AshAdminTheme} id={"cinder-table-#{@resource}"} >