Coder Social home page Coder Social logo

surface_bulma's People

Contributors

aarongraham avatar feliperenan avatar justin-m-morgan avatar kianmeng avatar michaelst avatar msaraiva avatar olivermt avatar zurga 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

surface_bulma's Issues

Putting the CDN inside a layout_view.ex not compile

I was trying to use bulma's CSS styles sing CDN and when I try to put inside the layout_view.ex.
The phoenix not compile.
I installed doing by:
npm install --save-dev bulma
and adding on the app.scss:
@import 'bulma';

Form control tracking issue

  • Checkbox
  • Colorinput
  • Dateinput
  • Dateselect
  • Datetimelocalinput
  • datetimeselect
  • emailinput
  • fileinput
  • multipleselect
  • numberinput
  • passwordinput
  • radiobutton
  • rangeinput
  • searchinput
  • select
  • submit
  • telephoneinput
  • textarea
  • textinput
  • timeinput
  • timeselect
  • urlinput

how to use catalogue for components that require state

If not solved yet, could we have a discussion on how to do stuff like Modal where you pass it a show true/false.

To use a modal (from this lib) you need a liveview or a live component that controls it.
How do we set up demos in surface catalogue for componets like that?

Issues combining <Form> and <Modal.Card>

At the moment, I am implementing some edit forms to use Modal.Card, like this one:

modal-form

Before the last update, I had implemented a Modal.Card myself, which I got rid of now because it had a similar design. It looks reasonably well, but there are some minor issues:

  • the header title is shown only if close button is shown
  • the close button interferes with my form and triggers a form submit. Perhaps setting type="button" on the close button could prevent that
  • (optional) closing on ESC-Button or clicking the background would be nice
  • it feels a bit ugly to wrap the whole Card component into the Form (see below), but I want to show the form submit button inside the modal footer.

Structure

<Form ...>
  <Card ...>
    <Header>Title</Header>
      ...some form fields....
    <Footer>
      <Form.Submit .../>
      <Button ...>Cancel</Button>
    </Footer>
  </Card>
</Form>

Will it work with Surface 0.7.0?

mix test test/bulma_test/surface_bulma/form_test.exs

  1. test form as an atom (SurfaceBulma.Components.FormTest)
    test/bulma_test/surface_bulma/form_test.exs:37
    ** (RuntimeError) invalid value for property "errors". Expected a :keyword, got: nil.

I am looking forward to use surface bulma on a project.

Passing function to Column `sort_by` results in an error

I have a Table with a date column and I'm trying to use the sort_by prop described in the documentation for Column:

  <Column label="Date" sort_by={{ {fn i -> Map.get(i, :datefield) end, &Date.compare/2} }}>
    {{ formatted_date(row.inserted_at) }}
  </Column>

This results in the following error:

