Packages

Rapid admin generator for Phoenix

Current section

3 Versions

Jump to

Compare versions

26 files changed
+838 additions
-262 deletions
  @@ -5,10 +5,10 @@
5 5
6 6 # Lit
7 7
8 - Lit is a rapid admin generator for Phoenix apps. It creates custom templates and relies
9 - on the Phoenix html generator under the hood.
8 + Lit is an admin generator for Phoenix LiveView and Talwind apps. It creates custom templates and relies
9 + on the Phoenix live generator under the hood. The project is a fork from [Torch](https://github.com/mojotech/torch)
10 10
11 - ![image](https://user-images.githubusercontent.com/7085617/36333572-70e3907e-132c-11e8-9ad2-bd5e98aadc7c.png)
11 + ![image](https://res.cloudinary.com/dwvh1fhcg/image/upload/v1606940594/test/lit_admin.png.png)
12 12
13 13 ## Installation
14 14
  @@ -19,47 +19,31 @@ To install Lit, perform the following steps:
19 19 ```elixir
20 20 def deps do
21 21 [
22 - {:lit, "~> 3.4"}
22 + {:lit, "~> 0.1.0"}
23 23 ]
24 24 end
25 25 ```
26 26
27 - 2. Add a `Plug.Static` plug to your `endpoint.ex`:
28 -
29 - ```elixir
30 - plug(
31 - Plug.Static,
32 - at: "/lit",
33 - from: {:lit, "priv/static"},
34 - gzip: true,
35 - cache_control_for_etags: "public, max-age=86400"
36 - )
37 - ```
38 -
39 - 3. Configure Lit by adding the following to your `config.exs`.
27 + 2. Configure Lit by adding the following to your `config.exs`.
40 28
41 29 ```
42 30 config :lit,
43 31 otp_app: :my_app_name,
44 - template_format: "eex" || "slime"
32 + default_web_namespace: "admin"
45 33 ```
46 34
47 - 4. Run `mix lit.install`
48 -
49 - **NOTE:** You can also choose to use `slime` templates, but you will need to
50 - first [install Phoenix Slime](https://github.com/slime-lang/phoenix_slime) and
51 - then update your configuration to specify `template_format: "slime"`.
35 + 3. Run `mix lit.install`
52 36
53 37 Now you're ready to start generating your admin! :tada:
54 38
55 39 ## Usage
56 40
57 41 Lit uses Phoenix generators under the hood. Lit injects it's own custom templates
58 - into your `priv/static` directory, then runs the `mix phx.gen.html` task with the options
42 + into your `priv/static` directory, then runs the `mix phx.gen.live` task with the options
59 43 you passed in. Finally, it uninstalls the custom templates so they don't interfere with
60 44 running the plain Phoenix generators.
61 45
62 - In light of that fact, the `lit.gen.html` task takes all the same arguments as the `phx.gen.html`,
46 + In light of that fact, the `lit.gen.live` task takes all the same arguments as the `phx.gen.live`,
63 47 but does some extra configuration on either end. Checkout `mix help phx.gen.html` for more details
64 48 about the supported options and format.
65 49
  @@ -76,21 +60,9 @@ Add the resource to your browser scope in lib/my_app_web/router.ex:
76 60
77 61 resources "/posts", PostController
78 62
79 - Ensure the following is added to your endpoint.ex:
80 -
81 - plug(
82 - Plug.Static,
83 - at: "/lit",
84 - from: {:lit, "priv/static"},
85 - gzip: true,
86 - cache_control_for_etags: "public, max-age=86400",
87 - headers: [{"access-control-allow-origin", "*"}]
88 - )
89 -
90 - :fire: Lit generated html for Posts! :fire:
91 63 ```
92 64
93 - Lit also installed an admin layout into your `my_app_web/templates/layout/lit.html.eex`.
65 + Lit also installed an admin layout into your `my_app_web/templates/admin/layout/lit.html.leex`.
94 66 You will want to update it to include your new navigation link:
95 67
96 68 ```
  @@ -172,48 +144,4 @@ the above `eex` to work.
172 144
173 145 ## Styling
174 146
175 - Lit generates two CSS themes you can use: `base.css` & `theme.css`.
176 - The base styles are basically bare bones, and the theme styles look like the screenshot
177 - above. Just change the stylesheet link in the `lit.html.eex` layout.
178 -
179 - If you want to use the theme, but override the colors, you'll need to include your
180 - own stylesheet with the specific overrides.
181 -
182 - ## Internationalization
183 -
184 - Lit comes with `.po` files for `en`, `ru`, `es` and `de` locales. If you are using
185 - lit and can provide us with translation files for other languages, please
186 - submit a Pull Request with the translation file. We'd love to add as many
187 - translations as possible.
188 -
189 - If you wish to add your own customized translations, you can configure Lit to
190 - use your own custom `MessagesBackend` and adding it in your Lit configuration
191 - settings in `config.exs`. You can find the all messages that can be customized
192 - in the default [i18n/backend.ex](lib/lit/i18n/backend.ex) file.
193 -
194 - If you are customizing a backend for a "standard" spoken language, please submit
195 - back a proper `.po` translation file for us to include in the official Lit
196 - releases so other users can take advantage.
197 -
198 - **Example**
199 -
200 - ```elixir
201 - defmodule MyApp.CustomMessagesBackend do
202 - def message("Contains"), do: "** CUSTOM Contains **"
203 - def message("Equals"), do: "** CUSTOM Equals ****"
204 - def message("< Prev"), do: "<--"
205 - def message("Next >"), do: "-->"
206 -
207 - # You can add a fallback so it won't break with newly added messages or
208 - # messages you did not customize
209 - def message(text), do: Lit.I18n.Backend.message(text)
210 - end
211 - ```
212 -
213 - ```elixir
214 - # config.exs
215 - config :lit,
216 - otp_app: :my_app_name,
217 - i18n_backend: MyApp.CustomMessagesBackend
218 - template_format: "eex" || "slime"
219 - ```
147 + Lit depends on that you use Tailwind in your application. The generated codes has comes with utilitu classes.
  @@ -12,8 +12,9 @@
12 12 <<"lib/lit/i18n/backend.ex">>,<<"lib/lit/views">>,
13 13 <<"lib/lit/views/page_view.ex">>,<<"lib/lit/views/flash_view.ex">>,
14 14 <<"lib/lit/views/table_view.ex">>,<<"lib/lit/views/filter_view.ex">>,
15 - <<"lib/lit/views/pagination_view.ex">>,<<"lib/lit/i18n.ex">>,<<"lib/mix">>,
16 - <<"lib/mix/tasks">>,<<"lib/mix/tasks/lit.uninstall.ex">>,
15 + <<"lib/lit/views/pagination_view.ex">>,<<"lib/lit/i18n.ex">>,
16 + <<"lib/lit_web">>,<<"lib/lit_web/live">>,<<"lib/lit_web/live/admin">>,
17 + <<"lib/mix">>,<<"lib/mix/tasks">>,<<"lib/mix/tasks/lit.uninstall.ex">>,
17 18 <<"lib/mix/tasks/lit.gen.live.ex">>,<<"lib/mix/tasks/lit.install.ex">>,
18 19 <<"lib/mix/tasks/lit.gen.html.ex">>,<<"lib/mix/lit.ex">>,
19 20 <<"lib/gettext.ex">>,<<"priv">>,<<"priv/gettext">>,
  @@ -30,8 +31,9 @@
30 31 <<"priv/static/lit.js">>,<<"priv/static/theme.css.map">>,
31 32 <<"priv/static/base.css.map">>,<<"priv/static/lit-logo.png">>,
32 33 <<"priv/static/lit.js.map">>,<<"priv/static/base.css">>,
33 - <<"priv/templates">>,<<"priv/templates/slime">>,
34 - <<"priv/templates/slime/phx.gen.html">>,
34 + <<"priv/templates">>,<<"priv/templates/lit.install">>,
35 + <<"priv/templates/lit.install/pagination_component.ex">>,
36 + <<"priv/templates/slime">>,<<"priv/templates/slime/phx.gen.html">>,
35 37 <<"priv/templates/slime/phx.gen.html/edit.html.slime">>,
36 38 <<"priv/templates/slime/phx.gen.html/index.html.slime">>,
37 39 <<"priv/templates/slime/phx.gen.html/form.html.slime">>,
  @@ -51,6 +53,16 @@
51 53 <<"priv/templates/common/phx.gen.html/controller.ex">>,
52 54 <<"priv/templates/common/phx.gen.html/controller_test.exs">>,
53 55 <<"priv/templates/common/phx.gen.html/view.ex">>,
56 + <<"priv/templates/phx.gen.live">>,
57 + <<"priv/templates/phx.gen.live/modal_component.ex">>,
58 + <<"priv/templates/phx.gen.live/index.html.leex">>,
59 + <<"priv/templates/phx.gen.live/show.ex">>,
60 + <<"priv/templates/phx.gen.live/live_test.exs">>,
61 + <<"priv/templates/phx.gen.live/index.ex">>,
62 + <<"priv/templates/phx.gen.live/form_component.html.leex">>,
63 + <<"priv/templates/phx.gen.live/show.html.leex">>,
64 + <<"priv/templates/phx.gen.live/form_component.ex">>,
65 + <<"priv/templates/phx.gen.live/live_helpers.ex">>,
54 66 <<"priv/templates/phx.gen.context">>,
55 67 <<"priv/templates/phx.gen.context/_grid.sass">>,
56 68 <<"priv/templates/phx.gen.context/test_cases.exs">>,
  @@ -93,4 +105,4 @@
93 105 {<<"optional">>,false},
94 106 {<<"repository">>,<<"hexpm">>},
95 107 {<<"requirement">>,<<"~> 3.1">>}]]}.
96 - {<<"version">>,<<"0.1.0">>}.
108 + {<<"version">>,<<"0.1.1">>}.
  @@ -39,7 +39,7 @@ defmodule Lit.FilterView do
39 39
40 40 iex> params = %{"post" => %{"title_contains" => "test"}}
41 41 ...> filter_select(:post, :title, params) |> safe_to_string()
42 - "<select class=\\"filter-type\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[title_contains]\\" selected>Contains</option><option value=\\"post[title_equals]\\">Equals</option></select>"
42 + "<select class=\\"focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[title_contains]\\" selected>Contains</option><option value=\\"post[title_equals]\\">Equals</option></select>"
43 43 """
44 44 @spec filter_select(prefix, field, map) :: Phoenix.HTML.safe()
45 45 def filter_select(prefix, field, params) do
  @@ -51,7 +51,7 @@ defmodule Lit.FilterView do
51 51 {message("Equals"), "#{prefix}[#{field}_equals]"}
52 52 ]
53 53
54 - select(:filters, "", opts, class: "filter-type", value: "#{prefix}[#{selected}]")
54 + select(:filters, "", opts, class: "focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3", value: "#{prefix}[#{selected}]")
55 55 end
56 56
57 57 @doc """
  @@ -62,7 +62,7 @@ defmodule Lit.FilterView do
62 62
63 63 iex> params = %{"post" => %{"updated_at_after" => "01/01/2019"}}
64 64 ...> filter_date_select(:post, :updated_at, params) |> safe_to_string()
65 - "<select class=\\"filter-type\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[updated_at_before]\\">Before</option><option value=\\"post[updated_at_after]\\" selected>After</option></select>"
65 + "<select class=\\"focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[updated_at_before]\\">Before</option><option value=\\"post[updated_at_after]\\" selected>After</option></select>"
66 66 """
67 67 @spec filter_date_select(prefix, field, map) :: Phoenix.HTML.safe()
68 68 def filter_date_select(prefix, field, params) do
  @@ -74,7 +74,7 @@ defmodule Lit.FilterView do
74 74 {message("After"), "#{prefix}[#{field}_after]"}
75 75 ]
76 76
77 - select(:filters, "", opts, class: "filter-type", value: "#{prefix}[#{selected}]")
77 + select(:filters, "", opts, class: "focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3", value: "#{prefix}[#{selected}]")
78 78 end
79 79
80 80 @doc """
  @@ -84,7 +84,7 @@ defmodule Lit.FilterView do
84 84
85 85 iex> params = %{"post" => %{"rating_greater_than" => 0}}
86 86 ...> number_filter_select(:post, :rating, params) |> safe_to_string()
87 - "<select class=\\"filter-type\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[rating_equals]\\">Equals</option><option value=\\"post[rating_greater_than]\\" selected>Greater Than</option><option value=\\"post[rating_greater_than_or]\\">Greater Than Or Equal</option><option value=\\"post[rating_less_than]\\">Less Than</option></select>"
87 + "<select class=\\"focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3\\" id=\\"filters_\\" name=\\"filters[]\\"><option value=\\"post[rating_equals]\\">Equals</option><option value=\\"post[rating_greater_than]\\" selected>Greater Than</option><option value=\\"post[rating_greater_than_or]\\">Greater Than Or Equal</option><option value=\\"post[rating_less_than]\\">Less Than</option></select>"
88 88 """
89 89 @spec number_filter_select(prefix, field, map) :: Phoenix.HTML.safe()
90 90 def number_filter_select(prefix, field, params) do
  @@ -98,7 +98,7 @@ defmodule Lit.FilterView do
98 98 {message("Less Than"), "#{prefix}[#{field}_less_than]"}
99 99 ]
100 100
101 - select(:filters, "", opts, class: "filter-type", value: "#{prefix}[#{selected}]")
101 + select(:filters, "", opts, class: "focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3", value: "#{prefix}[#{selected}]")
102 102 end
103 103
104 104 @doc """
  @@ -108,13 +108,13 @@ defmodule Lit.FilterView do
108 108
109 109 iex> params = %{"post" => %{"rating_equals" => 5}}
110 110 ...> filter_number_input(:post, :rating, params) |> safe_to_string()
111 - "<input id=\\"post_rating_equals\\" name=\\"post[rating_equals]\\" type=\\"number\\" value=\\"5\\">"
111 + "<input class=\\"focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3\\" id=\\"post_rating_equals\\" name=\\"post[rating_equals]\\" type=\\"number\\" value=\\"5\\">"
112 112 """
113 113 @spec filter_number_input(prefix, field, map) :: Phoenix.HTML.safe()
114 114 def filter_number_input(prefix, field, params) do
115 115 prefix_str = to_string(prefix)
116 116 {name, value} = find_param(params[prefix_str], field, :number)
117 - text_input(prefix, String.to_atom(name), value: value, type: "number")
117 + text_input(prefix, String.to_atom(name), value: value, type: "number", class: "focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3")
118 118 end
119 119
120 120 @doc """
  @@ -124,13 +124,13 @@ defmodule Lit.FilterView do
124 124
125 125 iex> params = %{"post" => %{"title_contains" => "test"}}
126 126 iex> filter_string_input(:post, :title, params) |> safe_to_string()
127 - "<input id=\\"post_title_contains\\" name=\\"post[title_contains]\\" type=\\"text\\" value=\\"test\\">"
127 + "<input class=\\"focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3\\" id=\\"post_title_contains\\" name=\\"post[title_contains]\\" type=\\"text\\" value=\\"test\\">"
128 128 """
129 129 @spec filter_string_input(prefix, field, map) :: Phoenix.HTML.safe()
130 130 def filter_string_input(prefix, field, params) do
131 131 prefix_str = to_string(prefix)
132 132 {name, value} = find_param(params[prefix_str], field)
133 - text_input(prefix, String.to_atom(name), value: value)
133 + text_input(prefix, String.to_atom(name), value: value, class: "focus:border-blue-400 focus:ring-2 focus:ring-blue-200 focus:outline-none w-full text-base placeholder-gray-400 border border-gray-300 rounded py-1.5 px-3")
134 134 end
135 135
136 136 @doc """
  @@ -207,7 +207,7 @@ defmodule Lit.FilterView do
207 207
208 208 iex> params = %{"post" => %{"draft_equals" => "false"}}
209 209 iex> filter_boolean_input(:post, :draft, params) |> safe_to_string()
210 - "<input name=\\"post[draft_equals]\\" type=\\"hidden\\" value=\\"false\\"><input id=\\"post_draft_equals\\" name=\\"post[draft_equals]\\" type=\\"checkbox\\" value=\\"true\\">"
210 + "<input name=\\"post[draft_equals]\\" type=\\"hidden\\" value=\\"false\\"><input class=\\"form-checkbox\\" id=\\"post_draft_equals\\" name=\\"post[draft_equals]\\" type=\\"checkbox\\" value=\\"true\\">"
211 211 """
212 212 @spec filter_boolean_input(prefix, field, map) :: Phoenix.HTML.safe()
213 213 def filter_boolean_input(prefix, field, params) do
  @@ -217,7 +217,7 @@ defmodule Lit.FilterView do
217 217 string when is_binary(string) -> string == "true"
218 218 end
219 219
220 - checkbox(prefix, :"#{field}_equals", value: value)
220 + checkbox(prefix, :"#{field}_equals", value: value, class: "form-checkbox")
221 221 end
222 222
223 223 defp lit_date_input(name, value) do
  @@ -90,6 +90,20 @@ defmodule Mix.Lit do
90 90 ])
91 91 end
92 92
93 + def inject_templates("phx.gen.live", _) do
94 + copy_from("priv/templates/phx.gen.live", [
95 + {"show.ex", "priv/templates/phx.gen.live/show.ex"},
96 + {"index.ex", "priv/templates/phx.gen.live/index.ex"},
97 + {"form_component.ex", "priv/templates/phx.gen.live/form_component.ex"},
98 + {"form_component.html.leex", "priv/templates/phx.gen.live/form_component.html.leex"},
99 + {"index.html.leex", "priv/templates/phx.gen.live/index.html.leex"},
100 + {"show.html.leex", "priv/templates/phx.gen.live/show.html.leex"},
101 + {"live_test.exs", "priv/templates/phx.gen.live/live_test.exs"},
102 + {"modal_component.ex", "priv/templates/phx.gen.live/modal_component.ex"},
103 + {"live_helpers.ex", "priv/templates/phx.gen.live/live_helpers.ex"},
104 + ])
105 + end
106 +
93 107 def inject_templates("phx.gen.context", _format) do
94 108 copy_from("priv/templates/phx.gen.context", [
95 109 {"access_no_schema.ex", "priv/templates/phx.gen.context/access_no_schema.ex"},
  @@ -29,11 +29,12 @@ defmodule Mix.Tasks.Lit.Gen.Html do
29 29 Enum.each(@commands, &Mix.Lit.inject_templates(&1, format))
30 30
31 31 # Run the Phoenix generator
32 - if format == "slime" do
33 - Mix.Task.run("phx.gen.html.slime", args)
34 - else
35 - Mix.Task.run("phx.gen.html", args)
36 - end
32 + Mix.Task.run("phx.gen.html", args)
33 + # if format == "slime" do
34 + # Mix.Task.run("phx.gen.html.slime", args)
35 + # else
36 + # Mix.Task.run("phx.gen.html", args)
37 + # end
37 38
38 39 # Remove the injected templates from priv/ so they will not
39 40 # affect future Phoenix generator commands
Loading more files…