anchor_view_components 0.20.1 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/app/assets/builds/anchor-view-components.css +1 -1
  4. data/app/assets/images/icons/calendar.svg +7 -0
  5. data/app/assets/images/icons/check.svg +4 -0
  6. data/app/assets/images/icons/paste-clipboard.svg +5 -0
  7. data/app/assets/images/icons/search.svg +5 -0
  8. data/app/assets/stylesheets/components/button.css +1 -1
  9. data/app/components/anchor/action_menu_component.html.erb +8 -10
  10. data/app/components/anchor/action_menu_component.rb +15 -6
  11. data/app/components/anchor/anchor_view_components.ts +2 -0
  12. data/app/components/anchor/autocomplete_component.html.erb +6 -7
  13. data/app/components/anchor/autocomplete_component.rb +29 -8
  14. data/app/components/anchor/badge_component.html.erb +3 -6
  15. data/app/components/anchor/badge_component.rb +0 -10
  16. data/app/components/anchor/banner_component.html.erb +1 -7
  17. data/app/components/anchor/banner_component.rb +0 -10
  18. data/app/components/anchor/button_component.html.erb +10 -17
  19. data/app/components/anchor/button_component.rb +14 -21
  20. data/app/components/anchor/component.rb +22 -5
  21. data/app/components/anchor/copy_to_clipboard_component.en.yml +2 -0
  22. data/app/components/anchor/copy_to_clipboard_component.html.erb +21 -0
  23. data/app/components/anchor/copy_to_clipboard_component.rb +15 -0
  24. data/app/components/anchor/copy_to_clipboard_controller.ts +34 -0
  25. data/app/components/anchor/dialog_component.html.erb +3 -4
  26. data/app/components/anchor/icon_component.html.erb +1 -3
  27. data/app/components/anchor/icon_component.rb +1 -1
  28. data/app/components/anchor/loading_indicator_component.html.erb +3 -4
  29. data/app/components/anchor/logo_component.html.erb +1 -4
  30. data/app/components/anchor/logo_component.rb +1 -1
  31. data/app/components/anchor/panel/body_component.html.erb +3 -5
  32. data/app/components/anchor/panel_component.html.erb +3 -1
  33. data/app/components/anchor/popover_component.html.erb +3 -7
  34. data/app/components/anchor/popover_controller.ts +21 -15
  35. data/app/components/anchor/prose_component.html.erb +5 -4
  36. data/app/components/anchor/radio_button_collection_component.html.erb +1 -0
  37. data/app/components/anchor/radio_button_collection_component.rb +35 -0
  38. data/app/components/anchor/radio_button_component.html.erb +11 -0
  39. data/app/components/anchor/radio_button_component.rb +105 -0
  40. data/app/components/anchor/side_nav_component.html.erb +8 -9
  41. data/app/components/anchor/side_nav_component.rb +4 -1
  42. data/app/components/anchor/tab_bar/tab_component.html.erb +8 -6
  43. data/app/components/anchor/tab_bar/tab_component.rb +2 -2
  44. data/app/components/anchor/tab_bar_component.html.erb +3 -4
  45. data/app/components/anchor/tab_bar_component.rb +3 -2
  46. data/app/components/anchor/table_component.rb +1 -1
  47. data/app/components/anchor/text_component.html.erb +1 -6
  48. data/app/components/anchor/text_component.rb +5 -14
  49. data/app/components/anchor/text_field_component.html.erb +19 -1
  50. data/app/components/anchor/text_field_component.rb +35 -16
  51. data/app/components/anchor/toast_component.html.erb +5 -8
  52. data/app/components/anchor/toast_component.rb +8 -12
  53. data/app/components/anchor/toast_controller.ts +5 -1
  54. data/app/helpers/anchor/fetch_or_fallback_helper.rb +26 -0
  55. data/app/helpers/anchor/form_builder.rb +52 -29
  56. data/app/helpers/anchor/tailwind_constants.rb +0 -37
  57. data/app/helpers/anchor/view_helper.rb +11 -0
  58. data/lib/anchor/view_components/engine.rb +0 -1
  59. data/lib/anchor/view_components/version.rb +1 -1
  60. data/previews/anchor/copy_to_clipboard_component_preview.rb +7 -0
  61. data/previews/anchor/forms_preview.rb +45 -0
  62. data/previews/forms/default.html.erb +6 -4
  63. data/previews/forms/radio_button_collection.html.erb +51 -0
  64. data/previews/forms/with_icons.html.erb +10 -0
  65. metadata +17 -2
