Git Product home page Git Product logo

flop_phoenix's People

Contributors

andreasknoepfle avatar bharathvijay avatar blakedietz avatar brianphilips avatar dependabot[bot] avatar dsrees avatar helderbarboza avatar maltoe avatar martosaur avatar mmore avatar nathanmalishev avatar obsidienne avatar onigirijack avatar tielur avatar tschmidleithner avatar vanderhoop avatar woylie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flop_phoenix's Issues

hide option for columns

Since slots must be a direct child of a component, it is not possible to use if to hide a :col. We need a :hide option for the :col slot.

Supporting multiple filter conditions per field

Is your feature request related to a problem? Please describe.
Hi @woylie, first off, thanks so much for maintaining this project. It's fantastic and I've really enjoyed the API you expose.

The problem: I'm building a relatively static form where I'd like to allow users to enter a min and max range for a specific value. The interface for this would provide 2 separate inputs per field.

EDIT: I believe this problem is well-described here: #88. There's a mention to PR #89, but I'm not sure if this issue ever got resolved?

As an example, let's say we'd like to enter a minimum and maximum age, I believe the field definitions would look like:

[
  age: [label: "Minimum age", op: :>=, type: "number"],
  age: [label: "Maximum age", op: :<=, type: "number"]
]

From my implementation efforts and research, it appears this structure with the same field and different operators isn't yet supported by the library.

In researching this issue, I found the work you did to use separate labels for multiple fields, and stumbled across this conversation as well which I believe is pertinent.

Right now, I believe the age fields in the example would appear separately in the UI; however, upon collating the filter values, they would be merged, taking the value of the first age filter, the second one being discarded, and values from the UI being assigned to different inputs via the zip-based implementation here.

I could be totally wrong here, if you have any advice to support the use case above, it would be extremely helpful!

Describe the solution you'd like
My ideal solution would be that this library supports an arbitrary number of independently configured inputs per field just based on the fields configuration described above.

Describe alternatives you've considered
As an alternative, I tried creating a more manual implementation of the filter_fields component as described here, but was unsuccessful.

Additional context
As I mentioned, I could be totally off base here, but I've been struggling with this for a few hours and would love any advice.

Thanks so much!

warning: you are accessing the variable ... inside a LiveView template.

With LiveView 0.18.1, warnings for Flop.Phoenix.filter_fields/1 are emitted.

warning: you are accessing the variable "field_opts" inside a LiveView template.

Using variables in HEEx templates are discouraged as they disable change tracking. You are only allowed to access variables defined by Elixir control-flow structures, such as if/case/for, or those defined by the special attributes :let/:if/:for.

Instead of:

    def add(assigns) do
      result = assigns.a + assigns.b
      ~H"the result is: <%= result %>"
    end

You must do:

    def add(assigns) do
      assigns = assign(assigns, :result, assigns.a + assigns.b)
      ~H"the result is: <%= @result %>"
    end

  lib/flop_phoenix.ex:828: Flop.Phoenix.filter_fields/1

Support safe HTML tuples in unsortable headers.

Hey @woylie

We'd like to be able to pass HTML to the headers (not sortable), hence we're doing

headers: [content_tag(:span, "foo")]

which unfortunately gets gobbled up by the headers({value, field}...) clause of the header/5 function. Our current workaround is something like headers: [{content_tag(:span, "foo"), nil}], which is ok but somewhat ugly.

PR incoming.

Best,
malte

Global :attr not allowed in slot

Describe the bug
Looks like Phoenix LiveView 0.18.3, added a raise to global within slot attr.
See phoenixframework/phoenix_live_view#2164 && phoenixframework/phoenix_live_view#2265

To Reproduce
Install Phoenix LiveView 0.18.3 & FlopPhoenix, you receive a compile error.

== Compilation error in file lib/flop_phoenix.ex == ** (CompileError) lib/flop_phoenix.ex:660: cannot define :global slot attributes (phoenix_live_view 0.18.3) lib/phoenix_component/declarative.ex:526: Phoenix.Component.Declarative.compile_error!/3 (phoenix_live_view 0.18.3) lib/phoenix_component/declarative.ex:251: Phoenix.Component.Declarative.__slot__!/6 lib/flop_phoenix.ex:628: (module)

Versions:

  • Flop.Phoenix: 0.16.0
  • Phoenix.LiveView: 0.18.3

Global config

With the latest changes, all customization options for the appearance of the components are separated from the options that only relate to the specific instance. These appearance options are likely the same for all templates. At the moment, the documentation suggests to define wrapper components that set these options and to import them with the other view helpers:

