ultimate_turbo_modal 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '079b28d06be34aba1359de1c34042f9f2ef131421f37af1a7f3c8619e156e68b'
4
- data.tar.gz: 551d973abd6d0c51f0ad1e01e87ead35697030e4fcce5f62ac591dc09683a284
3
+ metadata.gz: 8d0ae24b41414e4d9419260f7de74789e488a6e0d0a961749e10c3f6ca70ecaf
4
+ data.tar.gz: ef606673fe357af79d46760a2c59252da8052fcd6fdbce56fdf264cd015fe460
5
5
  SHA512:
6
- metadata.gz: 611ce181e6be8833b0b0c094bf2a776edf85dd050ca93fcf866401c1a3e05c0f81c2be519ba2ea8e98efd3ce3b28d7ee0fb2bb6512938a0142149b80fe9ac05e
7
- data.tar.gz: 77159bc4ce94a2bf2cc1abddbc068efe4b5bb3c821494d937262411961c2cfdc0df036fb83ed7dd56f8d25c1743d85d585e572a085f7374d8eecc050ec2bd11a
6
+ metadata.gz: '08ded5bfa9b93dac612301c125c9acd95fae727856154c861971917f3751cd1e11d15f49c9a3b2c00c70ac468c0e19f92a5d69a59158c63d5a826c70322478b2'
7
+ data.tar.gz: 23641e005618db6c5b09cfe64fbc8dad905930ae38bf04cfefcff722d681fc8423c7d39cb70d1cccb6d11743ff4cc9aa8c94c7b13a96d4e82b488d7e0a8fe240
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [1.4.0] - 2023-11-23
2
+
3
+ - Added ability to specify custom `data-action` for the close button.
4
+ - Code cleanup, deduplication
5
+
6
+ ## [1.3.1] - 2023-11-23
7
+
8
+ - Bug fixes
9
+
1
10
  ## [1.3.0] - 2023-11-14
2
11
 
3
12
  - Added ability to pass in a `title` block.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ultimate_turbo_modal (1.3.1)
4
+ ultimate_turbo_modal (1.4.0)
5
5
  phlex-rails (>= 1.0, < 2.0)
6
6
  rails (>= 7)
7
7
  stimulus-rails
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Phlex
2
4
  module DeferredRenderWithMainContent
3
5
  def template(&)
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class UltimateTurboModal::Base < Phlex::HTML
2
4
  prepend Phlex::DeferredRenderWithMainContent
3
5
  # @param padding [Boolean] Whether to add padding around the modal content
4
- # @param close_button [Boolean] Whether to show a close button.
6
+ # @param close_button [Boolean] Whether to show a close button
7
+ # @param close_button_sr_label [String] Close button label for screen readers
8
+ # @param close_button_data_action [String] `data-action` attribute for the close button
5
9
  # @param advance [Boolean] Whether to update the browser history when opening and closing the modal
6
10
  # @param header_divider [Boolean] Whether to show a divider between the header and the main content
7
11
  # @param footer_divider [Boolean] Whether to show a divider between the main content and the footer
@@ -10,6 +14,8 @@ class UltimateTurboModal::Base < Phlex::HTML
10
14
  def initialize(
11
15
  padding: UltimateTurboModal.configuration.padding,
12
16
  close_button: UltimateTurboModal.configuration.close_button,
17
+ close_button_sr_label: "Close modal",
18
+ close_button_data_action: "modal#hideModal",
13
19
  advance: UltimateTurboModal.configuration.advance,
14
20
  header: UltimateTurboModal.configuration.header,
15
21
  header_divider: UltimateTurboModal.configuration.header_divider,
@@ -18,6 +24,8 @@ class UltimateTurboModal::Base < Phlex::HTML
18
24
  )
19
25
  @padding = padding
20
26
  @close_button = close_button
27
+ @close_button_sr_label = close_button_sr_label
28
+ @close_button_data_action = close_button_data_action
21
29
  @advance = !!advance
22
30
  @advance_url = advance if advance.present? && advance.is_a?(String)
