loco_motion-rails 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -1
  3. data/app/components/daisy/actions/button_component.rb +109 -8
  4. data/app/components/daisy/actions/dropdown_component.html.haml +5 -5
  5. data/app/components/daisy/actions/dropdown_component.rb +94 -25
  6. data/app/components/daisy/actions/modal_component.html.haml +3 -2
  7. data/app/components/daisy/actions/modal_component.rb +94 -45
  8. data/app/components/daisy/actions/swap_component.rb +114 -5
  9. data/app/components/daisy/actions/theme_controller_component.html.haml +1 -1
  10. data/app/components/daisy/actions/theme_controller_component.rb +36 -1
  11. data/app/components/daisy/data_display/accordion_component.rb +79 -3
  12. data/app/components/daisy/data_display/avatar_component.rb +36 -16
  13. data/app/components/daisy/data_display/badge_component.rb +35 -5
  14. data/app/components/daisy/data_display/card_component.html.haml +5 -13
  15. data/app/components/daisy/data_display/card_component.rb +74 -39
  16. data/app/components/daisy/data_display/carousel_component.rb +38 -0
  17. data/app/components/daisy/data_display/chat_component.rb +40 -10
  18. data/app/components/daisy/data_display/collapse_component.rb +58 -1
  19. data/app/components/daisy/data_display/countdown_component.rb +49 -0
  20. data/app/components/daisy/data_display/diff_component.rb +37 -0
  21. data/app/components/daisy/data_display/figure_component.rb +49 -0
  22. data/app/components/daisy/data_display/kbd_component.rb +50 -2
  23. data/app/components/daisy/data_display/stat_component.rb +64 -6
  24. data/app/components/daisy/data_display/table_component.rb +99 -34
  25. data/app/components/daisy/data_display/timeline_component.rb +45 -0
  26. data/app/components/daisy/data_display/timeline_event_component.rb +39 -1
  27. data/app/components/daisy/feedback/alert_component.rb +46 -1
  28. data/app/components/daisy/feedback/loading_component.rb +39 -0
  29. data/app/components/daisy/feedback/progress_component.rb +39 -1
  30. data/app/components/daisy/feedback/radial_progress_component.rb +44 -1
  31. data/app/components/daisy/feedback/skeleton_component.rb +44 -0
  32. data/app/components/daisy/feedback/toast_component.rb +36 -0
  33. data/app/components/daisy/feedback/tooltip_component.rb +46 -10
  34. data/app/components/daisy/layout/artboard_component.rb +48 -0
  35. data/app/components/daisy/layout/divider_component.rb +50 -10
  36. data/app/components/daisy/layout/drawer_component.rb +62 -17
  37. data/app/components/daisy/layout/footer_component.rb +51 -11
  38. data/app/components/daisy/layout/hero_component.rb +67 -5
  39. data/app/components/daisy/layout/indicator_component.rb +55 -8
  40. data/app/components/daisy/layout/join_component.rb +71 -0
  41. data/app/components/daisy/layout/stack_component.rb +59 -0
  42. data/app/components/daisy/mockup/browser_component.rb +78 -0
  43. data/app/components/daisy/mockup/code_component.rb +144 -0
  44. data/app/components/daisy/mockup/device_component.rb +81 -0
  45. data/app/components/daisy/mockup/frame_component.rb +62 -0
  46. data/app/components/daisy/navigation/bottom_nav_component.rb +81 -2
  47. data/app/components/daisy/navigation/breadcrumbs_component.rb +40 -3
  48. data/app/components/daisy/navigation/link_component.rb +31 -6
  49. data/app/components/daisy/navigation/menu_component.rb +52 -20
  50. data/app/components/daisy/navigation/navbar_component.html.haml +1 -1
  51. data/app/components/daisy/navigation/navbar_component.rb +63 -2
  52. data/app/components/daisy/navigation/steps_component.rb +76 -0
  53. data/app/components/daisy/navigation/tabs_component.rb +110 -7
  54. data/app/components/hero/icon_component.rb +40 -0
  55. data/lib/daisy.rb +5 -0
  56. data/lib/loco_motion/helpers.rb +7 -0
  57. data/lib/loco_motion/version.rb +5 -0
  58. metadata +25 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a020015ecb6d9ee0a705d25830f1951b00c60be6ee4046939b878d3a720bfbda