# in the view helper module

def pagination(assigns) do
  assigns = assign_new(assigns, :opts, fn -> [] end)

  ~H"""
  <Flop.Phoenix.pagination {assigns} opts={pagination_opts(@opts)} />
  """
end

defp pagination_opts(opts) do
  default_opts = [
    ellipsis_attrs: [class: "ellipsis"],
    # ...
  ]

  Keyword.merge(default_opts, opts)
end

This approach has one drawback: If the library component raises an error because of a misconfiguration, the stack trace will point to the wrapper component instead of the template where it is used, making it harder to debug.

A better way would be to just expose a function that returns the default options and use the Flop.Phoenix components directly:

# in the view helper module
def pagination_opts(opts \\ []) do
  default_opts = [
    ellipsis_attrs: [class: "ellipsis"],
    # ...
  ]

  Keyword.merge(default_opts, opts)
end

# in the template
<Flop.Phoenix.pagination for={Pet} event="paginate" opts={pagination_opts()} />

This works fine, but now you have to remember to pass the opts assign everywhere you use the component. It would be nice if you could configure the default opts globally and let Flop.Phoenix handle merging any overrides into the defaults:

# in the view helper module
def pagination_opts do
  [
    ellipsis_attrs: [class: "ellipsis"],
    # ...
  ]
end

# in the application config
config :flop_phoenix, :pagination, default_opts: {MyApp.ViewHelpers, :pagination_opts}

# in the template, just using the global defaults
<Flop.Phoenix.pagination for={Pet} event="paginate" />

# to override a option
<Flop.Phoenix.pagination for={Pet} event="paginate" opts={[ellipsis_content: "โ€ฅ"]} />

And similarly with the table component.

improve table API

At the moment, the table component requires you to pass in a row function, which returns a list of contents that the component then wraps into <td> elements. This forces LiveView to update the table as a whole, and it is a bit cumbersome to work with if you want to render more complex markup or a function component in a column.

So instead, I'd like to remove the row_func assign and use let like this:

<Flop.Phoenix.table items={@items} meta={@meta}>
  <:col let={pet} label="Name" field={:name}>
    <%= pet.name %>
  </:col>
  <:col let={pet} label="Age">
    <%= pet.age %>
  </:col>
</Flop.Phoenix.table>

support passing path as string

Currently, the path helper needs to be passed as an mfa tuple to the table, pagination and cursor_pagination components.

<.pagination meta={@meta} path_helper={{Routes, :some_path, [@socket, :index]}} />

Phoenix 1.7 will come with verified routes, allowing you to use the ~p sigil, which compiles down to a string. flop_phoenix should allow to either reference a path helper as before, or to pass a path as a string directly.

<.pagination meta={@meta} path={~p"/some/path"} />

The Flop query parameters should be merged into the query parameters of the given path.

Tasks

  • build_path/3: handle string as first argument
  • validate_path_helper_or_event!/2: update validation to accept string
  • ensure all components accept a string as path

Pagination: hide/disable limit

This issue has two parts:

  1. Flop.Schema should allow to disable the limit parameter if a default limit is set.
  2. FlopPhoenix.pagination should hide the limit/page_size parameters if limiting is disabled or the default limit is used.

See discussion in #8.

clear ordering on third click

Problem

I cannot clear a sorting on a column

maybe Solution

  • click once: order asc
  • second click: order desc
  • third click: clear

Filter components: support multiple inputs for one field

At the moment, the filter input label is derived from a keyword list. If a field is used multiple times (e.g. from/to inputs for a date field), the first label in the list is used for all of the fields. The implementation needs to be changed to use the correct label.

pagination: display first/last links

The pagination helper should always display links to the first and last page, even if the number of page links is limited with the ellipsis option.

width option for columns

Add a width option to the table :col, which translates into a colgroup, in order to prevent column widths changing depending on the column contents when paginating or sorting.

Improve compatibility with Tailwind