23
31
  @title = title
@@ -93,4 +101,124 @@ class UltimateTurboModal::Base < Phlex::HTML
93
101
  def respond_to_missing?(method, include_private = false)
94
102
  self.class.included_modules.any? { |mod| mod.instance_methods.include?(method) } || super
95
103
  end
104
+
105
+ ## HTML components
106
+
107
+ def modal(&)
108
+ outer_divs do
109
+ div_content do
110
+ div_header
111
+ div_main(&)
112
+ div_footer if footer?
113
+ end
114
+ end
115
+ end
116
+
117
+ def outer_divs(&)
118
+ div_dialog do
119
+ div_overlay
120
+ div_outer do
121
+ div_inner(&)
122
+ end
123
+ end
124
+ end
125
+
126
+ def div_dialog(&)
127
+ div(id: "modal-container",
128
+ class: self.class::DIV_DIALOG_CLASSES,
129
+ role: "dialog",
130
+ aria: {
131
+ labeled_by: "modal-title-h",
132
+ modal: true
133
+ },
134
+ data: {
135
+ controller: "modal",
136
+ modal_target: "container",
137
+ modal_advance_url_value: advance_url,
138
+ action: "turbo:submit-end->modal#submitEnd keyup@window->modal#closeWithKeyboard click@window->modal#outsideModalClicked click->modal#outsideModalClicked",
139
+ transition_enter: "ease-out duration-100",
140
+ transition_enter_start: "opacity-0",
141
+ transition_enter_end: "opacity-100",
142
+ transition_leave: "ease-in duration-50",
143
+ transition_leave_start: "opacity-100",
144
+ transition_leave_end: "opacity-0",
145
+ padding: padding?.to_s,
146
+ title: title?.to_s,
147
+ header: header?.to_s,
148
+ close_button: close_button?.to_s,
149
+ header_divider: header_divider?.to_s,
150
+ footer_divider: footer_divider?.to_s
151
+ }, &)
152
+ end
153
+
154
+ def div_overlay
155
+ div(id: "modal-overlay", class: self.class::DIV_OVERLAY_CLASSES)
156
+ end
157
+
158
+ def div_outer(&)
159
+ div(id: "modal-outer", class: self.class::DIV_OUTER_CLASSES, &)
160
+ end
161
+
162
+ def div_inner(&)
163
+ div(id: "modal-inner", class: self.class::DIV_INNER_CLASSES, &)
164
+ end
165
+
166
+ def div_content(&)
167
+ div(id: "modal-content", class: self.class::DIV_CONTENT_CLASSES, data: {modal_target: "content"}, &)
168
+ end
169
+
170
+ def div_main(&)
171
+ div(id: "modal-main", class: self.class::DIV_MAIN_CLASSES, &)
172
+ end
173
+
174
+ def div_header(&)
175
+ div(id: "modal-header", class: self.class::DIV_HEADER_CLASSES) do
176
+ div_title
177
+ button_close
178
+ end
179
+ end
180
+
181
+ def div_title
182
+ div(id: "modal-title", class: self.class::DIV_TITLE_CLASSES) do
183
+ if @title_block.present?
184
+ render @title_block
185
+ else
186
+ h3(id: "modal-title-h", class: self.class::DIV_TITLE_H_CLASSES) { @title }
187
+ end
188
+ end
189
+ end
190
+
191
+ def div_footer
192
+ div(id: "modal-footer", class: self.class::DIV_FOOTER_CLASSES) do
193
+ render @footer
194
+ end
195
+ end
196
+
197
+ def button_close
198
+ div(id: "modal-close", class: self.class::BUTTON_CLOSE_CLASSES) do
199
+ close_button_tag do
200
+ icon_close
201
+ span(class: self.class::BUTTON_CLOSE_SR_ONLY_CLASSES) { @close_button_sr_label }
202
+ end
203
+ end
204
+ end
205
+
206
+ def close_button_tag(&)
207
+ button(type: "button",
208
+ aria: {label: "close"},
209
+ class: self.class::CLOSE_BUTTON_TAG_CLASSES,
210
+ data: {
211
+ action: @close_button_data_action
212
+ }, &)
213
+ end
214
+
215
+ def icon_close
216
+ svg(class: self.class::ICON_CLOSE_CLASSES, fill: "currentColor", viewBox: "0 0 20 20") do |s|
217
+ s.path(
218
+ fill_rule: "evenodd",
219
+ d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
220
+ clip_rule: "evenodd"
221
+ )
222
+ end
223
+ end
96
224
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal
2
4
  class << self
