Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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`.
Expand Down
138 changes: 138 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
</initial_code>
40 changes: 1 addition & 39 deletions lib/torch/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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: []

Expand Down Expand Up @@ -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"""
<label for={@for}>
Expand All @@ -174,9 +143,6 @@ defmodule Torch.Component do
@doc """
Renders generic error message
"""
attr(:for, :string, default: nil)
slot(:inner_block, required: true)

def torch_error(assigns) do
~H"""
<span class="invalid-feedback"><%= render_slot(@inner_block) %></span>
Expand All @@ -190,8 +156,6 @@ defmodule Torch.Component do

<.flash_messages flash={conn.assigns.flash} />
"""
attr(:flash, :map)

def flash_messages(assigns) do
~H"""
<section id="torch-flash-messages">
Expand All @@ -205,9 +169,6 @@ defmodule Torch.Component do
@doc """
Renders a simple flash message tag
"""
attr(:flash_type, :string)
attr(:message, :string)

def torch_flash(assigns) do
~H"""
<p class={"torch-flash #{@flash_type}"}><%= @message %>&nbsp;<button class="torch-flash-close">x</button></p>
Expand Down Expand Up @@ -249,3 +210,4 @@ defmodule Torch.Component do
for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts})
end
end

Loading