Is your feature request related to a problem? Please describe.
Tailwind becomes more and more popular and it does not rely on predefined classes which is used in every and each table. With Liveview we start encapsulating styled components. In my specific case, when I am using Flop.Phoenix.pagination I have two issues:

  1. I can't adjust the css class when it's the current page (fixed tois-current
  2. I can't change the order of previous_link, page_links and next_link. For instance I'd like to have the previous_link before the page_links and the next_link at the end.

Describe the solution you'd like
The first issue could be fixed with a further customization option. The second issue by allowing to override the content of the pagination function.

Describe alternatives you've considered
For the moment I am basically overriding the Flop.Phoenix.Pagination module which isn't nice.

Increase customization of table generator

Is your feature request related to a problem? Please describe.
This relates to the general intent of #26 to improve compatibility with Tailwind. I have a table where I am styling the tr tags with some css classes. flop_phoenix does not allow customization of tr tags right now. Moreover I have some styling classes for the td tags in header and body. For the header I could just pass my markup to the headers key. But then I need to reimplement building a sorting link + direction indicator. For the body I can't pass any td styling classes right now.

Describe the solution you'd like
Allow addings attributes to tr and td tags. A helper for sorting links with direction indicator in addition to build_path/3 might be beneficial.

Describe alternatives you've considered
Right now I am not using the table generator but just my own markup with an own reimplemented sortable link builder with direction indicator.

Helper for independent table column sorter

Mathias I saw that with the new sort table that you put in the last couple of days you have included some basic column sorting capabilities (I haven't used it, not working with LiveView just yet).

How difficult do you think it would be for you to extract the work you've done to produce a modifiable sort_column helper to use where we'd like on our own custom table layouts.

It might take at least the signature: sort_column(flop, column_name, display_name, *) where column_name is one of the whitelisted values from Flop, and display_name is whatever we'd like. I could use build in icons or any other function we'd like to hold the direction (something like you already have for customizing pagination links would be awesome).

I've been playing around basically looking at:

<table>
  <thead>
    <th><%= sort_column(@meta, :name, gettext("Name"), *)</th>
    <th><%= sort_column(@meta, :active, gettext("Status"), *)</th>
  </thead>
  <tbody>

  </tbody>
</table>

What do you think? Is the work extractable into a more tailored approach separate from the basic sorttable?

to_query doesn't read defaults from config module

Flop.Phoenix.to_query/2 only takes the Flop struct and options, but not the Meta struct. Flop.Phoenix.build_path/3 can take a Flop struct or a Meta struct and the options. Required changes in Flop.Phoenix:

  • if Meta struct is passed to build_path/3, get config_module option from struct and put it into the opts passed to to_query/2
  • document passing config_module option in both build_path/3 and to_query/2
  • ensure components pass along the backend option

improve parameter mapping functionality

By default, Flop.Phoenix puts all parameters into the query parameters when building URLs. This can overridden by passing a custom URL builder function since version 0.15, allowing you to map flop parameters to other URL structures (e.g. /categories/{category_id}/posts/{page}?s={search_term}). However, when you handle the parameters map in a controller function or in handle_params/3, you need convert those parameters back to the parameter format that Flop understands. Flop.nest_filters/3 and Flop.map_to_filter_params/2 can partially help with that, but it is still more effort than it should be. Also, you'll have to define both the URL builder and the URL parser function.

It would be great to have some way to define the URL mapping once in a concise format, and have Flop.Phoenix do the building/parsing for you.

Add support for phoenix_html 3.0.0

Describe the bug
phoenix_html 3.0.0 was recently (like very recently) released and flop_phoenix is pegged to ~> 2.14 which is preventing us from updating.

To Reproduce
Specify phoenix_html to 3.0.0 in your mix file as well as flop_phoenix 0.7.0

Expected behavior
Can update :)

Versions:

  • Flop.Phoenix: 0.7.0
  • Flop: 0.11.0
  • Elixir: 1.12.0
  • Erlang: 24
  • Phoenix.HTML: 3.0.0
  • Phoenix.LiveView: 0.16.0
  • Ecto:
  • Ecto SQL:

Use stream resetting/bulk insert functionality

The original live_view stream implementation did not have a way to reset the stream to clear it's elements when paginating.

Flop worked around this issue by using the row number instead of the items ID and updating the stream items in place, but the upcoming version of streams now has reset and bulk insert functionality.

Describe the solution you'd like
Use the streams reset to clear existing items when paginating instead of mutating existing rows for clarity. Additionally, a list of items can be passed in instead of iterating the insertion.

Issues
This new version of live_view hasn't been released just yet - this is primarily to track an eventual transition to the more logical APIs now that they exist.

Example in hidden_inputs_for_filter/1 not working as expected

Describe the bug
Hey @woylie,

I currently have a case where filter_fields/1 is a bit to static for us, since we need to render the filters in separate section, since there are so many that we want to group them nicely. The docs suggest that one can use https://hexdocs.pm/flop_phoenix/0.17.0/Flop.Phoenix.html#hidden_inputs_for_filter/1 in combination with inputs_for to render a single filter field, but I can't even get the example into a workable state and I suspect there is something wrong with it for using this with more than one filter.

<.form :let={f} for={@meta}>
  <.hidden_inputs_for_filter form={@form} />

  <div class="field-group">
    <div class="field">
      <%= for ff <- Phoenix.HTML.Form.inputs_for(f, :filters, fields: [:name]) do %>
        <.hidden_inputs_for_filter form={ff} />
        <.input label="Name" type="text" field={{ff, :value}} />
      <% end %>
    </div>
    <div class="field">
      <%= for ff <- Phoenix.HTML.Form.inputs_for(f, :filters, fields: [:email]) do %>
        <.hidden_inputs_for_filter form={ff} />
        <.input label="E-mail" type="email" field={{ff, :value}} />
      <% end %>
    </div>
  </div>
</.form>

The example produces two form fields, both with the same name/id filters[0][value]/ flop_filters_0_value which obviously does not work.

This makes sense, since the to generate the fields we loop over the fields with Enum.with_index so both end up with 0
https://github.com/woylie/flop_phoenix/blob/0.17.0/lib/flop_phoenix/form_data.ex#L82-L114

To Reproduce
Steps to reproduce the behavior:
Implement the example from the docs

Expected behavior
Not sure if I just got something wrong, but I think flop currently does not allow what is outlined in the example.

Versions:

  • Flop.Phoenix: 0.16.0
  • Flop: 0.18.2
  • Elixir: 1.14.1
  • Erlang: 25.1.2
  • Phoenix.HTML: 3.2.0
  • Phoenix.LiveView: 0.18.2
  • Ecto: 3.9.1
  • Ecto SQL: 3.9.0

Additional context

Maybe we can get that to work if one could pass field: and index: as options to the inputs_for instead of fields, so one can actually implement something like the example suggests.

Thank you a lot in advance and for this great tool ๐Ÿ’š

Cheers
Andi

Further Table Customization

Describe the solution you'd like
Adding :tbody for further customisation options for the table generation / opts.

Additional context
For getting further use out of tailwindcss, for example adding the claasses divide-y divide-zinc-100 border-t border-zinc-200 would be nice to be easily able to add

support passing path helper as mfa tuple

At the moment, the components require to set the path_helper and path_helper_args assigns.

path_helper={&Routes.pet_path/3}
path_helper_args={[@socket, :index]}

It would be good to support passing a single mfa tuple instead.

path_helper={{Routes, :pet_path, [@socket, :index]}}

allow passing additional values with click events

When a component is used multiple times in the same view and click events are used for updates, it might be necessary to pass additional parameters besides the page number with the click event.

Table footer

The sortable table should have an option for a footer. It could work the same way as the header option.

Make rows clickable

Is your feature request related to a problem? Please describe.
I would like to add an option to the table, to make each row easily clickable with the item available.

Describe the solution you'd like
Add extra attr to table

Describe alternatives you've considered
I've trade adding phx-click to each col but I couldn't quite get it working with the params as the :let didn't exist for the other attrs.

Unnest opts

With the assigns map for function components, there is no need to keep the opts under a separate key. Instead of this:

<Flop.Phoenix.table
  items={@pets}
  meta={@meta}
  path_helper={&Routes.pet_path/3}
  path_helper_args={[@socket, :index]}
  headers={table_headers()}
  row_func={&table_row/2},
  opts={[for: MyApp.Pet, socket: @socket]}
/>

We can have this:

<Flop.Phoenix.table
  items={@pets}
  meta={@meta}
  path_helper={&Routes.pet_path/3}
  path_helper_args={[@socket, :index]}
  headers={table_headers()}
  row_func={&table_row/2},
  for={MyApp.Pet}
  socket={@socket}
/>

support custom URL builder

At the moment, flop_phoenix attaches all parameters as query parameters. However, sometimes you might prefer to set the page with a path parameter (/pets/page/2). Maybe you would even want to set a certain filter parameter in the path (/pets/species/dog, /articles/category/announcements). This could probably be achieved by allowing the user to override the default build_path function. In combination with #111, this would mean that you could pass the path with an mfa tuple pointing to a route helper, a string, or a function that takes a flop and returns a path.

MFA:

<.pagination meta={@meta} path={{Routes, :pets, [@socket, :index]}} />

Unverified route:

<.pagination meta={@meta} path="/pets" />

Verified route:

<.pagination meta={@meta} path={~p"/pets"} />

Custom path builder for page in path:

<.pagination meta={@meta} path={&build_pets_path/1} />

def build_pets_path(params) do
  {page, params} = Keyword.pop(params, :page)
  {species, params} = Flop.pop_filter(params, :species)

  case species do
    nil -> ~p"/pets/page/#{page}?#{params}"
    species -> ~p"/pets/species/#{species}/page/#{page}?#{params}"
  end
end

Tasks

  • build_path/3: handle function as first argument
  • validate_path_helper_or_event!/2: update validation to accept function
  • ensure all components accept a function as path
  • Flop: add pop_filter/2

refactor filter components

Phoenix/LiveView is moving away from the render functions in Phoenix.HTML.Form (input, label etc.). Flop Phoenix will too. All functions that render HTML should be replaced by Phoenix components, and if render functions are passed as arguments to the components, these functions should be expected to be Phoenix components as well.

Furthermore, I think the only way to fix the warnings in #117 is to only pass the attributes to the inner block of filter_fields/1, instead of passing the rendered label and input.

  • remove filter_label/1 and filter_input/1
  • replace filter_hidden_inputs_for/1 with a Phoenix component
  • rewrite filter_fields/1 to only pass computed assigns to inner block instead of heex
  • add example for rendering inputs for a single filter

Provide a mechanism to pass in additional querystring params to page link helpers for sorting, pagination, and filtering

Is your feature request related to a problem? Please describe.
I need to pass in additional querystring params keys and values to pagination and sorting helpers so that I can maintain state on GET calls.

I'm blocked adding new features for lists that need to send params through to control their visibility and filter tweaking as they get reset/dropped when the user attempts to paginate or sort.

Describe the solution you'd like
Ideally a simple way to specify keys and values that should be included in the page link builder to the server along with the handled page=1&page_size=15 params that are submitted.

It might be nice also that instead of the whitelist of params that Flop Phoenix builds for its helper links that it includes any other querystring params sent by the server on the last request. It seems that the params that are being built are explicit and only concern the page link helpers.

That do you think Mattias?

Invalid pagination querystring being produced when pagination_type is :page (`limit` instead of `page_size`)

Hi Woylie,

I noticed that with the newest release here of 0.6.0, when I'm working with ":page" style pagination (page/page_size) I'm getting errors with the link builder helper in the UI where it is producing a URL of "http://localhost:4000/berries?page=2&limit=15".

On the previous release when I had default_limit and max_limit set to 15 each limit in the URL above would resolve to the correct page_size param as in "http://localhost:4000/berries?page=2&page_size=15"

This is with both Flop 0.10.0 and 0.9.1. Confirmed that the issue does not exist with flop_phoenix 0.5.0.

Here's what i have in my config.exs

config :flop,
  repo: App.Repo,
  default_limit: 15,
  pagination_types: [:page]

And here's what's being produced when I inspect flop.meta within the build_page_link_helper function:

%Flop{
  after: nil,
  before: nil,
  filters: [],
  first: nil,
  last: nil,
  limit: 15,
  offset: nil,
  order_by: nil,
  order_directions: nil,
  page: nil,
  page_size: nil
}

Any thoughts here?

Cheers,
Jesse

Updates for Phoenix 1.7 / improvements

  • use new inputs_for component, potentially remove hidden_inputs_for
  • pass Phoenix.HTML.FormField struct in filter_fields component
  • refactor FormData tests
  • FormData: ensure ID can be overridden
  • replace Phoenix.HTML.Form.form_for/3 in tests
  • update input_type to consider ecto_type option
  • refactor links

pass default values from options to components

It is possible to pass default_limit and default_order options to the Flop query functions and to Flop.Phoenix.build_path/3, but the components do not know about the used options when calling build_path/3, and thus those parameters cannot be removed from the query parameters.

Support Bootstrap style pagination

Bootstrap uses a ul and li for all parts of the pagination items. The order of next, previous, and list matter.

<nav aria-label="Page navigation example">
  <ul class="pagination">
    <li class="page-item"><a class="page-link" href="#">Previous</a></li>
    <li class="page-item"><a class="page-link" href="#">1</a></li>
    <li class="page-item"><a class="page-link" href="#">2</a></li>
    <li class="page-item"><a class="page-link" href="#">3</a></li>
    <li class="page-item"><a class="page-link" href="#">Next</a></li>
  </ul>
</nav>

https://getbootstrap.com/docs/5.0/components/pagination/

The pagination is very configurable but I didn't see how I could configure it to generate the necessary markup.

Thanks for your consideration and happy to contribute given some guidance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.