bootstrap5-rails-extensions 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d474e088c4e028c5086730e93991c25b52559a7851469d5b27ef648c569418b4
4
+ data.tar.gz: 7e376d17ad4dbadba15b41cfb7010575d69dabfee2f494f6f3fb1df2ed9e5770
5
+ SHA512:
6
+ metadata.gz: a4d051ba8ca01298ae82f600974f3021ac1786f5ff50dd64546fe18bcbd2549ecdf2c6f56210b6b84f7521cb84203c801a759a14cbe85ac39d8d5350f3feb954
7
+ data.tar.gz: c6a9ba17cea83be96fecb56e2a04b1074132117c46b6daaad85a602552c26e0d67abbfbd6ebb5194a485a0951f9a30ef971d9b8219f281c53ceff2e8c327f061
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ # エージェント運用ドキュメント(ローカル環境用)
14
+ AGENTS.md
15
+ CLAUDE.md
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,221 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bootstrap5-rails-extensions (0.1.0)
5
+ rails (>= 8.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (8.0.3)
11
+ actionpack (= 8.0.3)
12
+ activesupport (= 8.0.3)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ zeitwerk (~> 2.6)
16
+ actionmailbox (8.0.3)
17
+ actionpack (= 8.0.3)
18
+ activejob (= 8.0.3)
19
+ activerecord (= 8.0.3)
20
+ activestorage (= 8.0.3)
21
+ activesupport (= 8.0.3)
22
+ mail (>= 2.8.0)
23
+ actionmailer (8.0.3)
24
+ actionpack (= 8.0.3)
25
+ actionview (= 8.0.3)
26
+ activejob (= 8.0.3)
27
+ activesupport (= 8.0.3)
28
+ mail (>= 2.8.0)
29
+ rails-dom-testing (~> 2.2)
30
+ actionpack (8.0.3)
31
+ actionview (= 8.0.3)
32
+ activesupport (= 8.0.3)
33
+ nokogiri (>= 1.8.5)
34
+ rack (>= 2.2.4)
35
+ rack-session (>= 1.0.1)
36
+ rack-test (>= 0.6.3)
37
+ rails-dom-testing (~> 2.2)
38
+ rails-html-sanitizer (~> 1.6)
39
+ useragent (~> 0.16)
40
+ actiontext (8.0.3)
41
+ actionpack (= 8.0.3)
42
+ activerecord (= 8.0.3)
43
+ activestorage (= 8.0.3)
44
+ activesupport (= 8.0.3)
45
+ globalid (>= 0.6.0)
46
+ nokogiri (>= 1.8.5)
47
+ actionview (8.0.3)
48
+ activesupport (= 8.0.3)
49
+ builder (~> 3.1)
50
+ erubi (~> 1.11)
51
+ rails-dom-testing (~> 2.2)
52
+ rails-html-sanitizer (~> 1.6)
53
+ activejob (8.0.3)
54
+ activesupport (= 8.0.3)
55
+ globalid (>= 0.3.6)
56
+ activemodel (8.0.3)
57
+ activesupport (= 8.0.3)
58
+ activerecord (8.0.3)
59
+ activemodel (= 8.0.3)
60
+ activesupport (= 8.0.3)
61
+ timeout (>= 0.4.0)
62
+ activestorage (8.0.3)
63
+ actionpack (= 8.0.3)
64
+ activejob (= 8.0.3)
65
+ activerecord (= 8.0.3)
66
+ activesupport (= 8.0.3)
67
+ marcel (~> 1.0)
68
+ activesupport (8.0.3)
69
+ base64
70
+ benchmark (>= 0.3)
71
+ bigdecimal
72
+ concurrent-ruby (~> 1.0, >= 1.3.1)
73
+ connection_pool (>= 2.2.5)
74
+ drb
75
+ i18n (>= 1.6, < 2)
76
+ logger (>= 1.4.2)
77
+ minitest (>= 5.1)
78
+ securerandom (>= 0.3)
79
+ tzinfo (~> 2.0, >= 2.0.5)
80
+ uri (>= 0.13.1)
81
+ base64 (0.3.0)
82
+ benchmark (0.4.1)
83
+ bigdecimal (3.2.3)
84
+ builder (3.3.0)
85
+ concurrent-ruby (1.3.5)
86
+ connection_pool (2.5.4)
87
+ crass (1.0.6)
88
+ date (3.4.1)
89
+ drb (2.2.3)
90
+ erb (5.0.2)
91
+ erubi (1.13.1)
92
+ globalid (1.3.0)
93
+ activesupport (>= 6.1)
94
+ i18n (1.14.7)
95
+ concurrent-ruby (~> 1.0)
96
+ io-console (0.8.1)
97
+ irb (1.15.2)
98
+ pp (>= 0.6.0)
99
+ rdoc (>= 4.0.0)
100
+ reline (>= 0.4.2)
101
+ logger (1.7.0)
102
+ loofah (2.24.1)
103
+ crass (~> 1.0.2)
104
+ nokogiri (>= 1.12.0)
105
+ mail (2.8.1)
106
+ mini_mime (>= 0.1.1)
107
+ net-imap
108
+ net-pop
109
+ net-smtp
110
+ marcel (1.1.0)
111
+ mini_mime (1.1.5)
112
+ minitest (5.25.5)
113
+ net-imap (0.5.11)
114
+ date
115
+ net-protocol
116
+ net-pop (0.1.2)
117
+ net-protocol
118
+ net-protocol (0.2.2)
119
+ timeout
120
+ net-smtp (0.5.1)
121
+ net-protocol
122
+ nio4r (2.7.4)
123
+ nokogiri (1.18.10-aarch64-linux-gnu)
124
+ racc (~> 1.4)
125
+ nokogiri (1.18.10-aarch64-linux-musl)
126
+ racc (~> 1.4)
127
+ nokogiri (1.18.10-arm-linux-gnu)
128
+ racc (~> 1.4)
129
+ nokogiri (1.18.10-arm-linux-musl)
130
+ racc (~> 1.4)
131
+ nokogiri (1.18.10-arm64-darwin)
132
+ racc (~> 1.4)
133
+ nokogiri (1.18.10-x86_64-darwin)
134
+ racc (~> 1.4)
135
+ nokogiri (1.18.10-x86_64-linux-gnu)
136
+ racc (~> 1.4)
137
+ nokogiri (1.18.10-x86_64-linux-musl)
138
+ racc (~> 1.4)
139
+ pp (0.6.2)
140
+ prettyprint
141
+ prettyprint (0.2.0)
142
+ psych (5.2.6)
143
+ date
144
+ stringio
145
+ racc (1.8.1)
146
+ rack (3.2.1)
147
+ rack-session (2.1.1)
148
+ base64 (>= 0.1.0)
149
+ rack (>= 3.0.0)
150
+ rack-test (2.2.0)
151
+ rack (>= 1.3)
152
+ rackup (2.2.1)
153
+ rack (>= 3)
154
+ rails (8.0.3)
155
+ actioncable (= 8.0.3)
156
+ actionmailbox (= 8.0.3)
157
+ actionmailer (= 8.0.3)
158
+ actionpack (= 8.0.3)
159
+ actiontext (= 8.0.3)
160
+ actionview (= 8.0.3)
161
+ activejob (= 8.0.3)
162
+ activemodel (= 8.0.3)
163
+ activerecord (= 8.0.3)
164
+ activestorage (= 8.0.3)
165
+ activesupport (= 8.0.3)
166
+ bundler (>= 1.15.0)
167
+ railties (= 8.0.3)
168
+ rails-dom-testing (2.3.0)
169
+ activesupport (>= 5.0.0)
170
+ minitest
171
+ nokogiri (>= 1.6)
172
+ rails-html-sanitizer (1.6.2)
173
+ loofah (~> 2.21)
174
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
175
+ railties (8.0.3)
176
+ actionpack (= 8.0.3)
177
+ activesupport (= 8.0.3)
178
+ irb (~> 1.13)
179
+ rackup (>= 1.0.0)
180
+ rake (>= 12.2)
181
+ thor (~> 1.0, >= 1.2.2)
182
+ tsort (>= 0.2)
183
+ zeitwerk (~> 2.6)
184
+ rake (13.3.0)
185
+ rdoc (6.15.0)
186
+ erb
187
+ psych (>= 4.0.0)
188
+ tsort
189
+ reline (0.6.2)
190
+ io-console (~> 0.5)
191
+ securerandom (0.4.1)
192
+ stringio (3.1.7)
193
+ thor (1.4.0)
194
+ timeout (0.4.3)
195
+ tsort (0.2.0)
196
+ tzinfo (2.0.6)
197
+ concurrent-ruby (~> 1.0)
198
+ uri (1.0.3)
199
+ useragent (0.16.11)
200
+ websocket-driver (0.8.0)
201
+ base64
202
+ websocket-extensions (>= 0.1.0)
203
+ websocket-extensions (0.1.5)
204
+ zeitwerk (2.7.3)
205
+
206
+ PLATFORMS
207
+ aarch64-linux-gnu
208
+ aarch64-linux-musl
209
+ arm-linux-gnu
210
+ arm-linux-musl
211
+ arm64-darwin
212
+ x86_64-darwin
213
+ x86_64-linux-gnu
214
+ x86_64-linux-musl
215
+
216
+ DEPENDENCIES
217
+ bootstrap5-rails-extensions!
218
+ rake (~> 13.0)
219
+
220
+ BUNDLED WITH
221
+ 2.7.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dreaw Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # Bootstrap5 Rails Extensions(Rails Engine)
2
+
3
+ RailsアプリケーションでBootstrap 5をTurbo/Stimulusと併用しやすくするための拡張を提供します。モーダルやオフキャンバス、カード、テーブル、トースト向けのDSLヘルパーと、対応するStimulusコントローラを同梱しています。
4
+
5
+ ## インストール
6
+
7
+ Gemfile(ホストアプリ):
8
+
9
+ ```ruby
10
+ gem "bootstrap5-rails-extensions"
11
+ ```
12
+
13
+ `bundle install` を実行してください。
14
+
15
+ ## セットアップ
16
+
17
+ ### Stimulusコントローラの登録
18
+
19
+ Importmapをご利用の場合は、エンジンが提供するコントローラ群をpinしてください。
20
+
21
+ ```ruby
22
+ # config/importmap.rb
23
+ pin_all_from "bootstrap5_rails_extensions", under: "bootstrap5_rails_extensions"
24
+ ```
25
+
26
+ Stimulusアプリケーションへの登録例です。
27
+
28
+ ```javascript
29
+ // app/javascript/controllers/index.js
30
+ import { application } from "./application"
31
+
32
+ import ModalController from "bootstrap5_rails_extensions/modal_controller"
33
+ import OffcanvasController from "bootstrap5_rails_extensions/offcanvas_controller"
34
+ import ToastController from "bootstrap5_rails_extensions/toast_controller"
35
+
36
+ application.register("modal", ModalController)
37
+ application.register("offcanvas", OffcanvasController)
38
+ application.register("toast", ToastController)
39
+ ```
40
+
41
+ ESBuildやViteなどを利用している場合も、同様にコントローラをインポートして登録してください。
42
+
43
+ ### トースト用コンテナの設置
44
+
45
+ レイアウトなど、常に描画されるテンプレートに以下を追加しておくと、Turbo Stream経由でトーストを流し込めます。
46
+
47
+ ```erb
48
+ <%= render_toast %>
49
+ ```
50
+
51
+ フラッシュメッセージを初期表示したい場合は `render_toast(flash_messages: flash)` のように渡してください。
52
+
53
+ ## ビューヘルパー
54
+
55
+ ### render_modal
56
+
57
+ Stripeダッシュボード風のモーダルDSLです。`render_modal` にIDとタイトルを渡し、ブロックで本文・フッターを組み立てます。
58
+
59
+ ```erb
60
+ <%= render_modal id: "settingsModal", title: "設定", dialog: { size: :lg, centered: true } do |modal| %>
61
+ <% modal.body do %>
62
+ <turbo-frame id="modal-settings-frame">
63
+ <div class="text-center text-muted py-4">読み込み中...</div>
64
+ </turbo-frame>
65
+ <% end %>
66
+ <% end %>
67
+ ```
68
+
69
+ フォームを扱う場合は `form:` に `form_with` のオプションをそのまま渡してください。`modal.body` / `modal.footer` のブロックにはフォームビルダーが渡されます。
70
+
71
+ ```erb
72
+ <%= render_modal id: "userModal", title: "ユーザー追加", form: { model: User.new, url: users_path } do |modal| %>
73
+ <% modal.body do |f| %>
74
+ <%= f.text_field :name, class: "form-control" %>
75
+ <% end %>
76
+ <% modal.footer do |f| %>
77
+ <%= f.submit "保存", class: "btn btn-primary" %>
78
+ <% end %>
79
+ <% end %>
80
+ ```
81
+
82
+ モーダルを開くトリガー要素には、Stimulusコントローラが参照する属性を付与します。
83
+
84
+ ```erb
85
+ <%= link_to settings_path,
86
+ class: "btn btn-primary btn-sm",
87
+ data: { modal_target: "#settingsModal", turbo_frame: "modal-settings-frame" } do %>
88
+ 設定を開く
89
+ <% end %>
90
+ ```
91
+
92
+ 主なオプションです。
93
+
94
+ - `dialog: { size: :sm|:lg|:xl|:fullscreen, centered: true, scrollable: true }`
95
+ - `form:` `form_with` に渡すHash。指定時はブロックにフォームビルダーが渡されます。
96
+ - `data:` モーダル要素に付与するdata属性(`controller: "modal"` は自動付与されます)。
97
+
98
+ ### render_offcanvas
99
+
100
+ Bootstrap 5.3のオフキャンバスをDSLで構築します。タイトルは第1引数またはキーワード引数で指定できます。
101
+
102
+ ```erb
103
+ <%= render_offcanvas id: "previewOffcanvas", title: "プレビュー", placement: :end do %>
104
+ <turbo-frame id="offcanvas-form-template-preview">
105
+ 読み込み中...
106
+ </turbo-frame>
107
+ <% end %>
108
+ ```
109
+
110
+ トリガー例:
111
+
112
+ ```erb
113
+ <%= link_to preview_path,
114
+ class: "btn btn-outline-primary btn-sm",
115
+ data: { offcanvas_target: "#previewOffcanvas", turbo_frame: "offcanvas-form-template-preview" } do %>
116
+ プレビュー
117
+ <% end %>
118
+ ```
119
+
120
+ - `placement:` は `:start|:end|:top|:bottom` を受け付けます(既定は`:end`)。
121
+ - `footer:` に文字列またはブロックを渡すと、フッター領域を描画します。
122
+ - `data:` で追加のdata属性を渡せます(`controller: "offcanvas"` を自動付与します)。
123
+
124
+ ### render_card
125
+
126
+ カードを簡潔に定義できるDSLです。ヘッダー、本文、フッターを1度ずつ定義できます。
127
+
128
+ ```erb
129
+ <%= render_card class: "mb-4" do |card| %>
130
+ <% card.header class: "d-flex align-items-center" do %>
131
+ 設定
132
+ <% end %>
133
+ <% card.body class: "p-4" do %>
134
+ 本文...
135
+ <% end %>
136
+ <% card.footer class: "text-end" do %>
137
+ <%= link_to "閉じる", "#", class: "btn btn-outline-secondary" %>
138
+ <% end %>
139
+ <% end %>
140
+ ```
141
+
142
+ `data:` オプションで親要素にdata属性を追加できます。
143
+
144
+ ### render_table
145
+
146
+ `table-responsive` ラッパー付きでBootstrapのテーブルを出力します。
147
+
148
+ ```erb
149
+ <%= render_table class: "table-striped" do |table| %>
150
+ <% table.thead class: "table-light" do %>
151
+ <tr><th>名前</th><th>状態</th></tr>
152
+ <% end %>
153
+ <% table.tbody do %>
154
+ <% @users.each do |user| %>
155
+ <tr>
156
+ <td><%= user.name %></td>
157
+ <td><%= user.status %></td>
158
+ </tr>
159
+ <% end %>
160
+ <% end %>
161
+ <% end %>
162
+ ```
163
+
164
+ `wrapper_class` や `wrapper_html` を渡すことでラッパー要素のカスタマイズも可能です。
165
+
166
+ ### render_toast
167
+
168
+ トーストコンテナを描画し、フラッシュメッセージを初期表示できます。
169
+
170
+ ```erb
171
+ <%= render_toast id: "toast-root", position: "top-0 end-0", flash_messages: flash %>
172
+ ```
173
+
174
+ `position:` にはBootstrapのユーティリティクラスをそのまま渡してください。
175
+
176
+ ## Turbo Streams拡張
177
+
178
+ `Turbo::Streams::TagBuilder` を拡張し、`turbo_stream.toast` を利用できるようにしています。トーストコンテナ(既定ID: `toast-root`)にメッセージを積み上げます。
179
+
180
+ ```ruby
181
+ # コントローラまたはビュー
182
+ render turbo_stream: turbo_stream.toast(:notice, "保存しました")
183
+
184
+ # 追加オプション
185
+ render turbo_stream: turbo_stream.toast(:alert, @user, target: "custom-toast", autohide: false, delay: 8000)
186
+ ```
187
+
188
+ `message_or_record` にActiveModelを渡すと、エラー内容をリスト表示します。
189
+
190
+ ---
191
+
192
+ ご不明点があればIssueやPull Requestでお気軽にお知らせください。
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ # gem作成時の基本タスクのみ提供
6
+ task default: :build
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bootstrap5RailsExtensions
4
+ module CardHelper
5
+ # Bootstrapのcardコンポーネントを簡潔に記述できるDSL。
6
+ #
7
+ # 使い方:
8
+ # <%= render_card class: "mb-4" do |card| %>
9
+ # <% card.header class: "d-flex align-items-center" do %>
10
+ # タイトル
11
+ # <% end %>
12
+ # <% card.body class: "p-4" do %>
13
+ # 本文...
14
+ # <% end %>
15
+ # <% card.footer class: "text-end" do %>
16
+ # フッター
17
+ # <% end %>
18
+ # <% end %>
19
+ #
20
+ # オプション:
21
+ # class: 親要素に追加するクラス
22
+ # data: 親要素に付与するdata属性
23
+ def render_card(data: {}, **html_options)
24
+ raise ArgumentError, "カードは本文を定義する必要があります" unless block_given?
25
+
26
+ builder = CardBuilder.new(self)
27
+ yield(builder)
28
+
29
+ html_options[:class] = class_names("card", html_options[:class])
30
+
31
+ if data.present?
32
+ html_options[:data] = (html_options[:data] || {}).merge(data)
33
+ end
34
+
35
+ content_tag(:div, **html_options) do
36
+ builder.render
37
+ end
38
+ end
39
+
40
+ class CardBuilder
41
+ def initialize(view_context)
42
+ @view = view_context
43
+ @header_html = nil
44
+ @header_options = {}
45
+ @body_fragments = []
46
+ @body_options = {}
47
+ @footer_html = nil
48
+ @footer_options = {}
49
+ end
50
+
51
+ def header(content = nil, **options, &block)
52
+ raise ArgumentError, "カードヘッダーは一度だけ定義できます" if @header_html
53
+
54
+ @header_html = extract_content(content, &block)
55
+ css_class = extract_css_class!(options)
56
+ @header_options[:class] = css_class if css_class.present?
57
+ self
58
+ end
59
+
60
+ def body(content = nil, **options, &block)
61
+ html = extract_content(content, &block)
62
+ raise ArgumentError, "カード本文が空です" if blank_html?(html)
63
+
64
+ @body_fragments << html
65
+ css_class = extract_css_class!(options)
66
+ if css_class.present?
67
+ if @body_options[:class].present? && @body_options[:class] != css_class
68
+ raise ArgumentError, "カード本文のクラス指定は一度だけ行ってください"
69
+ end
70
+ @body_options[:class] = css_class
71
+ end
72
+ self
73
+ end
74
+
75
+ def footer(content = nil, **options, &block)
76
+ raise ArgumentError, "カードフッターは一度だけ定義できます" if @footer_html
77
+
78
+ html = extract_content(content, &block)
79
+ @footer_html = html unless blank_html?(html)
80
+ css_class = extract_css_class!(options)
81
+ @footer_options[:class] = css_class if css_class.present?
82
+ self
83
+ end
84
+
85
+ def render
86
+ raise ArgumentError, "カード本文を定義してください" if @body_fragments.empty?
87
+
88
+ fragments = []
89
+ fragments << wrap_section(@header_html, default_header_class, @header_options[:class]) if @header_html
90
+ fragments << wrap_section(@view.safe_join(@body_fragments), default_body_class, @body_options[:class])
91
+ fragments << wrap_section(@footer_html, default_footer_class, @footer_options[:class]) if @footer_html
92
+ @view.safe_join(fragments.compact)
93
+ end
94
+
95
+ private
96
+
97
+ def extract_content(content, &block)
98
+ if block_given?
99
+ @view.capture(&block)
100
+ else
101
+ content
102
+ end
103
+ end
104
+
105
+ def extract_css_class!(options)
106
+ return nil if options.nil? || options.empty?
107
+
108
+ css_class = options.delete(:class)
109
+ raise ArgumentError, "サポートされていないオプションです: #{options.keys.join(', ')}" if options.present?
110
+
111
+ css_class
112
+ end
113
+
114
+ def wrap_section(content, wrapper_class, extra_class = nil)
115
+ return if blank_html?(content)
116
+ if wrapper_class == false && extra_class.blank?
117
+ return content
118
+ end
119
+
120
+ combined_class = [wrapper_class == false ? nil : wrapper_class, extra_class.presence].compact.join(" ")
121
+ return content if combined_class.blank?
122
+
123
+ @view.content_tag(:div, content, class: combined_class)
124
+ end
125
+
126
+ def default_header_class
127
+ "card-header"
128
+ end
129
+
130
+ def default_body_class
131
+ "card-body"
132
+ end
133
+
134
+ def default_footer_class
135
+ "card-footer"
136
+ end
137
+
138
+ def blank_html?(html)
139
+ html.respond_to?(:blank?) ? html.blank? : html.to_s.strip.empty?
140
+ end
141
+ end
142
+ end
143
+ end