@@ -0,0 +1,15 @@
1
+ module Anchor
2
+ class CopyToClipboardComponent < Component
3
+ attr_reader :label, :value
4
+
5
+ def initialize(
6
+ value: nil,
7
+ label: nil,
8
+ **kwargs
9
+ )
10
+ @label = label
11
+ @value = value
12
+ super
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller<HTMLDivElement> {
4
+ static targets = [ "initialIcon", "successIcon"];
5
+ static classes = [ "hidden" ]
6
+
7
+ declare readonly initialIconTarget: SVGElement;
8
+ declare readonly successIconTarget: SVGElement;
9
+ declare readonly notificationDelayValue: number;
10
+ declare readonly hiddenClass: string;
11
+ declare readonly textToCopyValue: string;
12
+ declare readonly hasTextToCopyValue: boolean;
13
+
14
+ static values = {
15
+ notificationDelay: { type: Number, default: 1500 },
16
+ textToCopy: String,
17
+ };
18
+
19
+ copy(): void {
20
+ if (this.hasTextToCopyValue) {
21
+ navigator.clipboard.writeText(this.textToCopyValue);
22
+ }
23
+ this.toggleIcons();
24
+
25
+ setTimeout(() => {
26
+ this.toggleIcons();
27
+ }, this.notificationDelayValue);
28
+ }
29
+
30
+ toggleIcons(): void {
31
+ this.initialIconTarget.classList.toggle(this.hiddenClass);
32
+ this.successIconTarget.classList.toggle(this.hiddenClass);
33
+ }
34
+ }
@@ -1,14 +1,13 @@
1
1
  <%= show_button if show_button? %>
2
2
 
3
- <%= tag.dialog(
3
+ <%= tag.dialog(**merge_options(wrapper_options, {
4
4
  aria: { labelledby: title_id },
5
- class: class_names("w-[36rem] rounded-lg p-0 bg-white shadow-lg backdrop:bg-grey-100/50", classes),
5
+ class: "w-[36rem] rounded-lg p-0 bg-white shadow-lg backdrop:bg-grey-100/50",
6
6
  data: {
7
7
  controller: "dialog",
8
8
  testid: title_id,
9
9
  },
10
- id: id,
11
- ) do %>
10
+ })) do %>
12
11
  <header class="p-6 flex gap-4 justify-between items-center">
