### Example Base LiveView and Query Helpers
Source: https://github.com/grammacc/cartograph/blob/main/README.md
Provides an example of a 'base' LiveView and associated helper modules for shared parsing functions, demonstrating how to structure code for common query parameters like pagination or dynamic filters using Cartograph.
```elixir
defmodule MyApp.BaseLiveView do
use Phoenix.LiveView
use Cartograph.LiveViewParams, handle_params: true
end
defmodule MyApp.QueryHelpers do
import Phoenix.Component, only: [assign: 3]
def parse_params(socket, %{"userroles" => selected_roles}, :selected_roles) do
valid_roles = [:admin, :member]
socket
|> assign(:selected_roles, Enum.filter(selected_roles, &Enum.member?(valid_roles, &1)))
end
def parse_params(socket, %{}, :selected_roles), do: assign(socket, :selected_roles, [])
def parse_params(socket, %{"current_role" => current_role}, :current_role)
when current_role in [:admin, :member] do
assign(socket, :current_role, current_role)
end
def parse_params(socket, %{}, :current_role), do: socket
end
defmodule MyApp.ExampleLiveView do
use MyApp.BaseLiveView
alias Phoenix.LiveView.JS
alias MyApp.QueryHelpers
import Cartograph.Component, only: [cartograph_patch: 1]
@cartograph_parser [
handler: &QueryHelpers.parse_params/3,
keys: [:selected_roles],
]
@impl true
```
--------------------------------
### Install Cartograph Dependency in Mix.exs
Source: https://context7.com/grammacc/cartograph/llms.txt
Add the Cartograph library to your project's dependencies in the `mix.exs` file. This makes the library available for use in your Elixir application.
```elixir
def deps do
[
{:cartograph, "~> 0.2"}
]
end
```
--------------------------------
### Mount LiveView Component with Role State
Source: https://github.com/grammacc/cartograph/blob/main/README.md
Initializes the LiveView socket with state for selected roles and available role choices. It uses `assign_new` to set default values if they are not already present, ensuring a consistent starting state for the component.
```elixir
def mount(_params, _session, socket) do
mounted_socket =
socket
|> assign_new(:selected_roles, fn -> [] end)
|> assign_new(:role_choices, fn -> [{:admin, "Admin"}, {:member, "Member"}] end)
{:ok, mounted_socket}
end
```
--------------------------------
### Pagination Component with Cartograph Patch
Source: https://context7.com/grammacc/cartograph/llms.txt
A complete example of a pagination widget using `cartograph_patch` for stateful URL-based navigation. It allows users to navigate between pages and jump to a specific page, with all state managed via URL query parameters.
```elixir
defmodule MyApp.Components.Pagination do
use Phoenix.Component
import Cartograph.Component, only: [cartograph_patch: 1]
defp compute_prev_page(page_no, page_count) do
if page_no == 1, do: page_count, else: page_no - 1
end
defp compute_next_page(page_no, page_count) do
if page_no == page_count, do: 1, else: page_no + 1
end
attr :page_no, :integer, required: true
attr :page_count, :integer, required: true
attr :id, :string, required: true
def pagination(assigns) do
~H"""
Page {@page_no} of {@page_count}
Jump to:
"""
end
end
```
--------------------------------
### Multi-Select Filter Component with Cartograph Patch
Source: https://context7.com/grammacc/cartograph/llms.txt
An example of a role filter component with toggle behavior for multi-select checkboxes using `cartograph_patch`. It allows users to select multiple roles, and the selected roles are reflected in the URL query parameters.
```elixir
defmodule MyApp.UsersLive do
use Phoenix.LiveView
use Cartograph.LiveViewParams
import Cartograph.Component, only: [cartograph_patch: 1]
@impl true
def handle_params(%{"userroles" => selected_roles}, _uri, socket) do
valid_roles = [:admin, :member, :guest]
filtered = Enum.filter(selected_roles, &(&1 in valid_roles))
{:noreply, assign(socket, :selected_roles, filtered)}
end
@impl true
def handle_params(%{}, _uri, socket) do
{:noreply, assign(socket, :selected_roles, [])}
end
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket,
selected_roles: [],
role_choices: [{:admin, "Admin"}, {:member, "Member"}, {:guest, "Guest"}]
)}
end
def render(assigns) do
~H"""
Filter by Role
<%= for {value, label} <- @role_choices do %>
<% end %>
"""
end
end
```
--------------------------------
### Data-Driven Parsing with @cartograph_parser
Source: https://github.com/grammacc/cartograph/blob/main/README.md
Demonstrates using the @cartograph_parser module attribute to make parameter parsing more data-driven. This approach centralizes parsing logic and reduces boilerplate by defining handlers and keys within the attribute.
```elixir
@cartograph_parser [
handler: &__MODULE__.parse_params/3,
keys: [:selected_roles],
]
def parse_params(socket, params, :selected_roles), do: parse_selected_params(socket, params)
@impl true
def handle_params(params, _uri, socket), do: {:noreply, refresh_user_list(socket)}
```
--------------------------------
### Query Operations Reference
Source: https://context7.com/grammacc/cartograph/llms.txt
Reference for the different query operations available in Cartograph, including `:set`, `:add`, `:merge`, `:remove`, and `:toggle`.
```APIDOC
## Query Operations Reference
### Description
All query operations work with maps of key-value pairs. Keys and values can be atoms or strings. List values create multiple params with the same key.
### Operations
- **`:set`** - Replace the entire query string with the provided map.
- Example: `[set: %{new: "value"}]`
- Example with multi-values: `[set: %{tags: ["a", "b"]}]`
- **`:add`** - Append parameters to the existing query string without checking for duplicates.
- Example: `[add: %{foo: "baz"}]`
- **`:merge`** - Replace existing keys with new values or add new keys. Can also perform in-place value replacement using nested maps.
- Example: `[merge: %{foo: "updated", new: "key"}]`
- Example with nested map: `[merge: %{sort: %{"name-asc" => "name-desc"}}]`
- **`:remove`** - Remove parameters based on keys or specific key-value pairs.
- Example (by key list): `[remove: [:foo]]`
- Example (by map): `[remove: %{foo: "baz"}]`
- **`:toggle`** - Add a parameter if it's missing, remove it if it's present. Useful for boolean flags or array elements.
- Example: `[toggle: %{"selected[]" => "b"}]`
### Cumulative Operations
Operations can be combined and are applied in the order they appear in the list.
```elixir
# Before: /page?foo=start
# After: /page?dolor=sit
[
set: %{foo: "bar"},
merge: %{foo: "baz", lorem: "ipsum"},
toggle: %{foo: "baz"},
add: %{foo: "bar"},
remove: [:foo, :lorem]
]
```
```
--------------------------------
### Render Role Multi-Select UI with LiveView
Source: https://github.com/grammacc/cartograph/blob/main/README.md
Renders a collapsible section for user role selection using HEEx templating. It displays checkboxes for each role, allowing users to select or deselect them. The `phx-click` attribute is used to trigger an event when a checkbox is interacted with, updating the selected roles.
```elixir
def render(assigns) do
~H"""
Selected User Roles
<%= for {data_value, display_value} <- @role_choices do %>
data_value}])}
/>
{display_value}
<% end %>
"""
end
```
--------------------------------
### Integrate Cartograph with LiveView using Cartograph.LiveViewParams
Source: https://context7.com/grammacc/cartograph/llms.txt
This module integrates Cartograph's navigation events into a LiveView process. It automatically adds a `@cartograph_uri` assign and handles Cartograph patch/navigate events, simplifying query parameter management within LiveView components.
```elixir
defmodule MyApp.UsersLive do
use Phoenix.LiveView
# MUST be after Phoenix.LiveView
use Cartograph.LiveViewParams
import Cartograph.Component, only: [cartograph_patch: 1]
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :selected_roles, [])}
end
@impl true
def handle_params(%{"roles" => roles}, _uri, socket) do
{:noreply, assign(socket, :selected_roles, roles)}
end
@impl true
def handle_params(%{}, _uri, socket) do
{:noreply, assign(socket, :selected_roles, [])}
end
def render(assigns) do
~H"""
:admin}])}
/> Admin
"""
end
end
```
--------------------------------
### parse_navigate/2 - Pre-compute Navigate URIs for Links
Source: https://context7.com/grammacc/cartograph/llms.txt
Parses a full URI suitable for use in a `Phoenix.Component.link/1` component's `:navigate` attribute. This function generates complete URIs for navigation.
```APIDOC
## parse_navigate/2 - Pre-compute Navigate URIs for Links
### Description
Parses a full URI suitable for use in a `Phoenix.Component.link/1` component's `:navigate` attribute. Allows pre-rendering full URIs for copyable/bookmarkable links.
### Method
(N/A - Function call for URI generation)
### Endpoint
(N/A - Function call for URI generation)
### Parameters
#### Path Parameters
None
#### Query Parameters
- **query** (map) - Required - Specifies query operations like `:set`, `:merge`, `:add`, `:remove`, `:toggle`.
- **phx_value** (any) - Optional - Used in conjunction with `:merge` to specify a value to be merged into the query string, often from `phx-value`.
#### Request Body
None
### Request Example
```elixir
import Cartograph.Component, only: [parse_navigate: 2]
# Returns full URI string
parse_navigate("https://localhost:4000/users?page=1", query: [merge: %{page: 2}])
# => "https://localhost:4000/users?page=2"
# Works with URI structs
uri = URI.parse("https://example.com/dashboard")
parse_navigate(uri, query: [set: %{tab: "settings"}])
# => "https://example.com/dashboard?tab=settings"
# Use with phx_value for pre-computed values
parse_navigate("/search", query: [merge: %{"q" => :phx_value}], phx_value: "elixir")
# => "/search?q=elixir"
# In templates
~H"""
<.link navigate={parse_navigate(@cartograph_uri, query: [merge: %{ref: @user_id}])}>
View Profile
"""
```
### Response
#### Success Response (String)
- **uri** (string) - The generated navigate URI string.
#### Response Example
```
"https://localhost:4000/users?page=2"
```
```
--------------------------------
### parse_patch/2 - Pre-compute Patch URIs for Links
Source: https://context7.com/grammacc/cartograph/llms.txt
Parses a new path suitable for use in a `Phoenix.Component.link/1` component's `:patch` attribute. This allows for pre-rendering full URIs for copyable/bookmarkable links.
```APIDOC
## parse_patch/2 - Pre-compute Patch URIs for Links
### Description
Parses a new path suitable for use in a `Phoenix.Component.link/1` component's `:patch` attribute. Allows pre-rendering full URIs for copyable/bookmarkable links.
### Method
(N/A - Function call for URI generation)
### Endpoint
(N/A - Function call for URI generation)
### Parameters
#### Path Parameters
None
#### Query Parameters
- **query** (map) - Required - Specifies query operations like `:set`, `:merge`, `:add`, `:remove`, `:toggle`.
#### Request Body
None
### Request Example
```elixir
import Cartograph.Component, only: [parse_patch: 2]
# Basic usage - returns path string for patch
parse_patch("/users?page=1", query: [merge: %{page: 2}])
# => "/users?page=2"
# Set operation replaces entire query
parse_patch("/items?old=param", query: [set: %{new: "param"}])
# => "/items?new=param"
# Remove specific keys
parse_patch("/search?q=test&filter=active&sort=date", query: [remove: [:filter, :sort]])
# => "/search?q=test"
# Toggle array values
parse_patch("/list?tag[]=a&tag[]=b", query: [toggle: %{"tag[]" => "c"}])
# => "/list?tag[]=a&tag[]=b&tag[]=c"
# Use with Phoenix.Component.link in templates
~H"""
<.link patch={parse_patch(@cartograph_uri, query: [merge: %{page: @page + 1}])}>
Next Page
"""
# Breadcrumb component example
~H"""
<.link patch={parse_patch(@curr_uri, query: [remove: [:category, :subcategory]])}>
Home
"""
```
### Response
#### Success Response (String)
- **path** (string) - The generated patch URI string.
#### Response Example
```
"/users?page=2"
```
```
--------------------------------
### Auto-generate handle_params with Cartograph.LiveViewParams and handle_params: true
Source: https://context7.com/grammacc/cartograph/llms.txt
When using `@cartograph_parser` for all parameter parsing, setting `handle_params: true` in `use Cartograph.LiveViewParams` automatically generates a default `handle_params/3` implementation. This reduces boilerplate code for managing query parameters.
```elixir
defmodule MyApp.FilteredListLive do
use Phoenix.LiveView
use Cartograph.LiveViewParams, handle_params: true
import Cartograph.Component, only: [cartograph_patch: 1]
@cartograph_parser [
handler: &__MODULE__.parse_params/3,
keys: [:page, :sort]
]
def parse_params(socket, %{"page" => page}, :page) do
assign(socket, :page, String.to_integer(page))
end
def parse_params(socket, %{}, :page), do: assign(socket, :page, 1)
def parse_params(socket, %{"sort" => sort}, :sort) do
assign(socket, :sort, sort)
end
def parse_params(socket, %{}, :sort), do: assign(socket, :sort, "name")
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, page: 1, sort: "name")}
end
def render(assigns) do
~H"""
"""
end
end
```
--------------------------------
### Refactor LiveView Parsing with Private Function
Source: https://github.com/grammacc/cartograph/blob/main/README.md
Refactors a LiveView's handle_params/3 function to use a private function for parsing 'selected_roles', making the handle_params/3 function more extensible and readable. It defines a helper function to parse roles and then calls it within handle_params/3.
```elixir
def parse_selected_roles(socket, %{"userroles" => selected_roles}) do
valid_roles = [:admin, :member]
socket
|> assign(:selected_roles, Enum.filter(selected_roles, &(Enum.member?(valid_roles, &1))))
end
def parse_selected_roles(socket, %{}), do: assign(socket, :selected_roles, [])
@impl true
def handle_params(params, _uri, socket) do
updated_socket =
socket
|> parse_selected_roles(params)
|> refresh_user_list()
{:noreply, updated_socket}
end
```
--------------------------------
### cartograph_navigate/2 - Push Navigate Events with Relative Query Updates
Source: https://context7.com/grammacc/cartograph/llms.txt
Pushes a navigation event to a specified URI with relative query parsing applied. This function returns a `Phoenix.LiveView.JS` struct.
```APIDOC
## cartograph_navigate/2 - Push Navigate Events with Relative Query Updates
### Description
Returns a `Phoenix.LiveView.JS` struct that pushes a navigation event to the specified URI with relative query parsing applied.
### Method
(Implicitly POST or PUT via JS struct)
### Endpoint
(Not directly applicable, as it pushes JS events)
### Parameters
#### Path Parameters
None
#### Query Parameters
- **query** (map) - Required - Specifies query operations like `:set`, `:merge`, `:add`, `:remove`, `:toggle`.
#### Request Body
None (operates on JS events)
### Request Example
```elixir
import Cartograph.Component, only: [cartograph_navigate: 2]
# Navigate to a different path with query params
# Navigates to: /dashboard?view=analytics
cartograph_navigate(~p"/dashboard", query: [set: %{view: "analytics"}])
# Navigate with merged params
# Navigates to: /users?role=admin&page=1
cartograph_navigate("/users?role=admin", query: [merge: %{page: 1}])
# Using URI struct
uri = URI.parse("https://example.com/search")
cartograph_navigate(uri, query: [set: %{q: "elixir"}])
# In template with phx_value
~H"""
:phx_value}])}
/>
"""
```
### Response
#### Success Response (JS Struct)
- **type** (atom) - `:js
- **event** (atom) - `:navigate
- **value** (map) - Contains navigation details including the target URI.
#### Response Example
(Returns a JS struct, not a direct HTTP response)
```
--------------------------------
### Generate LiveView Patch Events with cartograph_patch/1
Source: https://context7.com/grammacc/cartograph/llms.txt
The `cartograph_patch/1` function generates a `Phoenix.LiveView.JS` struct to push patch events with relative query updates. It supports various operations like toggle, merge, add, remove, and set on query parameters, and can utilize the `:phx_value` placeholder for input values.
```elixir
import Cartograph.Component, only: [cartograph_patch: 1]
# Toggle a value in an array-valued param
# Current URL: /users
# After click: /users?roles[]=admin
cartograph_patch(query: [toggle: %{"roles[]" => :admin}])
# Merge a single value (replaces existing key)
# Current URL: /users?page=1
# After click: /users?page=2
cartograph_patch(query: [merge: %{page: 2}])
# Add values without removing duplicates
# Current URL: /items?tag=elixir
# After click: /items?tag=elixir&tag=phoenix
cartograph_patch(query: [add: %{tag: "phoenix"}])
# Remove params by key
# Current URL: /search?q=test&filter=active
# After click: /search?q=test
cartograph_patch(query: [remove: [:filter]])
# Remove specific key-value pairs
# Current URL: /items?tag=elixir&tag=phoenix
# After click: /items?tag=elixir
cartograph_patch(query: [remove: %{tag: "phoenix"}])
# Set entire query (replaces all existing params)
# Current URL: /page?foo=bar&baz=qux
# After click: /page?fresh=start
cartograph_patch(query: [set: %{fresh: "start"}])
# Use :phx_value placeholder for input values
# User types "5" and presses Enter -> /items?page=5
~H"""
:phx_value}])}
/>
"""
# Pass loading options through to JS.push
cartograph_patch(query: [merge: %{page: 2}], loading: "#spinner", page_loading: true)
```
--------------------------------
### Elixir LiveView with Cartograph Query Patching for User Filtering
Source: https://github.com/grammacc/cartograph/blob/main/README.md
This Elixir LiveView component allows filtering a list of users by role. It uses `Cartograph.LiveViewParams` to manage URL query parameters for selected roles and `Cartograph.Component.cartograph_patch/1` to update the URL when roles are toggled. The component dynamically refreshes the user list based on the selected roles.
```elixir
defmodule MyApp.ExampleLive do
use Phoenix.LiveView
use Cartograph.LiveViewParams
alias Phoenix.LiveView.JS
import Cartograph.Component, only: [cartograph_patch: 1]
@impl true
def handle_params(%{"userroles" => selected_roles} = _params, _uri, socket) do
valid_roles = [:admin, :member]
updated_socket =
socket
|> assign(:selected_roles, Enum.filter(selected_roles, &Enum.member?(valid_roles, &1)))
|> refresh_user_list()
{:noreply, updated_socket}
end
@impl true
def handle_params(%{} = _params, _uri, socket) do
updated_socket =
socket
|> assign(:selected_roles, [])
|> refresh_user_list()
{:noreply, updated_socket}
end
defp refresh_user_list(%Phoenix.LiveView.Socket{assigns: %{selected_roles: []}} = socket) do
stream_async(socket, :user_list, fn ->
{:ok, Repo.all(User), reset: true}
end)
end
defp refresh_user_list(%Phoenix.LiveView.Socket{assigns: %{selected_roles: roles}} = socket) do
stream_async(socket, :user_list, fn ->
res =
from(User)
|> where([user: u], u.role in ^roles)
|> Repo.all()
{:ok, res, reset: true}
end)
end
@impl true
def mount(_params, _session, socket) do
mounted_socket =
socket
|> assign_new(:selected_roles, fn -> [] end)
|> assign_new(:role_choices, fn -> [{:admin, "Admin"}, {:member, "Member"}] end)
{:ok, mounted_socket}
end
def render(assigns) do
~H"""
Selected User Roles
<%= for {data_value, display_value} <- @role_choices do %>
data_value}])}
/>
{display_value}
<% end %>
"""
end
end
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.