3
5
  attr_accessor :configuration
@@ -1,137 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal::Flavors
2
4
  class Tailwind < UltimateTurboModal::Base
3
-
4
- private
5
-
6
- def modal(&)
7
- outer_divs do
8
- div_content do
9
- div_header
10
- div_main(&)
11
- div_footer if footer?
12
- end
13
- end
14
- end
15
-
16
- def outer_divs(&)
17
- div_dialog do
18
- div_overlay
19
- div_outer do
20
- div_inner(&)
21
- end
22
- end
23
- end
24
-
25
- def div_dialog(&)
26
- div(id: "modal-container",
27
- class: "relative group",
28
- role: "dialog",
29
- aria: {
30
- labeled_by: "modal-title-h",
31
- modal: true
32
- },
33
- data: {
34
- controller: "modal",
35
- modal_target: "container",
36
- modal_advance_url_value: advance_url,
37
- action: "turbo:submit-end->modal#submitEnd keyup@window->modal#closeWithKeyboard click@window->modal#outsideModalClicked click->modal#outsideModalClicked",
38
- transition_enter: "ease-out duration-100",
39
- transition_enter_start: "opacity-0",
40
- transition_enter_end: "opacity-100",
41
- transition_leave: "ease-in duration-50",
42
- transition_leave_start: "opacity-100",
43
- transition_leave_end: "opacity-0",
44
- padding: padding?.to_s,
45
- title: title?.to_s,
46
- header: header?.to_s,
47
- close_button: close_button?.to_s,
48
- header_divider: header_divider?.to_s,
49
- footer_divider: footer_divider?.to_s
50
- }, &)
51
- end
52
-
53
- def div_overlay
54
- div(id: "modal-overlay",
55
- class: "fixed inset-0 bg-gray-900 bg-opacity-50 transition-opacity dark:bg-opacity-80 z-40")
56
- end
57
-
58
- def div_outer(&)
59
- div(id: "modal-outer",
60
- class: "fixed inset-0 z-50 overflow-y-auto sm:max-w-[80%] md:max-w-3xl sm:mx-auto m-4", &)
61
- end
62
-
63
- def div_inner(&)
64
- div(id: "modal-inner",
65
- class: "flex min-h-full items-center justify-center p-1 sm:p-4", &)
66
- end
67
-
68
- def div_content(&)
69
- div(id: "modal-content",
70
- class: "relative transform overflow-hidden rounded-lg bg-white text-left shadow transition-all
71
- sm:my-8 sm:max-w-3xl dark:bg-gray-800 dark:text-white",
72
- data: {modal_target: "content"}, &)
73
- end
74
-
75
- def div_main(&)
76
- div(id: "modal-main", class: "group-data-[padding=true]:p-4 group-data-[padding=true]:pt-2", &)
77
- end
78
-
79
- def header_block
80
- return if @header_block.blank?
81
- render @header_block
82
- nil
83
- end
84
-
85
- def div_header(&)
86
- div(id: "modal-header", class: "flex justify-between items-center w-full py-4 rounded-t dark:border-gray-600 group-data-[header-divider=true]:border-b group-data-[header=false]:absolute") do
87
- div_title
88
- button_close
89
- end
90
- end
91
-
92
- def div_title
93
- div(id: "modal-title", class: "pl-4") do
94
- if @title_block.present?
95
- render @title_block
96
- else
97
- h3(id: "modal-title-h", class: "group-data-[title=false]:hidden text-lg font-semibold text-gray-900 dark:text-white") { @title }
98
- end
99
- end
100
- end
101
-
102
- def div_footer
103
- div(id: "modal-footer", class: "flex p-4 rounded-b dark:border-gray-600 group-data-[footer-divider=true]:border-t") do
104
- render @footer
105
- end
106
- end
107
-
108
- def button_close
109
- div(id: "modal-close", class: "mr-4 group-data-[close-button=false]:hidden") do
110
- close_button_tag do
111
- icon_close
112
- span(class: "sr-only") { "Close modal" }
113
- end
114
- end
115
- end
116
-
117
- def close_button_tag(&)
118
- button(type: "button",
119
- aria: {label: "close"},
120
- class: "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm
121
- p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white",
122
- data: {
123
- action: "modal#hideModal"
124
- }, &)
125
- end
126
-
127
- def icon_close
128
- svg(class: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20") do |s|
129
- s.path(
130
- fill_rule: "evenodd",
131
- d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
132
- clip_rule: "evenodd"
133
- )
134
- end
135
- end
5
+ DIV_DIALOG_CLASSES = "relative group"
6
+ DIV_OVERLAY_CLASSES = "fixed inset-0 bg-gray-900 bg-opacity-50 transition-opacity dark:bg-opacity-80 z-40"
7
+ DIV_OUTER_CLASSES = "fixed inset-0 z-50 overflow-y-auto sm:max-w-[80%] md:max-w-3xl sm:mx-auto m-4"
8
+ DIV_INNER_CLASSES = "flex min-h-full items-center justify-center p-1 sm:p-4"
9
+ DIV_CONTENT_CLASSES = "relative transform overflow-hidden rounded-lg bg-white text-left shadow transition-all sm:my-8 sm:max-w-3xl dark:bg-gray-800 dark:text-white"
10
+ DIV_MAIN_CLASSES = "group-data-[padding=true]:p-4 group-data-[padding=true]:pt-2"
11
+ DIV_HEADER_CLASSES = "flex justify-between items-center w-full py-4 rounded-t dark:border-gray-600 group-data-[header-divider=true]:border-b group-data-[header=false]:absolute"
12
+ DIV_TITLE_CLASSES = "pl-4"
13
+ DIV_TITLE_H_CLASSES = "group-data-[title=false]:hidden text-lg font-semibold text-gray-900 dark:text-white"
14
+ DIV_FOOTER_CLASSES = "flex p-4 rounded-b dark:border-gray-600 group-data-[footer-divider=true]:border-t"
15
+ BUTTON_CLOSE_CLASSES = "mr-4 group-data-[close-button=false]:hidden"
16
+ BUTTON_CLOSE_SR_ONLY_CLASSES = "sr-only"
17
+ CLOSE_BUTTON_TAG_CLASSES = "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
18
+ ICON_CLOSE_CLASSES = "w-5 h-5"
136
19
  end
137
20
  end
@@ -1,124 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal::Flavors
2
4
  class Vanilla < UltimateTurboModal::Base
3
-
4
- private
5
-
6
- def modal(&)
7
- outer_divs do
8
- div_content do
9
- div_header
10
- div_main(&)
11
- div_footer if footer?
12
- end
13
- end
14
- end
15
-
16
- def outer_divs(&)
17
- div_dialog do
18
- div_overlay
19
- div_outer do
20
- div_inner(&)
21
- end
22
- end
23
- end
24
-
25
- def div_dialog(&)
26
- div(id: "modal-container",
27
- class: "modal-container",
28
- role: "dialog",
29
- aria: {
30
- labeled_by: "modal-title-h",
31
- modal: true
32
- },
33
- data: {
34
- controller: "modal",
35
- modal_target: "container",
36
- modal_advance_url_value: advance_url,
37
- action: "turbo:submit-end->modal#submitEnd keyup@window->modal#closeWithKeyboard click@window->modal#outsideModalClicked click->modal#outsideModalClicked",
38
- transition_enter: "ease-out duration-100",
39
- transition_enter_start: "opacity-0",
40
- transition_enter_end: "opacity-100",
41
- transition_leave: "ease-in duration-50",
42
- transition_leave_start: "opacity-100",
43
- transition_leave_end: "opacity-0",
44
- padding: padding?.to_s,
45
- title: title?.to_s,
46
- header: header?.to_s,
47
- close_button: close_button?.to_s,
48
- header_divider: header_divider?.to_s,
49
- footer_divider: footer_divider?.to_s
50
- }, &)
51
- end
52
-
53
- def div_overlay
54
- div(id: "modal-overlay", class: "modal-overlay")
55
- end
56
-
57
- def div_outer(&)
58
- div(id: "modal-outer", class: "modal-outer", &)
59
- end
60
-
61
- def div_inner(&)
62
- div(id: "modal-inner", class: "modal-inner", &)
63
- end
64
-
65
- def div_content(&)
66
- div(id: "modal-content", class: "modal-content", data: {modal_target: "content"}, &)
67
- end
68
-
69
- def div_main(&)
70
- div(id: "modal-main", class: "modal-main", &)
71
- end
72
-
73
- def div_header(&)
74
- div(id: "modal-header", class: "modal-header") do
75
- div_title
76
- button_close
77
- end
78
- end
79
-
80
- def div_title
81
- div(id: "modal-title", class: "modal-title") do
82
- if @title_block.present?
83
- render @title_block
84
- else
85
- h3(id: "modal-title-h", class: "modal-title-h") { @title }
86
- end
87
- end
88
- end
89
-
90
- def div_footer
91
- div(id: "modal-footer", class: "modal-footer") do
92
- render @footer
93
- end
94
- end
95
-
96
- def button_close
97
- div(id: "modal-close", class: "modal-close") do
98
- close_button_tag do
99
- icon_close
100
- span(class: "sr-only") { "Close modal" }
101
- end
102
- end
103
- end
104
-
105
- def close_button_tag(&)
106
- button(type: "button",
107
- aria: {label: "close"},
108
- class: "modal-close-button",
109
- data: {
110
- action: "modal#hideModal"
111
- }, &)
112
- end
113
-
114
- def icon_close
115
- svg(class: "modal-close-icon", fill: "currentColor", viewBox: "0 0 20 20") do |s|
116
- s.path(
117
- fill_rule: "evenodd",
118
- d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
119
- clip_rule: "evenodd"
120
- )
121
- end
122
- end
5
+ DIV_DIALOG_CLASSES = "modal-container"
6
+ DIV_OVERLAY_CLASSES = "modal-overlay"
7
+ DIV_OUTER_CLASSES = "modal-outer"
8
+ DIV_INNER_CLASSES = "modal-inner"
9
+ DIV_CONTENT_CLASSES = "modal-content"
10
+ DIV_MAIN_CLASSES = "modal-main"
11
+ DIV_HEADER_CLASSES = "modal-header"
12
+ DIV_TITLE_CLASSES = "modal-title"
13
+ DIV_TITLE_H_CLASSES = "modal-title-h"
14
+ DIV_FOOTER_CLASSES = "modal-footer"
15
+ BUTTON_CLOSE_CLASSES = "modal-close"
16
+ BUTTON_CLOSE_SR_ONLY_CLASSES = "sr-only"
17
+ CLOSE_BUTTON_TAG_CLASSES = "modal-close-button"
18
+ ICON_CLOSE_CLASSES = "modal-close-icon"
123
19
  end
124
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal::Helpers
2
4
  module ControllerHelper
3
5
  extend ActiveSupport::Concern
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal::Helpers
2
4
  module StreamHelper
3
5
  def modal(message)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UltimateTurboModal::Helpers
2
4
  module ViewHelper
3
5
  def modal(**, &)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UltimateTurboModal
4
- VERSION = "1.3.1"
4
+ VERSION = "1.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ultimate_turbo_modal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Mercier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-23 00:00:00.000000000 Z
11
+ date: 2023-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex-rails