Current section
48 Versions
Jump to
Current section
48 Versions
Compare versions
15
files changed
+233
additions
-104
deletions
| @@ -1,5 +1,68 @@ | |
| 1 1 | # Changelog |
| 2 2 | |
| 3 | + ## 0.9.0 |
| 4 | + |
| 5 | + ### Changes |
| 6 | + |
| 7 | + - [Text input](`PrimerLive.Component.text_input/1`): Set disabled color on read-only field with inset style. |
| 8 | + - [Checkbox group](`PrimerLive.Component.checkbox_group/1`) and [Radio group](`PrimerLive.Component.radio_group/1`): Show required marker in legend. |
| 9 | + - [Action menu](`PrimerLive.Component.action_menu/1`) and [Select menu](`PrimerLive.Component.select_menu/1`): Added attribute `offset_x` to define the absolute offset for the menu on the horizontal axis. |
| 10 | + - [Header](`PrimerLive.Component.header/1`): added attributes `variant="base"` to create a light colored header, and `is_compact` for a smaller vertical height. |
| 11 | + - Updated upstream Primer Design System libraries. This update includes a number of changes to modified CSS Custom Properties. |
| 12 | + - Updated upstream dependency `@primer/css` to `21.5.0`. |
| 13 | + - Added `@primer/view-components` version `0.34.0`. This is the Rails version of Primer Design, which is the most developed so far. |
| 14 | + - Updated [Octicon](`PrimerLive.Component.octicon/1`) icons to version `v19.12.0`. |
| 15 | + - Added support for right-to-left languages. |
| 16 | + - Added support for `@scope`, allowing PrimerLive to be used alongside other CSS libraries. See usage instructions in [Installation](doc-extra/installation.md#scoped-css). |
| 17 | + - Added documentation [Styling](doc-extra/styling.md). This contains notes about styling with custom CSS and pre-made classes, reusing styled components and support for right-to-left languages. |
| 18 | + |
| 19 | + ### Depreciations |
| 20 | + |
| 21 | + Standalone `form_control` as wrapper is soft-deprecated. |
| 22 | + |
| 23 | + The use of `form_control` as wrapper component around a form input component was causing compatibility issues with displaying validation messages. Since input components should be able to display a validation message even when not wrapped inside a `form_control`, it became difficult to distinguish between a validation messages originating from the `form_control` and one originating from the nested input. |
| 24 | + |
| 25 | + With the introduction of `checkbox_group` and `radio_group` in version 0.5, there has been less need for a standalone form control wrapper. |
| 26 | + |
| 27 | + Overall, moving `form_control` to the background and using it only as an internal component resolves a range of problems. |
| 28 | + |
| 29 | + See "Updating to 0.9" below. |
| 30 | + |
| 31 | + ### Removals |
| 32 | + |
| 33 | + - `form_group` was soft-deprecated in 0.5.0 and has been removed, along with component attributes `form_group` and `is_form_group`. |
| 34 | + |
| 35 | + ### Bug fixes |
| 36 | + |
| 37 | + - [Checkbox group](`PrimerLive.Component.checkbox_group/1`) and [Radio group](`PrimerLive.Component.radio_group/1`): Restored display of validation message. |
| 38 | + - [Textarea](`PrimerLive.Component.textarea/1`): Set correct success color when displaying success feedback. |
| 39 | + |
| 40 | + ### Updating to 0.9 |
| 41 | + |
| 42 | + #### form_control |
| 43 | + |
| 44 | + There have 2 ways to create a form control: as a wrapper, and as an input attribute. If you've been using `form_control` as wrapper around inputs, change: |
| 45 | + |
| 46 | + ``` |
| 47 | + <.form :let={f} for={@changeset}> |
| 48 | + <.form_control form={f} field={:first_name} for="first-name"> |
| 49 | + <.text_input form={f} field={:first_name} id="first-name" /> |
| 50 | + </.form_control> |
| 51 | + </.form> |
| 52 | + ``` |
| 53 | + |
| 54 | + to |
| 55 | + |
| 56 | + ``` |
| 57 | + <.form :let={f} for={@changeset}> |
| 58 | + <.text_input form={f} field={:first_name} id="first-name" is_form_control /> |
| 59 | + </.form> |
| 60 | + ``` |
| 61 | + |
| 62 | + #### CSS Custom Properties (Variables) |
| 63 | + |
| 64 | + If you are using CSS Custom Properties from the Primer Design System, these may need to be updated. Verify the current available options in [primer-live.css](https://github.com/ArthurClemens/primer_live/blob/development/priv/static/primer-live.css). |
| 65 | + |
| 3 66 | ## 0.8.0 |
| 4 67 | |
| 5 68 | ### Refactoring of dialogs, drawers, and menus |
| @@ -515,3 +578,7 @@ Bug fix: | |
| 515 578 | ## 0.1.1 |
| 516 579 | |
| 517 580 | First release. |
| 581 | + |
| 582 | + ``` |
| 583 | + |
| 584 | + ``` |
| @@ -1,14 +1,14 @@ | |
| 1 1 | # PrimerLive |
| 2 2 | |
| 3 | - An implementation of GitHub's [Primer Design System](https://primer.style/design) for Phoenix LiveView. |
| 3 | + An implementation of GitHub's [Primer Design System](https://primer.style/design) for Phoenix LiveView and regular (non-LiveView) views in Phoenix applications. |
| 4 4 | |
| 5 | - Primer Design provides a strong base for creating data driven applications such as rich CRUD applications with interactive forms. |
| 5 | + PrimerLive provides a strong base for creating data driven applications such as rich CRUD applications with interactive forms that integrate with Phoenix Forms, Ash Forms and Ecto Changesets. |
| 6 6 | |
| 7 | - All PrimerLive components accept the `class` attribute for customisations (and often the `classes` struct to address inner elements). [Primer's utility classes](https://primer.style/design/foundations/css-utilities/getting-started) allow a wide range of customisations without having to write custom styles. |
| 7 | + Menus and dialogs allow for in-page viewing and editing of data, without leaving the current page. Alternatively, menus and dialogs can be invoked when visiting a specific route. |
| 8 8 | |
| 9 | - PrimerLive components can be used in [Phoenix LiveView pages](https://github.com/phoenixframework/phoenix_live_view) and regular (non-LiveView) views in Phoenix applications. |
| 9 | + Components (and their inner elements) can be styled with custom CSS, or by passing pre-made utility classes. |
| 10 10 | |
| 11 | - Forms created with [Ash Framework](https://www.ash-hq.org/) are supported. |
| 11 | + Right-to-left languages are supported. |
| 12 12 | |
| 13 13 | ## Documentation |
| @@ -1,7 +1,7 @@ | |
| 1 1 | {<<"links">>, |
| 2 2 | [{<<"GitHub">>,<<"https://github.com/ArthurClemens/primer_live">>}]}. |
| 3 3 | {<<"name">>,<<"primer_live">>}. |
| 4 | - {<<"version">>,<<"0.8.0">>}. |
| 4 | + {<<"version">>,<<"0.9.0">>}. |
| 5 5 | {<<"description">>, |
| 6 6 | <<"An implementation of GitHub's Primer Design System for Phoenix LiveView.">>}. |
| 7 7 | {<<"elixir">>,<<"~> 1.17">>}. |
| @@ -20,10 +20,12 @@ | |
| 20 20 | <<"LICENSE.md">>,<<"priv/static">>,<<"priv/static/primer-live.cjs.js.map">>, |
| 21 21 | <<"priv/static/primer-live.css">>,<<"priv/static/primer-live.esm.js">>, |
| 22 22 | <<"priv/static/primer-live.cjs.js">>,<<"priv/static/prompt.d.ts">>, |
| 23 | - <<"priv/static/primer-live.min.css">>,<<"priv/static/primer-live.min.js">>, |
| 24 | - <<"priv/static/primer-live.js">>,<<"priv/static/index.d.ts">>, |
| 25 | - <<"priv/static/primer-live.esm.js.map">>,<<"CHANGELOG.md">>, |
| 26 | - <<"package.json">>]}. |
| 23 | + <<"priv/static/primer-live.min.css">>, |
| 24 | + <<"priv/static/primer-live-scoped.css">>, |
| 25 | + <<"priv/static/primer-live-scoped.min.css">>, |
| 26 | + <<"priv/static/primer-live.min.js">>,<<"priv/static/primer-live.js">>, |
| 27 | + <<"priv/static/index.d.ts">>,<<"priv/static/primer-live.esm.js.map">>, |
| 28 | + <<"CHANGELOG.md">>,<<"package.json">>]}. |
| 27 29 | {<<"requirements">>, |
| 28 30 | [[{<<"name">>,<<"ecto">>}, |
| 29 31 | {<<"app">>,<<"ecto">>}, |
unknownlib/component.ex
File is too large to be displayed (100 KB limit).
| @@ -551,70 +551,80 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 551 551 | |
| 552 552 | ## Tests |
| 553 553 | |
| 554 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id("my-input-id", nil, nil, "", nil) |
| 554 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id("my-input-id", nil, nil, "", nil, false) |
| 555 555 | "my-input-id" |
| 556 556 | |
| 557 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, "my-id", nil, "", nil) |
| 557 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id("my-input-id", nil, nil, "", nil, true) |
| 558 | + "my-input-id" |
| 559 | + |
| 560 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id("my-input-id", nil, nil, "", "my-value", true) |
| 561 | + "my-input-id-my-value" |
| 562 | + |
| 563 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, "my-id", nil, "", nil, false) |
| 558 564 | "my-id" |
| 559 565 | |
| 560 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "", nil) |
| 566 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "", nil, false) |
| 561 567 | nil |
| 562 568 | |
| 563 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "[my-input-name]", nil) |
| 569 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "[my-input-name]", nil, false) |
| 564 570 | "[my-input-name]" |
| 565 571 | |
| 566 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "[my-input-name][]", nil) |
| 572 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :select, "[my-input-name][]", nil, false) |
| 567 573 | "[my-input-name]" |
| 568 574 | |
| 569 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "", nil) |
| 575 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "", nil, false) |
| 570 576 | nil |
| 571 577 | |
| 572 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "my-input-name", nil) |
| 578 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "my-input-name", nil, false) |
| 573 579 | "my-input-name" |
| 574 580 | |
| 575 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "my-input-name", "my-value") |
| 581 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "my-input-name", "my-value", false) |
| 576 582 | "my-input-name[my-value]" |
| 577 583 | |
| 578 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name]", "my-value") |
| 584 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name]", "my-value", false) |
| 579 585 | "[my-input-name][my-value]" |
| 580 586 | |
| 581 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name][]", "my-value") |
| 587 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name][]", "my-value", false) |
| 582 588 | "[my-input-name][my-value]" |
| 583 589 | |
| 584 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name][]", nil) |
| 590 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :checkbox, "[my-input-name][]", nil, false) |
| 585 591 | "[my-input-name][]" |
| 586 592 | |
| 587 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "", nil) |
| 593 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "", nil, false) |
| 588 594 | nil |
| 589 595 | |
| 590 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "my-input-name", nil) |
| 596 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "my-input-name", nil, false) |
| 591 597 | "my-input-name" |
| 592 598 | |
| 593 | - iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "my-input-name", "my-value") |
| 599 | + iex> PrimerLive.Helpers.AttributeHelpers.input_id(nil, nil, :radio_button, "my-input-name", "my-value", false) |
| 594 600 | "my-input-name[my-value]" |
| 595 601 | |
| 596 602 | """ |
| 597 | - def input_id(input_id, _id, _input_type, _input_name, _value_for_derived_label) |
| 603 | + def input_id(input_id, _id, _input_type, _input_name, value_for_derived_label, is_multiple) |
| 604 | + when not is_nil(input_id) and is_multiple and is_binary(value_for_derived_label), |
| 605 | + do: "#{input_id}-#{value_for_derived_label}" |
| 606 | + |
| 607 | + def input_id(input_id, _id, _input_type, _input_name, _value_for_derived_label, _is_multiple) |
| 598 608 | when not is_nil(input_id), |
| 599 609 | do: input_id |
| 600 610 | |
| 601 | - def input_id(_input_id, id, _input_type, _input_name, _value_for_derived_label) |
| 611 | + def input_id(_input_id, id, _input_type, _input_name, _value_for_derived_label, _is_multiple) |
| 602 612 | when not is_nil(id), |
| 603 613 | do: id |
| 604 614 | |
| 605 | - def input_id(_input_id, _id, input_type, "", _value_for_derived_label) |
| 615 | + def input_id(_input_id, _id, input_type, "", _value_for_derived_label, _is_multiple) |
| 606 616 | when input_type === :select, |
| 607 617 | do: nil |
| 608 618 | |
| 609 | - def input_id(_input_id, _id, input_type, input_name, _value_for_derived_label) |
| 619 | + def input_id(_input_id, _id, input_type, input_name, _value_for_derived_label, _is_multiple) |
| 610 620 | when input_type === :select, |
| 611 621 | do: input_name |> String.replace(~r/\[\]$/, "") |
| 612 622 | |
| 613 | - def input_id(_input_id, _id, input_type, "", value_for_derived_label) |
| 623 | + def input_id(_input_id, _id, input_type, "", value_for_derived_label, _is_multiple) |
| 614 624 | when input_type === :checkbox or input_type === :radio_button, |
| 615 625 | do: value_for_derived_label |
| 616 626 | |
| 617 | - def input_id(_input_id, _id, input_type, input_name, value_for_derived_label) |
| 627 | + def input_id(_input_id, _id, input_type, input_name, value_for_derived_label, _is_multiple) |
| 618 628 | when (input_type === :checkbox or input_type === :radio_button) and is_binary(input_name) do |
| 619 629 | cond do |
| 620 630 | String.match?(input_name, ~r/\[\]$/) -> |
| @@ -628,8 +638,10 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 628 638 | end |
| 629 639 | end |
| 630 640 | |
| 631 | - def input_id(_input_id, _id, _input_type, "", _value_for_derived_label), do: nil |
| 632 | - def input_id(_input_id, _id, _input_type, input_name, _value_for_derived_label), do: input_name |
| 641 | + def input_id(_input_id, _id, _input_type, "", _value_for_derived_label, _is_multiple), do: nil |
| 642 | + |
| 643 | + def input_id(_input_id, _id, _input_type, input_name, _value_for_derived_label, _is_multiple), |
| 644 | + do: input_name |
| 633 645 | |
| 634 646 | @spec cleanup_id(nil | binary) :: nil | binary |
| 635 647 | def cleanup_id(id) when is_nil(id), do: nil |
| @@ -650,10 +662,6 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 650 662 | # ID and label |
| 651 663 | id_attrs = common_id_attrs(assigns, input_type, common_shared_attrs) |
| 652 664 | |
| 653 | - # Form group |
| 654 | - form_control_attrs = |
| 655 | - common_form_control_attrs(assigns, id_attrs.input_id, common_shared_attrs) |
| 656 | - |
| 657 665 | # Field state |
| 658 666 | field_state_attrs = |
| 659 667 | common_field_state_attrs( |
| @@ -662,6 +670,11 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 662 670 | common_shared_attrs |
| 663 671 | ) |
| 664 672 | |
| 673 | + # Form control |
| 674 | + form_control_attrs = |
| 675 | + common_form_control_attrs(assigns, id_attrs.input_id, common_shared_attrs) |
| 676 | + |> Map.merge(field_state_attrs |> Map.drop([:caption])) |
| 677 | + |
| 665 678 | [ |
| 666 679 | common_shared_attrs, |
| 667 680 | id_attrs, |
| @@ -708,8 +721,16 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 708 721 | value_for_derived_label = checked_value || value |
| 709 722 | |
| 710 723 | input_id = |
| 711 | - input_id(assigns[:input_id], id, input_type, input_name, value_for_derived_label) |
| 712 | - |> create_dom_id() |
| 724 | + assigns.input_id || |
| 725 | + input_id( |
| 726 | + assigns[:input_id], |
| 727 | + id, |
| 728 | + input_type, |
| 729 | + input_name, |
| 730 | + value_for_derived_label, |
| 731 | + is_multiple |
| 732 | + ) |
| 733 | + |> create_dom_id() |
| 713 734 | |
| 714 735 | derived_label = |
| 715 736 | case input_type do |
| @@ -732,31 +753,18 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 732 753 | form: form, |
| 733 754 | field_or_name: field_or_name |
| 734 755 | }) do |
| 735 | - deprecated_form_group = assigns[:form_group] |
| 736 | - deprecated_is_form_group = !!assigns[:is_form_group] |
| 737 | - deprecated_has_form_group = deprecated_is_form_group || !!deprecated_form_group |
| 756 | + form_control = assigns[:form_control] |
| 757 | + is_form_control = assigns[:is_form_control] || !!form_control |
| 738 758 | |
| 739 | - ComponentHelpers.deprecated_message( |
| 740 | - "Deprecated attr form_group: use form_control. Since 0.5.0.", |
| 741 | - !is_nil(assigns[:form_group]) |
| 742 | - ) |
| 743 | - |
| 744 | - ComponentHelpers.deprecated_message( |
| 745 | - "Deprecated attr is_form_group: use is_form_control. Since 0.5.0.", |
| 746 | - assigns[:is_form_group] == true |
| 747 | - ) |
| 748 | - |
| 749 | - form_control = assigns[:form_control] || deprecated_form_group |
| 750 | - is_form_control = assigns[:is_form_control] || !!form_control || deprecated_is_form_group |
| 751 | - |
| 752 | - has_form_control = is_form_control || deprecated_has_form_group |
| 759 | + has_form_control = is_form_control |
| 753 760 | |
| 754 761 | form_control_attrs = |
| 755 762 | Map.merge(form_control || %{}, %{ |
| 756 763 | form: form, |
| 757 764 | field: field_or_name, |
| 765 | + input_id: input_id, |
| 758 766 | for: input_id, |
| 759 | - deprecated_has_form_group: deprecated_has_form_group |
| 767 | + is_full_width: assigns[:is_full_width] |
| 760 768 | }) |
| 761 769 | |
| 762 770 | %{ |
| @@ -765,7 +773,7 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 765 773 | } |
| 766 774 | end |
| 767 775 | |
| 768 | - defp common_field_state_attrs(assigns, %{input_name: input_name, input_id: input_id}, %{ |
| 776 | + defp common_field_state_attrs(assigns, %{input_name: input_name, input_id: input_id, id: id}, %{ |
| 769 777 | form: form, |
| 770 778 | field_or_name: field_or_name |
| 771 779 | }) do |
| @@ -785,10 +793,13 @@ defmodule PrimerLive.Helpers.AttributeHelpers do | |
| 785 793 | show_message? = !!message && !ignore_errors? && assigns[:type] !== "hidden" |
| 786 794 | |
| 787 795 | validation_message_id = |
| 788 | - if !is_nil(field_state.message), |
| 789 | - do: |
| 790 | - assigns[:validation_message_id] || |
| 791 | - "#{input_id}-validation" |
| 796 | + cond do |
| 797 | + !show_message? -> nil |
| 798 | + assigns[:validation_message_id] -> assigns[:validation_message_id] |
| 799 | + input_id -> "#{input_id}-validation" |
| 800 | + id -> "#{id}-validation" |
| 801 | + true -> random_string() |
| 802 | + end |
| 792 803 | |
| 793 804 | validation_marker_class = |
| 794 805 | if has_changeset? do |
Loading more files…