invalid value for attribute "phx-value-value". Expected a type that implements the String.Chars protocol (e.g. string, boolean, integer, atom, ...), got: {#Function<1.56407787/1 in MyAppWeb.Live.Components.Table.render/1>, &Date.compare/2}

I'm thinking the problem happens when SurfaceBulma.Table passes col.sort_by as the phx-value-value prop for the anchor tag for sorted_click events, since phx-value-value appears to need a string but is getting the tuple instead?

Or am I doing something wrong? I'm pretty new to both Surface and LiveView. Thanks in advance!

Table doesn't always refresh when value of `data` prop is changed

When the data prop of SurfaceBelma.Table is changed, if the internal sorted_by assign is nil, then sorted_data/1 returns the new data value only if the sorted_data assign is nil, which is true only on mount.

As a result, the table does not render changes made to the data prop until it is remounted or resorted.

(As a workaround, I was able to force a refresh by calling send_update/3 to trigger an update, passing sorted_data as an assign set to the updated value of data, so that it would be the value returned by sorted_data/1.)

If I can find the time in the next few days I'll try to submit a fix, but I wanted to get this recorded before I forget.

General discussion of stateless vs stateful components

Thanks for all the work going on here to build reusable components for live_view using Bulma.

Recently the new implementation of Panel replaced my own rather naive one, but a default slot is missing. Looking into it, the new Panel seems to be a stateful component handling switching tabs etc.

Since now I had the impression that most components in surface_bulma were intentionally stateless and focused on composing the structure needed to make use of Bulma properly.

Concerning Panel, it should work without any tabs at all, as they are optional, like multiple panel-blocks. And for handling tab switching, it has to be considered whether it is just an UI matter and could be done on the client (via apine.js, etc.) or if it requires behavior on the server (data fetching, etc) that should be handled by LiveView.

Imho, surface_bulma should enable both by keeping components as lean and stateless as possible by focusing on composing the presentation needed for Bulma. On top of that, there could be components that make use of these presentations to enable behaviours such as stateful tabs.

Another example would be a Dropdown component. I am working on an autocomplete component based on Bulmas Dropdown, because it is very flexible in displaying the content. In context of an autocomplete function it does make sense to handle state in my component, as there is open/closing the dropdown, a search string, fetching data, selecting an item, switching the trigger component from an input to a more beautiful representation of the item, etc.
But in general, if the Dropdown is just presenting a static bunch of information or links that are followed, a stateless component with properties setting the required classes ("is-active"/"is-hoverable") should be fine. So the component user has the choice how to toggle its state. And when properly thought out, it is easy to extend with stateful features.

Yet another example would be static tables vs interactive ones that enable sorting and such.

Sorry for writing that much, I just thought it would be wise to point out the direction of the library before we start implementing many components this way or the other.

The other point is that I'd probably wait until surface will work well with live_view >= 0.17 before diving into bulma components more deeply. The recent changes decenty messed up my development environment 😄

implement widths for table columns and scss changes in general

This requires scss work and will not work with CDN linked bulma.

Not sure what to do here.
Expose the scss stuff and let people link it in as an asset?

The scss to enable this is pretty trivial: https://gist.github.com/alalfakawma/b94de00f31895b91077a2e3f71ace1fe

But in general a lot around bulma revolves around altering variables in a scss setting, so the library should probably enable a way to utilize scss stuff from priv or asset folder, then expose that back out as something you can use in a ´link´ element.

Would appreciate feedback / oppinions @msaraiva :)

`class` prop seems to be required in `<Title/>`

Some chosen deps:

