### 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.