13
12
  <%= render Anchor::TextComponent.new(
14
13
  variant: :heading_2xl,
@@ -1,6 +1,4 @@
1
1
  <%= anchor_svg(
2
2
  "icons/#{icon}",
3
- aria: { hidden: true },
4
- class: classes,
5
- data: data,
3
+ **merge_options(wrapper_options, { aria: { hidden: true } }),
6
4
  ) %>
@@ -3,7 +3,7 @@ module Anchor
3
3
  def initialize(icon:, **kwargs)
4
4
  @icon = icon
5
5
 
6
- super
6
+ super(**kwargs)
7
7
  end
8
8
 
9
9
  private
@@ -1,7 +1,6 @@
1
1
  <%= tag.span t(".label"), class: "sr-only" %>
2
2
 
3
- <%= anchor_icon(
3
+ <%= anchor_icon(**merge_options(wrapper_options,
4
+ class: class_names(wrapper_options[:class], "text-grey-100 animate-spin"),
4
5
  icon: "loading-indicator",
5
- classes: "text-grey-100 animate-spin",
6
- data: data,
7
- ) %>
6
+ )) %>
@@ -1,7 +1,4 @@
1
- <%= tag.div(
2
- class: classes,
3
- data: data,
4
- ) do %>
1
+ <%= tag.div(wrapper_options) do %>
5
2
  <%= tag.div(
6
3
  class: class_names(
7
4
  "w-fit max-w-full",
@@ -35,7 +35,7 @@ module Anchor
35
35
  ]
36
36
  @variant_aspect_ratio = VARIANT_ASPECT_RATIO_MAPPINGS[variant]
37
37
 
38
- super
38
+ super(**kwargs)
39
39
  end
40
40
 
41
41
  private
@@ -1,8 +1,6 @@
1
- <%= tag.div(
2
- class: class_names(
3
- "p-6",
4
- classes
5
- )
1
+ <%= tag.div(**wrapper_options.except(:class).merge({
2
+ class: class_names("p-6", wrapper_options[:class]),
3
+ }),
6
4
  ) do %>
7
5
  <%= content %>
8
6
  <% end %>
@@ -1,4 +1,6 @@
1
- <%= tag.div(class: class_names("border border-subdued", classes)) do %>
1
+ <%= tag.div(**merge_options(wrapper_options,
2
+ class: "border border-subdued",
3
+ )) do %>
2
4
  <%= header %>
3
5
 
4
6
  <% if active? %>
@@ -4,15 +4,11 @@
4
4
  ) do %>
5
5
  <%= trigger if trigger? %>
6
6
 
7
- <%= tag.div(
8
- class: class_names(
9
- "absolute -left-full -top-full m-0 bg-white rounded border border-subdued popover-open:block",
10
- classes
11
- ),
7
+ <%= tag.div(**merge_options(wrapper_options,
8
+ class: "absolute -left-full -top-full m-0 bg-white rounded border border-subdued popover-open:block",
12
9
  data: { action: "toggle->popover#positionPopover" },
13
- id:,
14
10
  popover: "auto",
15
- ) do %>
11
+ )) do %>
16
12
  <%= content %>
17
13
  <% end %>
18
14
  <% end %>
@@ -1,5 +1,5 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
- import { computePosition, autoPlacement, offset } from "@floating-ui/dom";
2
+ import { computePosition, autoPlacement, offset, autoUpdate } from "@floating-ui/dom";
3
3
 
4
4
  export default class extends Controller<HTMLDivElement> {
5
5
  static targets = ["button"];
@@ -7,20 +7,26 @@ export default class extends Controller<HTMLDivElement> {
7
7
  declare readonly buttonTarget: HTMLButtonElement;
8
8
 
9
9
  positionPopover = (event: { target: HTMLDivElement }): void => {
10
+ const trigger = this.buttonTarget
10
11
  const popover = event.target;
11
- computePosition(this.buttonTarget, popover, {
12
- placement: "bottom-start",
13
- middleware: [
14
- autoPlacement({
15
- allowedPlacements: ["bottom-start", "bottom-end", "top-start", "top-end"],
16
- }),
17
- offset(4)
18
- ],
19
- }).then(({ x, y }) => {
20
- Object.assign(popover.style, {
21
- left: `${x}px`,
22
- top: `${y}px`,
23
- });
24
- });
12
+
13
+ const updatePosition = () => {
14
+ computePosition(trigger, popover, {
15
+ placement: "bottom-start",
16
+ middleware: [
17
+ autoPlacement({
18
+ allowedPlacements: ["bottom-start", "bottom-end", "top-start", "top-end"],
19
+ }),
20
+ offset(4)
21
+ ],
22
+ }).then(({ x, y }) => {
23
+ Object.assign(popover.style, {
24
+ left: `${x}px`,
25
+ top: `${y}px`,
26
+ });
27
+ })
28
+ };
29
+
30
+ autoUpdate(trigger, popover, updatePosition);
25
31
  };
26
32
  }
@@ -1,8 +1,9 @@
1
1
  <%= tag.div(
2
2
  content,
3
- class: class_names(
4
- "prose",
5
- classes,
3
+ **wrapper_options.except(:class).merge(
4
+ class: class_names(
5
+ "prose",
6
+ wrapper_options[:class],
7
+ ),
6
8
  ),
7
- data: data
8
9
  ) %>
@@ -0,0 +1 @@
1
+ <%= tag.div content, class: "flex flex-col space-y-2 text-base" %>
@@ -0,0 +1,35 @@
1
+ module Anchor
2
+ class RadioButtonCollectionComponent < Component
3
+ attr_reader :attribute, :descriptions, :form_builder
4
+
5
+ def initialize(
6
+ form_builder:,
7
+ attribute:,
8
+ collection:,
9
+ value_method:,
10
+ text_method:,
11
+ **options
12
+ )
13
+ @form_builder = form_builder
14
+ @attribute = attribute
15
+ @collection = collection
16
+ @value_method = value_method
17
+ @text_method = text_method
18
+ @descriptions = options.delete(:descriptions)
19
+
20
+ super
21
+
22
+ def options
23
+ { class: RadioButtonComponent::INPUT_CLASSES }
24
+ end
25
+
26
+ def radio(radio:)
27
+ RadioButtonComponent.new(
28
+ radio:,
29
+ attribute:,
30
+ form_builder:
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ <div class="flex gap-1 items-start">
2
+ <%= radio.radio_button(radio_options) %>
3
+ <% if description.present? %>
4
+ <div class="flex flex-col gap-1">
5
+ <%= radio.label %>
6
+ <%= description_span %>
7
+ </div>
8
+ <% else %>
9
+ <%= tag.div radio.label, class: "self-center" %>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,105 @@
1
+ module Anchor
2
+ class RadioButtonComponent < Anchor::Component
3
+ INPUT_CLASSES = %w(
4
+ appearance-none
5
+ w-[18px]
6
+ m-[3px]
7
+ aspect-square
8
+ rounded-full
9
+ border-2
10
+ border-grey-60
11
+ hover:border-grey-80
12
+ checked:bg-gradient-radial
13
+ checked:from-blue-50
14
+ checked:from-[50%]
15
+ checked:to-transparent
16
+ checked:to-[54%]
17
+ checked:border-blue-50
18
+ checked:hover:from-blue-60
19
+ checked:hover:from-[50%]
20
+ checked:hover:to-transparent
21
+ checked:hover:to-[54%]
22
+ checked:hover:border-blue-60
23
+ disabled:border-grey-40
24
+ disabled:checked:border-grey-40
25
+ disabled:checked:from-grey-40
26
+ disabled:checked:from-[50%]
27
+ disabled:checked:to-transparent
28
+ disabled:checked:to-[54%]
29
+ disabled:checked:border-grey-40
30
+ aria-readonly:border-grey-40
31
+ aria-readonly:checked:border-grey-40
32
+ aria-readonly:checked:from-grey-40
33
+ aria-readonly:checked:from-[50%]
34
+ aria-readonly:checked:to-transparent
35
+ aria-readonly:checked:to-[54%]
36
+ aria-readonly:checked:border-grey-40
37
+ ).freeze
38
+
39
+ DESCRIPTION_CLASSES = %w(
40
+ text-secondary
41
+ text-sm
42
+ ).freeze
43
+
44
+ def initialize(radio:, attribute:, form_builder:)
45
+ @radio = radio
46
+ @attribute = attribute
47
+ @form_builder = form_builder
48
+
49
+ super
50
+ end
51
+
52
+ private
53
+
54
+ attr_reader :radio, :attribute, :form_builder
55
+
56
+ delegate :value, to: :radio
57
+
58
+ def aria_description
59
+ { describedby: description_id }
60
+ end
61
+
62
+ def description
63
+ if radio.object.is_a?(Hash)
64
+ radio.object[:description]
65
+ elsif radio.object.respond_to?(:description)
66
+ radio.object.description
67
+ elsif I18n.exists?(description_translation_scope)
68
+ human_attribute_description
69
+ else
70
+ false
71
+ end
72
+ end
73
+
74
+ def description_id
75
+ "#{attribute}-#{value}-description"
76
+ end
77
+
78
+ def description_span
79
+ tag.span description, id: description_id, class: DESCRIPTION_CLASSES
80
+ end
81
+
82
+ def description_text
83
+ human_attribute_description
84
+ end
85
+
86
+ def description_translation_scope
87
+ ["activerecord.values", i18n_model_key, description_id.gsub("-", ".")]
88
+ end
89
+
90
+ def human_attribute_description
91
+ I18n.t description_translation_scope.join(".")
92
+ end
93
+
94
+ def i18n_model_key
95
+ form_builder.object.class.model_name.i18n_key
96
+ end
97
+
98
+ def radio_options
99
+ {
100
+ aria: description.present? && aria_description,
101
+ data: { testid: "radio-#{attribute}-#{radio.value}".dasherize },
102
+ }
103
+ end
104
+ end
105
+ end
@@ -1,20 +1,19 @@
1
- <%= tag.div(
2
- class: "flex min-h-[100dvh] max-h-[100dvh] overflow-hidden",
1
+ <%= tag.div(**merge_options(wrapper_options, {
2
+ class: "flex min-h-[100dvh] max-h-[100dvh] overflow-hidden",
3
3
  data: {
4
4
  controller: "toggle",
5
5
  toggle_toggle_class: "side-nav-open",
6
6
  toggle_initial_label_value: t(".hide_nav"),
7
7
  toggle_intermediate_label_value: t(".show_nav"),
8
- },
9
- ) do %>
10
- <%= tag.header(
8
+ }
9
+ })) do %>
10
+ <%= tag.header(**merge_options(sidebar_options, {
11
11
  class: class_names(
12
12
  "flex flex-shrink-0 w-[15rem] relative",
13
13
  "[.side-nav-open_&]:w-[3.5rem] transition-[width] duration-500",
14
- classes
15
14
  ),
16
- data: { testid: "side-nav" }
17
- ) do %>
15
+ data: { testid: "side-nav" },
16
+ })) do %>
18
17
  <%= tag.div(
19
18
  class: class_names(
20
19
  "bg-teal text-white w-[15rem] flex flex-col divide-y divide-solid divide-white/10 duration-500",
@@ -58,7 +57,7 @@
58
57
  ),
59
58
  data: { toggle_target: "inertify" },
60
59
  ) do %>
61
- <%= render Anchor::ActionMenuComponent.new(classes: "py-4", menu_classes: "ml-4 w-[198px]") do |c| %>
60
+ <%= render Anchor::ActionMenuComponent.new(classes: "py-4", menu_options: { class: "ml-4 w-[198px]" }) do |c| %>
62
61
  <% c.with_show_button(
63
62
  classes: "text-lg text-white w-full hover:text-white hover:bg-white/[.05] px-6 justify-between rounded-none",
64
63
  full_width: true,
@@ -8,18 +8,21 @@ module Anchor
8
8
  apps_button_text:,
9
9
  nav_items:,
10
10
  employee:,
11
+ sidebar_options: {},
11
12
  **kwargs
12
13
  )
13
14
  @apps = apps
14
15
  @apps_button_text = apps_button_text
15
16
  @nav_items = nav_items
16
17
  @employee = employee
18
+ @sidebar_options = sidebar_options
17
19
 
18
20
  super
19
21
  end
20
22
 
21
23
  private
22
24
 
23
- attr_reader :apps, :apps_button_text, :employee, :nav_items
25
+ attr_reader :apps, :apps_button_text, :employee, :nav_items,
26
+ :sidebar_options
24
27
  end
25
28
  end
@@ -1,11 +1,13 @@
1
1
  <%= link_to(
2
2
  content,
3
3
  href,
4
- aria: {
5
- current: { page: active }
6
- },
7
- class: class_names(
8
- "inline-block px-5 py-4 text-sm font-semibold relative text-gray-600 hover:text-gray-900",
9
- "text-gray-900 after:block after:bg-blue-500 after:h-1 after:absolute after:-bottom-1 after:inset-x-0" => active,
4
+ **merge_options(wrapper_options,
5
+ aria: {
6
+ current: { page: active }
7
+ },
8
+ class: class_names(
9
+ "inline-block px-5 py-4 text-sm font-semibold relative text-gray-600 hover:text-gray-900",
10
+ "text-gray-900 after:block after:bg-blue-500 after:h-1 after:absolute after:-bottom-1 after:inset-x-0" => active,
11
+ )
10
12
  )
11
13
  ) %>
@@ -1,11 +1,11 @@
1
1
  module Anchor
2
2
  module TabBar
3
3
  class TabComponent < Component
4
- def initialize(href:, active: false)
4
+ def initialize(href:, active: false, **kwargs)
5
5
  @href = href
6
6
  @active = active
7
7
 
8
- super
8
+ super(**kwargs)
9
9
  end
10
10
 
11
11
  private
@@ -1,7 +1,6 @@
1
- <%= tag.nav(
2
- aria: { label: label },
3
- class: class_names("shadow-[inset_0_-1px] shadow-gray-400 pb-1", classes),
4
- ) do %>
1
+ <%= tag.nav(**merge_options(wrapper_options,
2
+ class: "shadow-[inset_0_-1px] shadow-gray-400 pb-1",
3
+ )) do %>
5
4
  <ul class="flex">
6
5
  <% tabs.each do |tab| %>
7
6
  <%= tag.li tab %>
@@ -3,9 +3,10 @@ module Anchor
3
3
  renders_many :tabs, TabBar::TabComponent
4
4
 
5
5
  def initialize(label:, **kwargs)
6
- @label = label
6
+ aria = kwargs.delete(:aria) || {}
7
+ aria[:label] = label
7
8
 
8
- super
9
+ super(**kwargs, aria:)
9
10
  end
10
11
 
11
12
  private
@@ -23,7 +23,7 @@ module Anchor
23
23
  @caption = caption
24
24
  @paginate = paginate
25
25
 
26
- super
26
+ super()
27
27
  end
28
28
 
29
29
  private
@@ -1,8 +1,3 @@
1
- <%= content_tag(
2
- tag,
3
- class: class_names(variant, classes),
4
- id: id,
5
- data: data
6
- ) do %>
1
+ <%= content_tag(tag, **wrapper_options) do %>
7
2
  <%= content %>
8
3
  <% end %>
@@ -3,7 +3,7 @@ module Anchor
3
3
  TAG_DEFAULT = :p
4
4
 
5
5
  VARIANT_DEFAULT = :body_base
6
- VARIANT_MAPPING = {
6
+ VARIANT_MAPPINGS = {
7
7
  body_lg: "text-lg",
8
8
  body_base: "text-base",
9
9
  body_sm: "text-sm",
@@ -16,25 +16,16 @@ module Anchor
16
16
  subheading_sm: "text-sm font-bold leading-4 uppercase",
17
17
  subheading_xs: "text-xs font-bold leading-4 uppercase",
18
18
  }.freeze
19
- VARIANT_OPTIONS = VARIANT_MAPPING.keys
19
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
20
20
 
21
- def initialize(
22
- variant: VARIANT_DEFAULT,
23
- tag: TAG_DEFAULT,
24
- **kwargs
25
- )
26
- @variant = VARIANT_MAPPING[
27
- fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
28
- ]
21
+ def initialize(tag: TAG_DEFAULT, **kwargs)
29
22
  @tag = tag
30
- @id = id
31
-
32
- super
23
+ super(**kwargs)
33
24
  end
34
25
 
35
26
  private
36
27
 
37
- attr_reader :variant, :tag, :id
28
+ attr_reader :tag
38
29
 
39
30
  def render?
40
31
  content.present?
@@ -1 +1,19 @@
1
- <%= content %>
1
+ <% if starting_icon? || ending_icon? %>
2
+ <%= tag.div class: "relative" do %>
3
+ <%= content %>
4
+ <% if starting_icon? %>
5
+ <%= anchor_icon(
6
+ icon: starting_icon,
7
+ classes: class_names(ICON_CLASSES, "left-4")
8
+ ) %>
9
+ <% end %>
10
+ <% if ending_icon? %>
11
+ <%= anchor_icon(
12
+ icon: ending_icon,
13
+ classes: class_names(ICON_CLASSES, "right-4")
14
+ ) %>
15
+ <% end %>
16
+ <% end %>
17
+ <% else %>
18
+ <%= content %>
19
+ <% end %>
@@ -1,30 +1,49 @@
1
1
  module Anchor
2
2
  class TextFieldComponent < Component
3
- INPUT_CLASSES = %w(
4
- block
5
- border
6
- form-input
7
- min-h-[40px]
8
- mt-1
9
- resize-none
10
- rounded
11
- text-base
12
- w-full
13
- [&[readonly]]:bg-grey-10
14
- [&[readonly]]:border-grey-40
15
- [&[readonly]]:shadow-none
3
+ ICON_CLASSES = %w(
4
+ absolute
5
+ h-5
6
+ w-5
7
+ top-1/2
8
+ -translate-y-1/2
9
+ text-secondary
10
+ pointer-events-none
16
11
  ).freeze
17
12
 
18
- def initialize(form_builder:, attribute:, **options)
13
+ def initialize(
14
+ form_builder:,
15
+ attribute:,
16
+ starting_icon: nil,
17
+ ending_icon: nil
18
+ )
19
19
  @form_builder = form_builder
20
20
  @attribute = attribute
21
- @options = options
21
+ @starting_icon = starting_icon
22
+ @ending_icon = ending_icon
22
23
 
23
24
  super
24
25
  end
25
26
 
27
+ attr_reader :starting_icon, :ending_icon
28
+
26
29
  def options
27
- { class: INPUT_CLASSES }
30
+ {
31
+ class: class_names(
32
+ TailwindConstants::INPUT,
33
+ "pl-11" => starting_icon?,
34
+ "pr-11" => ending_icon?
35
+ ),
36
+ }
37
+ end
38
+
39
+ private
40
+
41
+ def starting_icon?
42
+ starting_icon.present?
43
+ end
44
+
45
+ def ending_icon?
46
+ ending_icon.present?
28
47
  end
29
48
  end
30
49
  end
@@ -1,12 +1,9 @@
1
1
  <%= tag.div(
2
- class: class_names(
3
- "toast",
4
- variant,
5
- ),
6
- data: { controller: "toast" },
7
- id: id,
8
- popover: "manual",
9
- role:,
2
+ **merge_options(wrapper_options, {
3
+ class: "toast",
4
+ data: { controller: "toast", testid: test_id },
5
+ popover: "manual",
6
+ })
10
7
  ) do %>
11
8
  <% if icon? %>
12
9
  <%= icon %>