[     {:phoenix, "~> 1.7.7"},
      {:phoenix_ecto, "~> 4.4"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_dashboard, "~> 0.7.0"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.18.18"},
      {:surface, "~> 0.10.0"},
      {:surface_bulma, github: "surface-ui/surface_bulma"},
    # ...
]

Minimal reproduction:

defmodule PainWeb.Order do
  use PainWeb, :surface_live_view

  alias SurfaceBulma.Title

  def render(assigns) do
    ~F"""
      <Title size="3" class="" >Schedule an appointment</Title>
    """
  end
end

...renders:

image

defmodule PainWeb.Order do
  use PainWeb, :surface_live_view

  alias SurfaceBulma.Title

  def render(assigns) do
    ~F"""
      <Title size="3">Schedule an appointment</Title>
    """
  end
end

...errors:


ArgumentError at GET /order

Exception:

** (ArgumentError) argument error
    :erlang.++(["title", "is-3", {:"is-spaced", nil} | nil], [])
    (surface 0.10.0) lib/surface/type_handler/css_class.ex:35: Surface.TypeHandler.CssClass.expr_to_value/3
    (surface 0.10.0) lib/surface/type_handler.ex:150: Surface.TypeHandler.expr_to_value!/7
    (surface_bulma 0.4.0) lib/surface_bulma/title.ex:18: anonymous fn/2 in SurfaceBulma.Title."render (overridable 1)"/1
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:398: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:544: anonymous fn/4 in Phoenix.LiveView.Diff.traverse_dynamic/7
    (elixir 1.15.4) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:396: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:544: anonymous fn/4 in Phoenix.LiveView.Diff.traverse_dynamic/7
    (elixir 1.15.4) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:396: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:544: anonymous fn/4 in Phoenix.LiveView.Diff.traverse_dynamic/7
    (elixir 1.15.4) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:396: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/diff.ex:139: Phoenix.LiveView.Diff.render/3
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/static.ex:252: Phoenix.LiveView.Static.to_rendered_content_tag/4
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/static.ex:135: Phoenix.LiveView.Static.render/3
    (phoenix_live_view 0.18.18) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
    (phoenix 1.7.7) lib/phoenix/router.ex:430: Phoenix.Router.__call__/5
    (pain 0.1.0) lib/pain_web/endpoint.ex:1: PainWeb.Endpoint.plug_builder_call/2

Code:

nofile

No code available.

Called with 2 arguments

  • ["title", "is-3", {:"is-spaced", nil} | nil]
  • []

lib/surface/type_handler/css_class.ex

30       expr_to_value(value, opts, ctx)
31     end
32   
33     def expr_to_value(clauses, opts, ctx) do
34       value =
35>        Enum.reduce(clauses ++ opts, [], fn item, classes ->
36           case item do
37             list when is_list(list) ->
38               case expr_to_value(list, [], ctx) do
39                 {:ok, new_classes} -> Enum.reverse(new_classes) ++ classes
40                 error -> error

lib/surface/type_handler.ex

145           IOHelper.compile_error(message, meta.file, meta.line)
146       end
147     end
148   
149     def expr_to_value!(type, name, clauses, opts, module, original, ctx) do
150>      case handler(type).expr_to_value(clauses, opts, ctx) do
151         {:ok, value} ->
152           value
153   
154         {:error, value} ->
155           message = runtime_error_message(type, name, value, module, original)

lib/surface_bulma/title.ex

13     prop spaced, :boolean
14   
15     slot default
16   
17     def render(assigns) do
18>      ~F"""
19       <h1 :if={@size == "1"} class={["title", "is-1", "is-spaced": @spaced] ++ @class}><#slot /></h1>
20       <h2 :if={@size == "2"} class={["title", "is-2", "is-spaced": @spaced] ++ @class}><#slot /></h2>
21       <h3 :if={@size == "3"} class={["title", "is-3", "is-spaced": @spaced] ++ @class}><#slot /></h3>
22       <h4 :if={@size == "4"} class={["title", "is-4", "is-spaced": @spaced] ++ @class}><#slot /></h4>
23       <h5 :if={@size == "5"} class={["title", "is-5", "is-spaced": @spaced] ++ @class}><#slot /></h5>

lib/phoenix_live_view/diff.ex

393            changed?
394          ) do
395       {_counter, diff, children, pending, components, template} =
396         traverse_dynamic(
397           socket,
398>          invoke_dynamic(rendered, false),
399           %{},
400           pending,
401           components,
402           template,
403           changed?

lib/phoenix_live_view/diff.ex

539       Enum.reduce(dynamic, {0, %{}, children, pending, components, template}, fn
540         entry, {counter, diff, children, pending, components, template} ->
541           child = Map.get(children, counter)
542   
543           {serialized, child_fingerprint, pending, components, template} =
544>            traverse(socket, entry, child, pending, components, template, changed?)
545   
546           # If serialized is nil, it means no changes.
547           # If it is an empty map, then it means it is a rendered struct
548           # that did not change, so we don't have to emit it either.
549           diff =

lib/enum.ex

No code available.

lib/phoenix_live_view/diff.ex

391            components,
392            template,
393            changed?
394          ) do
395       {_counter, diff, children, pending, components, template} =
396>        traverse_dynamic(
397           socket,
398           invoke_dynamic(rendered, false),
399           %{},
400           pending,
401           components,

lib/phoenix_live_view/diff.ex

539       Enum.reduce(dynamic, {0, %{}, children, pending, components, template}, fn
540         entry, {counter, diff, children, pending, components, template} ->
541           child = Map.get(children, counter)
542   
543           {serialized, child_fingerprint, pending, components, template} =
544>            traverse(socket, entry, child, pending, components, template, changed?)
545   
546           # If serialized is nil, it means no changes.
547           # If it is an empty map, then it means it is a rendered struct
548           # that did not change, so we don't have to emit it either.
549           diff =

lib/enum.ex

No code available.

lib/phoenix_live_view/diff.ex

391            components,
392            template,
393            changed?
394          ) do
395       {_counter, diff, children, pending, components, template} =
396>        traverse_dynamic(
397           socket,
398           invoke_dynamic(rendered, false),
399           %{},
400           pending,
401           components,

lib/phoenix_live_view/diff.ex

539       Enum.reduce(dynamic, {0, %{}, children, pending, components, template}, fn
540         entry, {counter, diff, children, pending, components, template} ->
541           child = Map.get(children, counter)
542   
543           {serialized, child_fingerprint, pending, components, template} =
544>            traverse(socket, entry, child, pending, components, template, changed?)
545   
546           # If serialized is nil, it means no changes.
547           # If it is an empty map, then it means it is a rendered struct
548           # that did not change, so we don't have to emit it either.
549           diff =

lib/enum.ex

No code available.

lib/phoenix_live_view/diff.ex

391            components,
392            template,
393            changed?
394          ) do
395       {_counter, diff, children, pending, components, template} =
396>        traverse_dynamic(
397           socket,
398           invoke_dynamic(rendered, false),
399           %{},
400           pending,
401           components,

lib/phoenix_live_view/diff.ex

134       render(%{socket | fingerprints: new_fingerprints()}, rendered, new_components(uuids))
135     end
136   
137     def render(%{fingerprints: prints} = socket, %Rendered{} = rendered, components) do
138       {diff, prints, pending, components, nil} =
139>        traverse(socket, rendered, prints, %{}, components, nil, true)
140   
141       # cid_to_component is used by maybe_reuse_static and it must be a copy before changes.
142       # However, given traverse does not change cid_to_component, we can read it now.
143       {cid_to_component, _, _} = components
144   

lib/phoenix_live_view/static.ex

247       Phoenix.HTML.Tag.content_tag(tag, "", attrs)
248     end
249   
250     defp to_rendered_content_tag(socket, tag, view, attrs) do
251       rendered = Utils.to_rendered(socket, view)
252>      {_, diff, _} = Diff.render(socket, rendered, Diff.new_components())
253       Phoenix.HTML.Tag.content_tag(tag, {:safe, Diff.to_iodata(diff)}, attrs)
254     end
255   
256     defp load_live!(view_or_component, kind) do
257       case view_or_component.__live__() do

lib/phoenix_live_view/static.ex

130             {:data, data_attrs}
131             | extended_attrs
132           ]
133   
134           try do
135>            {:ok, to_rendered_content_tag(socket, tag, view, attrs), socket.assigns}
136           catch
137             :throw, {:phoenix, :child_redirect, redirected, flash} ->
138               {:stop, Utils.replace_flash(%{socket | redirected: redirected}, flash)}
139           end
140   

lib/phoenix_live_view/controller.ex

34           end
35         end
36   
37     """
38     def live_render(%Plug.Conn{} = conn, view, opts \\ []) do
39>      case LiveView.Static.render(conn, view, opts) do
40         {:ok, content, socket_assigns} ->
41           conn
42           |> Phoenix.Controller.put_view(LiveView.Static)
43           |> Phoenix.Controller.render(
44             "template.html",

lib/phoenix/router.ex

425           :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
426           halted_conn
427   
428         %Plug.Conn{} = piped_conn ->
429           try do
430>            plug.call(piped_conn, plug.init(opts))
431           else
432             conn ->
433               measurements = %{duration: System.monotonic_time() - start}
434               metadata = %{metadata | conn: conn}
435               :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)

lib/pain_web/endpoint.ex

1>  defmodule PainWeb.Endpoint do
2     use Phoenix.Endpoint, otp_app: :pain
3   
4     # The session will be stored in the cookie and signed,
5     # this means its contents can be read but not tampered with.
6     # Set :encryption_salt if you would also like to encrypt it.

Connection details

Params

%{}

Request info

Headers

  • accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
  • accept-encoding: gzip, deflate
  • accept-language: en-US,en;q=0.5
  • connection: keep-alive
  • cookie: _pain_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYQjZ3YURjTW9BZEtTMGk3YXJBeHVoVzlw.pvZkj7mPLmIkzMPehthaOXA0Sd10k9BEeo7YU6xaJKY
  • dnt: 1
  • host: 0.0.0.0:4000
  • upgrade-insecure-requests: 1
  • user-agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0

Session

%{"_csrf_token" => "B6waDcMoAdKS0i7arAxuhW9p"}

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.