4
- data.tar.gz: c9bc31844a168d4427423912be65f354387aba43f2c6d6711998cee947e2311d
3
+ metadata.gz: 7d4d1891389f5b0a860f930a6d4e6dbb9053b6a77e349cd0f43b9de22740482b
4
+ data.tar.gz: 2f99a232731636bfaeca166092c0fb4ca51ddae6300e420951d0c0791997e1d7
5
5
  SHA512:
6
- metadata.gz: f88796625ae5f6f7c7690563d0a687ed2d8932924d305cb12da0985fdcfdb00f629e6a25618afe5498ec6932af0e8c45b1cc3008a9f68d28940563f9f41f1fb0
7
- data.tar.gz: 5f09dacd5dab790708428cb3e853e40fa59a5d20da58b4d11f9e99cca76bf940b1d3d311e55e8396b140acb2271eddb3df202175164ba59306acc041427d65cd
6
+ metadata.gz: ad963fe072e997ad83879704d8a5d9285f2e964371ee9c7418dfe7377db7950b09f43e3ff8555fad25aee3d141b7b6927ba5e70f965684d749057aaa1d6223c8
7
+ data.tar.gz: 367ecb0bf6b3b9d3ff77e6f596b12322fe1b1026bcecc619032c70e59ed94d344f3b18907e8dc8caf63b4d95343e351a808254b50da0db14c8a98b8282cb5a18
data/README.md CHANGED
@@ -33,11 +33,13 @@ your solution is aligned with our goals.
33
33
  - [About](#about)
34
34
  - [Getting Started](#getting-started)
35
35
  - [Installing / Setting up Rails](#installing--setting-up-rails)
36
+ - [Using UUIDs by Default](#using-uuids-by-default)
36
37
  - [Install HAML (Optional)](#install-haml-optional)
37
38
  - [Install DaisyUI (Optional)](#install-daisyui-optional)
38
39
  - [Try Out Your Application](#try-out-your-application)
39
40
  - [Debugging](#debugging)
40
41
  - [Testing](#testing)
42
+ - [Services / Service Objects](#services--service-objects)
41
43
  - [Authentication](#authentication)
42
44
  - [Web Console](#web-console)
43
45
  - [BetterErrors (Optional)](#bettererrors-optional)
@@ -203,6 +205,20 @@ Congratulations!
203
205
  You can now visit [http://localhost:3000](http://localhost:3000) in your web
204
206
  browser and see your running Rails application!
205
207
 
208
+ ### Using UUIDs by Default
209
+
210
+ We believe strongly in migrating all of your primary keys to UUIDs to increase
211
+ security as well as avoiding potential scaling issues in the future.
212
+
213
+ To enable this by default, create the following file:
214
+
215
+ ```ruby
216
+ # config/initializers/generators.rb
217
+ Rails.application.config.generators do |generator|
218
+ generator.orm :active_record, primary_key_type: :uuid
219
+ end
220
+ ```
221
+
206
222
  ### Install HAML (Optional)
207
223
 
208
224
  While you can use the default ERB templating system that comes with Rails, we
@@ -479,6 +495,28 @@ We'll have some guides and examples for this coming soon!
479
495
  > We plan to have a writeup soon (an ADR specifically) on exactly why we made
480
496
  > the switch.
481
497
 
498
+ ## Services / Service Objects
499
+
500
+ It is best practice to separate your logic into Service Objects rather than
501
+ shoving all of it into your Controllers and Models.
502
+
503
+ One solution we really like is
504
+ [ActiveInteraction](https://github.com/AaronLasseigne/active_interaction).
505
+
506
+ It is very stable, has wonderful documentation, and gives you a clean way to
507
+ build your service objects with support for things like composed interactions
508
+ and even ActiveModel validations.
509
+
510
+ Add `gem 'active_interaction', '~> 5.3'` to your `Gemfile` and create a new
511
+ class called `ApplicationInteraction` if you want to give it a try!
512
+
513
+ ```
514
+ # app/interactions/application_interaction.rb
515
+ class ApplicationInteraction < ActiveInteraction::Base
516
+ # Your interactions will inherit from this class!
517
+ end
518
+ ```
519
+
482
520
  ## Authentication
483
521
 
484
522
  There are a **lot** of different ways to handle user authentication in Ruby on
@@ -884,7 +922,7 @@ the GitHub Discussions feature and let us know!
884
922
  - [ ] Basic versions of DaisyUI Data Input
885
923
  - [ ] Basic versions of DaisyUI Layout
886
924
  - [ ] Basic versions of DaisyUI Mockup
887
- - [ ] ~~Get YARD docs rendering with (better) Markdown~~ _**Working for now**_
925
+ - [x] ~~Get YARD docs rendering with (better) Markdown~~ _**Working for now**_
888
926
  - [x] Extract relevant pieces into a yard-loco_motion plugin
889
927
  - [x] Publish Gem
890
928
  - [x] Publish NPM package
@@ -902,6 +940,12 @@ the GitHub Discussions feature and let us know!
902
940
  - [ ] Build some have docs / guides / examples for using playwright-ruby-client
903
941
  - [x] See if we can build a `Tippable` concern that relevant components can
904
942
  include to automatically add the tooltip param and classes where possible
943
+ - [x] Rename `tail` methods to `end` since we use that in other places
944
+ - [x] Update CardComponent Figure to be a proper class like other components
945
+ - [x] Create a GitHub pull request template to standardize PR submissions
946
+ - [ ] See if we can update the Join component to auto-add the `join-item` CSS
947
+ under certain conditions
948
+ - [ ] Add title and description content_for blocks to all examples for SEO purposes
905
949
 
906
950
  [1]: https://loco-motion.profoundry.us/
907
951
  [2]: https://loco-motion-demo-staging.profoundry.us/
@@ -1,12 +1,97 @@
1
- # Here are the Button docs!
1
+ #
2
+ # The Button component can be used to render HTML `<button>` or `<a>` elements
3
+ # that are styled to look like a clickable element.
4
+ #
5
+ # Note that we do **not** use component parts for the icons since we're calling
6
+ # `heroicon_tag` within the component. But we **do** provide custom CSS & HTML
7
+ # options to allow overriding / customization.
8
+ #
9
+ # Includes the {LocoMotion::Concerns::TippableComponent} module to enable easy
10
+ # tooltip addition.
11
+ #
12
+ # @loco_example Basic Usage
13
+ # = daisy_button("Click Me")
14
+ #
15
+ # = daisy_button do
16
+ # Click Me Too
17
+ #
18
+ # = daisy_button(icon: "heart", tip: "Love")
19
+ #
20
+ # = daisy_button(title: "Button with Two Icons", left_icon: "heart", right_icon: "plus")
21
+ #
2
22
  class Daisy::Actions::ButtonComponent < LocoMotion::BaseComponent
3
23
  prepend LocoMotion::Concerns::TippableComponent
4
24
 
5
- set_component_name :btn
6
-
7
- def initialize(*args, **kws, &block)
25
+ #
26
+ # Instantiate a new Button component.
27
+ #
28
+ # @param title [String] The title of the button. Defaults to "Submit" if none
29
+ # of title, left icon, or right icon is provided. Will be considered the
30
+ # `action` parameter if **both** the title and a block are provided.
31
+ #
32
+ # @param action [String] The Stimulus action that should fire when the button
33
+ # is clicked.
34
+ #
35
+ # @param kws [Hash] The keyword arguments for the component.
36
+ #
37
+ # @option kws title [String] The title of the button. You can also
38
+ # pass the title, icons, or any other HTML content as a block.
39
+ #
40
+ # @option kws action [String] The Stimulus action that should fire
41
+ # when the button is clicked.
42
+ #
43
+ # > **Note:** _You should use either the `action` or the `href` option, but
44
+ # not both._
45
+ #
46
+ # @option kws href [String] A path or URL to which the user will be
47
+ # directed when the button is clicked. Forces the Button to use an `<a>`
48
+ # tag.
49
+ #
50
+ # > **Note:** _You should use either the `action` or the `href` option, but
51
+ # not both._
52
+ #
53
+ # @option kws target [String] The HTML `target` of for the `<a>` tag
54
+ # (`_blank`, `_parent`, or a specific tab / window / iframe, etc).
55
+ #
56
+ # @option kws icon [String] The name of Hero icon to render inside
57
+ # the button. This is an alias of `left_icon`.
58
+ #
59
+ # @option kws icon_css [String] The CSS classes to apply to the icon.
60
+ # This is an alias of `left_icon_css`.
61
+ #
62
+ # @option kws icon_html [Hash] Additional HTML attributes to apply to
63
+ # the icon. This is an alias of `left_icon_html`.
64
+ #
65
+ # @option kws left_icon [String] The name of Hero icon to render inside
66
+ # the button to the left of the text.
67
+ #
68
+ # @option kws left_icon_css [String] The CSS classes to apply to the left
69
+ # icon.
70
+ #
71
+ # @option kws left_icon_html [Hash] Additional HTML attributes to apply to
72
+ # the left icon.
73
+ #
74
+ # @option kws right_icon [String] The name of Hero icon to render inside
75
+ # the button to the right of the text.
76
+ #
77
+ # @option kws right_icon_css [String] The CSS classes to apply to the right
78
+ # icon.
79
+ #
80
+ # @option kws right_icon_html [Hash] Additional HTML attributes to apply to
81
+ # the right icon.
82
+ #
83
+ def initialize(title = nil, action = nil, **kws, &block)
8
84
  super
9
85
 
86
+ # If both a title and a block are provided, assume the title is the action
87
+ action = title if title && block_given?
88
+
89
+ # Force the title to be nil if a block is given so we don't accidentally
90
+ # render two titles
91
+ title = nil if block_given?
92
+
93
+ @action = config_option(:action, action)
94
+
10
95
  @href = config_option(:href)
11
96
  @target = config_option(:target)
12
97
 
@@ -23,15 +108,21 @@ class Daisy::Actions::ButtonComponent < LocoMotion::BaseComponent
23
108
  @right_icon_html = config_option(:right_icon_html, @icon_html)
24
109
 
25
110
  default_title = @left_icon || @right_icon ? nil : "Submit"
26
- @simple_title = config_option(:title, args[0] || default_title)
111
+ @simple_title = config_option(:title, title || default_title)
27
112
  end
28
113
 
114
+ #
115
+ # Calls the {setup_component} method before rendering the component.
116
+ #
29
117
  def before_render
30
118
  setup_component
31
119
  end
32
120
 
33
- private
34
-
121
+ #
122
+ # Sets the tagname to `<a>` if an `href` is provided, otherwise sets it to
123
+ # `<button>`. Adds the `btn` CSS class to the component. Also adds
124
+ # `items-center` and `gap-2` CSS classes if an icon is present.
125
+ #
35
126
  def setup_component
36
127
  if @href
37
128
  set_tag_name(:component, :a)
@@ -42,13 +133,23 @@ class Daisy::Actions::ButtonComponent < LocoMotion::BaseComponent
42
133
 
43
134
  add_css(:component, "btn")
44
135
 
45
- add_css(:component, "items-center gap-2") if @icon
136
+ if @icon || @left_icon || @right_icon
137
+ add_css(:component, "[:where(&)]:items-center [:where(&)]:gap-2")
138
+ end
139
+
140
+ add_html(:component, { "data-action": @action }) if @action
46
141
  end
47
142
 
143
+ #
144
+ # Returns the HTML attributes for the left icon.
145
+ #
48
146
  def left_icon_html
49
147
  { class: @left_icon_css }.merge(@left_icon_html)
50
148
  end
51
149
 
150
+ #
151
+ # Returns the HTML attributes for the right icon.
152
+ #
52
153
  def right_icon_html
53
154
  { class: @right_icon_css }.merge(@right_icon_html)
54
155
  end
@@ -1,13 +1,13 @@
1
1
  = part(:component) do
2
- - if title?
3
- = title
2
+ - if activator?
3
+ = activator
4
4
  - else
5
- = part(:title) do
6
- = @simple_title
5
+ = button
7
6
 
8
7
  - if items.any?
9
8
  = part(:menu) do
10
9
  - items.each do |item|
11
- = item
10
+ = part(:menu_item) do
11
+ = item
12
12
  - else
13
13
  = content
@@ -1,48 +1,117 @@
1
- # Here are the Button docs!
1
+ #
2
+ # The Dropdown component shows a Button, or any other component you wish, with a
3
+ # hovering menu that opens on click (or hover). It provides a flexible way to
4
+ # create dropdown menus with customizable triggers and content.
5
+ #
6
+ # Note that the dropdown uses slots for both the activator and menu items,
7
+ # allowing for maximum flexibility in how the dropdown is triggered and what
8
+ # content it displays.
9
+ #
10
+ # @part menu The default / styled menu rendered by the dropdown. Contains all
11
+ # menu items and provides the dropdown's positioning and animation.
12
+ # @part menu_item The styles for every item in the dropdown. Provides consistent
13
+ # spacing and hover states.
14
+ #
15
+ # @slot button The button that triggers the dropdown. This is the default trigger
16
+ # and is styled automatically.
17
+ # @slot activator A custom (i.e. non-button) activator for the dropdown.
18
+ # Automatically adds the `role="button"` and `tabindex="0"` attributes.
19
+ # @slot item+ The items in the dropdown. Each item will be styled consistently
20
+ # with proper spacing and hover states.
21
+ #
22
+ # @loco_example Basic Usage
23
+ # = daisy_dropdown do |dropdown|
24
+ # - dropdown.with_button do
25
+ # Click me!
26
+ # - dropdown.with_item do
27
+ # Item 1
28
+ # - dropdown.with_item do
29
+ # Item 2
30
+ #
31
+ # @loco_example Custom Activator
32
+ # = daisy_dropdown do |dropdown|
33
+ # - dropdown.with_activator do
34
+ # = heroicon_tag "bars-3", css: "size-6"
35
+ # - dropdown.with_item do
36
+ # Menu Item 1
37
+ # - dropdown.with_item do
38
+ # Menu Item 2
39
+ #
40
+ # @loco_example Complex Items
41
+ # = daisy_dropdown do |dropdown|
42
+ # - dropdown.with_button do
43
+ # User Settings
44
+ # - dropdown.with_item do
45
+ # .flex.gap-2.items-center
46
+ # = heroicon_tag "user-circle"
47
+ # Profile
48
+ # - dropdown.with_item do
49
+ # .flex.gap-2.items-center
50
+ # = heroicon_tag "cog-6-tooth"
51
+ # Settings
52
+ # - dropdown.with_item do
53
+ # .flex.gap-2.items-center.text-error
54
+ # = heroicon_tag "arrow-right-on-rectangle"
55
+ # Logout
56
+ #
2
57
  class Daisy::Actions::DropdownComponent < LocoMotion::BaseComponent
3
58
 
4
- TitleComponent = LocoMotion::BasicComponent.build do
5
- def before_render
6
- add_html(:component, { role: "button", tabindex: 0 })
7
- end
8
- end
9
-
10
- ItemComponent = LocoMotion::BasicComponent.build do
11
- def before_render
12
- set_tag_name(:component, :li)
13
- end
14
- end
59
+ include ViewComponent::SlotableDefault
15
60
 
16
- define_parts :title, :menu
61
+ define_parts :menu, :menu_item
17
62
 
18
- renders_one :title, TitleComponent
19
- renders_many :items, ItemComponent
63
+ renders_one :activator, LocoMotion::BasicComponent.build(html: { role: "button", tabindex: 0 })
64
+ renders_one :button, Daisy::Actions::ButtonComponent
65
+ renders_many :items
20
66
 
21
- def initialize(*args, **kws, &block)
67
+ #
68
+ # Creates a new instance of the DropdownComponent.
69
+ #
70
+ # @param title [String] The title of the dropdown. Will be used as the button
71
+ # text if no custom button or activator is provided.
72
+ #
73
+ # @param kws [Hash] The keyword arguments for the component.
74
+ #
75
+ # @option kws title [String] The title of the dropdown. You can also pass this
76
+ # as the first argument.
77
+ #
78
+ def initialize(title = nil, **kws, &block)
22
79
  super
23
80
 
24
- @simple_title = config_option(:title, "Submit")
81
+ @simple_title = config_option(:title, title)
25
82
  end
26
83
 
84
+ #
85
+ # Adds the relevant Daisy classes to the component.
86
+ #
27
87
  def before_render
28
88
  setup_component
29
- setup_title
30
89
  setup_menu
31
90
  end
32
91
 
92
+ #
93
+ # Add the `dropdown` CSS class to the component.
94
+ #
33
95
  def setup_component
34
96
  add_css(:component, "dropdown")
35
97
  end
36
98
 
37
- def setup_title
38
- set_tag_name(:title, :div)
39
- add_css(:title, "btn")
40
- add_html(:title, { role: "button", tabindex: 0 })
41
- end
42
-
99
+ #
100
+ # Make the menu a `<ul> / <li>` element and add the relevant Daisy classes.
101
+ #
43
102
  def setup_menu
103
+ # Setup menu itself
44
104
  set_tag_name(:menu, :ul)
45
105
  add_css(:menu, "dropdown-content menu bg-base-100 rounded-box shadow w-52 p-2 z-[1]")
46
- add_html(:menu, { role: "menu", tabindex: 0 })
106
+
107
+ # Setup menu items
108
+ set_tag_name(:menu_item, :li)
109
+ end
110
+
111
+ #
112
+ # Provides a default button if no button or custom activator is provided.
113
+ #
114
+ def default_button
115
+ Daisy::Actions::ButtonComponent.new(title: @simple_title)
47
116
  end
48
117
  end
@@ -1,6 +1,7 @@
1
1
  - if activator?
2
- = part(:activator) do
3
- = activator
2
+ = activator
3
+ - else
4
+ = button
4
5
 
5
6
  = part(:component) do
6
7
  = part(:box) do
@@ -1,51 +1,81 @@
1
1
  #
2
2
  # The Modal component renders a modal dialog that can be opened and closed. It
3
- # includes a backdrop, a close icon, a title, and actions.
3
+ # provides a structured way to display content that requires user attention or
4
+ # interaction, such as forms, confirmations, or detailed information.
4
5
  #
5
- # @part dialog The main `<dialog>` container.
6
- # @part box The container for the modal content.
7
- # @part activator The container for the activator which opens the modal.
8
- # @part close_icon_wrapper The container for the close icon.
9
- # @part close_icon The default close icon.
10
- # @part backdrop The backdrop that covers the rest of the screen.
11
- # @part title Container for the default title for the modal.
12
- # @part actions Container for all modal actions.
13
- # @part start_actions Container for the left (start) aligned actions for the modal.
14
- # @part end_actions The end actions for the modal.
6
+ # Note that the modal uses the HTML `<dialog>` element and its native methods
7
+ # (`showModal()` and `close()`). This provides better accessibility and keyboard
8
+ # navigation compared to div-based modals.
15
9
  #
16
- # @slot activator The activator for the modal.
17
- # @slot close_icon A custom close icon for the modal.
18
- # @slot title A custom title for the modal.
19
- # @slot start_actions Left (or start) aligned actions for the modal.
20
- # @slot end_actions Right (or end) aligned actions for the modal.
10
+ # @part dialog The main `<dialog>` container that wraps the modal content.
11
+ # @part box The container for the modal content, providing padding and layout.
12
+ # @part close_icon_wrapper The container for the close icon, positioned in the
13
+ # top-right corner.
14
+ # @part close_icon The default close icon button.
15
+ # @part backdrop The semi-transparent backdrop that covers and dims the main
16
+ # content.
17
+ # @part title Container for the modal title, styled for prominence.
18
+ # @part actions Container for all modal action buttons.
19
+ # @part start_actions Container for left (start) aligned action buttons.
20
+ # @part end_actions Container for right (end) aligned action buttons.
21
21
  #
22
- # @loco_example Basic Usage
22
+ # @slot activator A custom element that opens the modal. Automatically adds
23
+ # `role="button"` and `tabindex="0"` attributes for accessibility.
24
+ # @slot button The button that opens the modal. Defaults to a standard Daisy
25
+ # button with the modal's title.
26
+ # @slot close_icon A custom close button to replace the default 'X' icon.
27
+ # @slot title Custom title content, replacing the default text title.
28
+ # @slot start_actions Left (start) aligned buttons, typically for secondary actions
29
+ # like "Cancel".
30
+ # @slot end_actions Right (end) aligned buttons, typically for primary actions
31
+ # like "Submit" or "Save".
32
+ #
33
+ # @loco_example Basic Modal
23
34
  # = daisy_modal(title: "Simple Modal") do |modal|
35
+ # - modal.with_button(css: "btn-primary") { "Open Modal" }
36
+ # %p This is a basic modal with some content.
37
+ # - modal.with_end_actions do
38
+ # %form{ method: :dialog }
39
+ # = daisy_button { "Close" }
40
+ #
41
+ # @loco_example Form Modal
42
+ # = daisy_modal(title: "Edit Profile") do |modal|
43
+ # - modal.with_button { "Edit Profile" }
44
+ # = form_with(model: @user) do |f|
45
+ # .space-y-4
46
+ # = f.text_field :name, class: "input input-bordered w-full"
47
+ # = f.email_field :email, class: "input input-bordered w-full"
48
+ # - modal.with_start_actions do
49
+ # %form{ method: :dialog }
50
+ # = daisy_button { "Cancel" }
51
+ # - modal.with_end_actions do
52
+ # = daisy_button(css: "btn-primary", type: "submit") { "Save Changes" }
53
+ #
54
+ # @loco_example Custom Activator
55
+ # = daisy_modal(title: "User Details") do |modal|
24
56
  # - modal.with_activator do
25
- # - onclick = "document.getElementById('#{modal.dialog_id}').showModal()"
26
- # = daisy_button(css: 'btn-primary', html: { onclick: onclick }) do
27
- # Open Modal
57
+ # .flex.items-center.gap-2.cursor-pointer
58
+ # = heroicon_tag "user-circle"
59
+ # %span View Details
28
60
  #
29
- # Here is some really long modal content that should go well past the
30
- # spot where the close icon appears...
61
+ # %dl.space-y-2
62
+ # %dt Name
63
+ # %dd John Doe
64
+ # %dt Email
65
+ # %dd john@example.com
31
66
  #
32
- # - modal.with_end_actions(css: "flex flex-row items-center gap-2") do
67
+ # - modal.with_end_actions do
33
68
  # %form{ method: :dialog }
34
- # = daisy_button do
35
- # Cancel
36
- # %form{ action: "", method: :get }
37
- # %input{ type: "hidden", name: "submitted", value: "true" }
38
- # = daisy_button(css: "btn-primary") do
39
- # Submit
69
+ # = daisy_button { "Close" }
40
70
  #
41
71
  class Daisy::Actions::ModalComponent < LocoMotion::BaseComponent
42
72
  set_component_name :modal
43
73
 
44
- define_parts :dialog, :box, :actions,
45
- :activator, :close_icon_wrapper, :close_icon,
74
+ define_parts :dialog, :box, :actions, :close_icon_wrapper, :close_icon,
46
75
  :backdrop, :title, :start_actions, :end_actions
47
76
 
48
- renders_one :activator
77
+ renders_one :activator, LocoMotion::BasicComponent.build(html: { role: "button", tabindex: 0 })
78
+ renders_one :button, Daisy::Actions::ButtonComponent
49
79
  renders_one :close_icon
50
80
  renders_one :title
51
81
  renders_one :start_actions
@@ -63,33 +93,35 @@ class Daisy::Actions::ModalComponent < LocoMotion::BaseComponent
63
93
  attr_reader :simple_title
64
94
 
65
95
  #
66
- # Instantiate a new Modal component. All options are expected to be passed as
67
- # keyword arguments.
96
+ # Creates a new instance of the ModalComponent.
97
+ #
98
+ # @param title [String] The title of the modal. Used in both the modal header
99
+ # and the default trigger button.
68
100
  #
69
- # @param args [Array] Currently unused and passed through to the
70
- # BaseComponent.
71
101
  # @param kws [Hash] The keyword arguments for the component.
72
102
  #
73
- # @option kws dialog_id [String] A specific ID you would like the dialog to
74
- # use. Auto-generates a random ID using `SecureRandom.uuid` if not provided.
75
- # @option kws closable [Boolean] Whether or not the modal should allow
76
- # closing.
77
- # @option kws title [String] A simple title that you would like the
78
- # component to render above the main content of the modal (see
79
- # {simple_title}).
103
+ # @option kws title [String] The title of the modal. You can also pass this as
104
+ # the first argument.
105
+ #
106
+ # @option kws closable [Boolean] If true (default), shows a close icon in the
107
+ # top-right corner.
80
108
  #
81
- def initialize(*args, **kws, &block)
109
+ # @option kws dialog_id [String] A custom ID for the dialog element. If not
110
+ # provided, a unique ID will be generated.
111
+ #
112
+ def initialize(title = nil, **kws, &block)
82
113
  super
83
114
 
84
115
  @dialog_id = config_option(:dialog_id, SecureRandom.uuid)
85
116
  @closable = config_option(:closable, true)
86
- @simple_title = config_option(:title)
117
+ @simple_title = config_option(:title, title)
87
118
  end
88
119
 
89
120
  #
90
121
  # Sets up the component with various CSS classes and HTML attributes.
91
122
  #
92
123
  def before_render
124
+ setup_activator_or_button
93
125
  setup_component
94
126
  setup_backdrop
95
127
  setup_box
@@ -100,6 +132,18 @@ class Daisy::Actions::ModalComponent < LocoMotion::BaseComponent
100
132
 
101
133
  private
102
134
 
135
+ def setup_activator_or_button
136
+ onclick = "document.getElementById('#{dialog_id}').showModal()"
137
+
138
+ element = if activator?
139
+ activator
140
+ else
141
+ button || default_button
142
+ end
143
+
144
+ element.add_html(:component, { onclick: onclick })
145
+ end
146
+
103
147
  def setup_component
104
148
  set_tag_name(:component, :dialog)
105
149
  add_html(:component, id: dialog_id)
@@ -131,4 +175,9 @@ class Daisy::Actions::ModalComponent < LocoMotion::BaseComponent
131
175
  def setup_actions
132
176
  add_css(:actions, "mt-2 flex flex-row items-center justify-between")
133
177
  end
178
+
179
+ # Provide a default button if no button is supplied.
180
+ def default_button
181
+ with_button(simple_title)
182
+ end
134
183
  end