diff --git a/CHANGELOG.md b/CHANGELOG.md index d49c16d2..6071011c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## Unreleased + +### Added + +- Migration from Scrivener to Flop for pagination, filtering, and sorting +- Added `Torch.FlopAdapter` module to bridge between Scrivener and Flop APIs +- Added `Torch.PaginationView.pagination_from_meta/2` for rendering pagination from Flop.Meta +- Added `Torch.TableView.flop_table_link/3` for generating sortable table headers with Flop +- Updated documentation with migration guide from Scrivener to Flop + +### Changed + +- Updated `Torch.Helpers.paginate/4` to use Flop internally while maintaining backward compatibility +- Updated `Torch.Pagination` module to use Flop while maintaining backward compatibility +- Updated code generation templates to use Flop + # Changelog ## [v5.5.0](https://github.com/mojotech/torch/tree/v5.5.0) (2025-01-02) diff --git a/README.md b/README.md index 889d6620..f89ec94d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![License](https://img.shields.io/hexpm/l/torch.svg)](https://github.com/mojotech/torch/blob/master/LICENSE) [![Hex.pm](https://img.shields.io/hexpm/v/torch.svg)](https://hex.pm/packages/torch) -[![Build Status](https://github.com/mojotech/torch/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/mojotech/torch/actions/workflows/ci.yml) +[![Build Status](https://travis-ci.org/mojotech/torch.svg?branch=master)](https://travis-ci.org/mojotech/torch) [![Coverage Status](https://coveralls.io/repos/github/mojotech/torch/badge.svg?branch=master)](https://coveralls.io/github/mojotech/torch?branch=master) # Torch @@ -222,6 +222,48 @@ end Note: You'll need to install & import `Maybe` into your views `{:maybe, "~> 1.0.0"}` for the above `heex` to work. +## Pagination + +Torch provides pagination using [Flop](https://github.com/woylie/flop) (previously [Scrivener](https://github.com/drewolson/scrivener)). +Pagination is automatically included in the generated controllers and templates. + +If you're using Ecto, you can use Flop to add pagination to your queries: + +```elixir +# In your controller +def index(conn, params) do + {:ok, {users, meta}} = + User + |> Flop.validate_and_run(params, for: User) + + render(conn, "index.html", users: users, meta: meta) +end + +# In your view +def pagination(conn, meta) do + Torch.PaginationView.pagination_from_meta(conn, meta) +end +``` + +For more information on using Flop, see the [Flop documentation](https://hexdocs.pm/flop/readme.html). + +**NOTE** If you want to customize the pagination functions themselves for your application, do not use the default `Torch.Pagination` as described above; instead you will need to define your own `paginate_*/2` method that will return a `Scrivener.Page` object. You can also define your own pagination system and functions as well, but that will require further customization of the generated Torch controllers as well. + +## Migrating from Scrivener to Flop + +Torch 6.0.0 migrates from Scrivener to Flop for pagination. Flop provides more advanced filtering, sorting, and pagination capabilities. The migration is designed to be backward compatible, but there are some changes to be aware of: + +1. Torch now includes both Scrivener and Flop dependencies, with Flop being the primary pagination library. +2. A `Torch.FlopAdapter` module is provided to bridge between Scrivener and Flop APIs. +3. The `Torch.Helpers.paginate/4` function now uses Flop internally but maintains the same interface. +4. New functions are available for working directly with Flop: + - `Torch.PaginationView.pagination_from_meta/2` for rendering pagination from a Flop.Meta struct + - `Torch.TableView.flop_table_link/3` for generating sortable table headers with Flop parameters + +For new applications, we recommend using Flop directly. For existing applications, the adapter provides backward compatibility while you transition to Flop. + +See the [UPGRADING.md](UPGRADING.md) file for more details on migrating from Scrivener to Flop. + ## Styling Torch generates two CSS themes you can use: `base.css` & `theme.css`. diff --git a/UPGRADING.md b/UPGRADING.md index f14097e7..4d47662f 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,5 +1,142 @@ # Upgrading +## Upgrading to Torch 6.0.0 (Scrivener to Flop Migration) + +Torch 6.0.0 introduces a significant change by migrating from Scrivener to Flop for pagination, filtering, and sorting. This migration provides more advanced features while maintaining backward compatibility. + +### What's Changed + +1. **Dependencies**: Torch now includes both Scrivener and Flop, with Flop being the primary pagination library. +2. **API Compatibility**: A `Torch.FlopAdapter` module bridges between Scrivener and Flop APIs to maintain backward compatibility. +3. **New Features**: Flop provides additional capabilities like cursor-based pagination, compound fields, and more advanced filtering. + +### Backward Compatibility + +Existing code that uses Torch's pagination should continue to work without changes. The following functions maintain backward compatibility: + +- `Torch.Helpers.paginate/4` +- `Torch.Pagination` module +- `Torch.PaginationView.pagination/1` + +### Using Flop Directly (Recommended for New Code) + +For new applications or when enhancing existing ones, we recommend using Flop directly: + +```elixir +# In your controller +def index(conn, params) do + {:ok, {users, meta}} = + User + |> Flop.validate_and_run(params, for: User) + + render(conn, "index.html", users: users, meta: meta) +end + +# In your view +def pagination(conn, meta) do + Torch.PaginationView.pagination_from_meta(conn, meta) +end +``` + +### New Functions for Flop Integration + +Torch provides new functions for working directly with Flop: + +- `Torch.PaginationView.pagination_from_meta/2` - Renders pagination links from a Flop.Meta struct +- `Torch.TableView.flop_table_link/3` - Generates sortable table headers with Flop parameters + +### Migrating Existing Code to Flop + +While not required, migrating to Flop directly provides access to more advanced features: + +1. **Update Schema**: Add `@derive {Flop.Schema, ...}` to your Ecto schemas to define filterable and sortable fields: + +```elixir +@derive {Flop.Schema, + filterable: [:name, :email, :status], + sortable: [:name, :email, :inserted_at] +} +schema "users" do + # ... +end +``` + +2. **Update Controllers**: Replace Scrivener pagination with Flop: + +```elixir +# Before (with Scrivener) +def index(conn, params) do + page = + User + |> Repo.paginate(params) + + render(conn, "index.html", users: page.entries, page: page) +end + +# After (with Flop) +def index(conn, params) do + {:ok, {users, meta}} = + User + |> Flop.validate_and_run(params, for: User) + + render(conn, "index.html", users: users, meta: meta) +end +``` + +3. **Update Views**: Use the new Flop-compatible pagination function: + +```elixir +# Before +<%= Torch.PaginationView.pagination(@conn) %> + +# After +<%= Torch.PaginationView.pagination_from_meta(@conn, @meta) %> +``` + +4. **Update Table Headers**: Use the Flop-compatible table link function: + +```elixir +# Before +<%= table_link(@conn, "Name", :name) %> + +# After +<%= flop_table_link(@conn, "Name", :name) %> +``` + +### Advanced Flop Features + +Flop provides several advanced features not available in Scrivener: + +1. **Cursor-based Pagination**: More efficient for large datasets: + +```elixir +flop_params = %{ + "first" => 10, + "after" => cursor +} +``` + +2. **Compound Fields**: Filter on multiple fields with a single parameter: + +```elixir +@derive {Flop.Schema, + filterable: [:name, :email], + compound_fields: [full_text: [:name, :email]] +} +``` + +3. **Custom Fields**: Define custom filter logic: + +```elixir +@derive {Flop.Schema, + custom_fields: [ + %{name: :age_range, filter: {MyApp.CustomFilters, :filter_by_age_range}} + ] +} +``` + +For more information on using Flop's advanced features, see the [Flop documentation](https://hexdocs.pm/flop/readme.html). + ### Torch v4 to Torch v5 Torch v5 **IS NOT ** fully backwards compatible with Torch v4. Due to Phoenix 1.7 dropping the inclusion @@ -56,3 +193,4 @@ becomes: Another option to "upgrade" is to just generate new templates again via the Torch v4 generators. Run the same generator commands as the first time and overwrite your existing files. Then resolve any customization previously made to your Torch v3 templates by re-applying those change to the newly generated Torch v4 templates. + diff --git a/lib/torch/component.ex b/lib/torch/component.ex index a577add6..2f72574c 100644 --- a/lib/torch/component.ex +++ b/lib/torch/component.ex @@ -18,34 +18,6 @@ defmodule Torch.Component do <.torch_input field={@form[:email]} type="email" /> <.torch_input name="my-input" errors={["oh no!"]} /> """ - attr(:id, :any, default: nil) - - attr(:type, :string, - default: "text", - values: ~w(number checkbox textarea date datetime time datetime-local select text string file) - ) - - attr(:value, :any) - attr(:name, :any) - attr(:label, :string, default: nil) - - attr(:field, Phoenix.HTML.FormField, - doc: "a form field struct retrieved from the form, for example: @form[:email]" - ) - - attr(:errors, :list, default: []) - attr(:checked, :boolean, doc: "the checked flag for checkbox inputs") - attr(:prompt, :string, default: nil, doc: "the prompt for select inputs") - attr(:options, :list, doc: "the options to pass to `Phoenix.HTML.Form.options_for_select/2`") - attr(:multiple, :boolean, default: false, doc: "the multiple flag for select inputs") - - attr(:rest, :global, - include: - ~w(autocomplete cols disabled form max maxlength min minlength pattern placeholder readonly required rows size step) - ) - - slot(:inner_block) - def torch_input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do errors = if Phoenix.Component.used_input?(field), do: field.errors, else: [] @@ -160,9 +132,6 @@ defmodule Torch.Component do @doc """ Renders a label """ - attr(:for, :string, default: nil) - slot(:inner_block, required: true) - def torch_label(assigns) do ~H"""