A powerful Elixir package for managing position fields in Ecto schemas. Easily add drag-and-drop ordering, list reordering, and position management to your Phoenix applications.
- 🎯 Precise Positioning - Insert records at specific positions with automatic repositioning
- 🔄 Flexible Movement - Move records up, down, to top, bottom, or relative to other records
- 🎌 Scoped Operations - Limit positioning operations to specific query scopes
- 🗂️ Multi-tenant Support - Full database prefix support for multi-tenant applications
- 🔧 Automatic Cleanup - Smart repositioning when removing records
- 🔄 Position Reset - Bulk reset positions for data cleanup
Add ecto_position
to your list of dependencies in mix.exs
:
def deps do
[
{:ecto_position, "~> 0.5.0"}
]
end
# Add at specific position (repositions existing records)
{:ok, todo} = EctoPosition.add(Repo, new_todo, 1)
# Add at top (position 0)
{:ok, todo} = EctoPosition.add(Repo, new_todo, :top)
# Add at bottom (last position)
{:ok, todo} = EctoPosition.add(Repo, new_todo, :bottom)
# Add relative to another record
{:ok, todo} = EctoPosition.add(Repo, new_todo, {:above, existing_todo})
{:ok, todo} = EctoPosition.add(Repo, new_todo, {:below, existing_todo})
# Move to specific position
{:ok, todo} = EctoPosition.move(Repo, todo, 2)
# Move up/down one position
{:ok, todo} = EctoPosition.move(Repo, todo, :up)
{:ok, todo} = EctoPosition.move(Repo, todo, :down)
# Move to top/bottom
{:ok, todo} = EctoPosition.move(Repo, todo, :top)
{:ok, todo} = EctoPosition.move(Repo, todo, :bottom)
# Move relative to another record
{:ok, todo} = EctoPosition.move(Repo, todo, {:above, other_todo})
{:ok, todo} = EctoPosition.move(Repo, todo, {:below, other_todo})
# Remove and reposition remaining records
{:ok, todo} = EctoPosition.remove(Repo, todo)
Repo.delete(todo)
# Reset all positions in order (useful for data cleanup)
{:ok, todos} = EctoPosition.reset(Repo, Todo)
Limit operations to specific subsets of records:
# Only affect todos in a specific category
scope = from(t in Todo, where: t.category == ^"Work")
{:ok, todo} = EctoPosition.add(Repo, new_todo, :top, scope: scope)
{:ok, todo} = EctoPosition.move(Repo, todo, :bottom, scope: scope)
{:ok, todo} = EctoPosition.remove(Repo, todo, scope: scope)
{:ok, todos} = EctoPosition.reset(Repo, scope)
Works seamlessly with database prefixes:
# Operations automatically respect the record's prefix
{:ok, todo} = EctoPosition.add(Repo, tenant_todo, :top)
{:ok, todo} = EctoPosition.move(Repo, tenant_todo, :bottom)
Option | Description | Example |
---|---|---|
Integer |
Specific position (0-based) | 0 , 1 , 5 |
:top |
Move to first position | :top |
:bottom |
Move to last position | :bottom |
:up |
Move up one position | :up |
:down |
Move down one position | :down |
{:above, record} |
Position above another record | {:above, todo} |
{:below, record} |
Position below another record | {:below, todo} |
- Negative positions are automatically set to 0
- Positions beyond range are set to the last position
- Edge case handling - moving up from top or down from bottom is safe
- Automatic repositioning - other records adjust automatically
- Nil record handling -
{:above, nil}
moves to top,{:below, nil}
moves to bottom
EctoPosition.add(repo, record, position, opts \\ [])
- Add record at positionEctoPosition.move(repo, record, position, opts \\ [])
- Move existing recordEctoPosition.remove(repo, record, opts \\ [])
- Remove record and reposition othersEctoPosition.reset(repo, queryable, opts \\ [])
- Reset all positions
:scope
- Limit operations to a specific query scope- Database prefixes are automatically handled
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License.
Made with ❤️ for the Elixir community