bulma-phlex 0.16.0 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 358df6739ab960f7d8b3799001c40b0d80a6e1c1d599b31b115b67bdcae46645
4
- data.tar.gz: 54a368dec6bf6dfa7c21f32779e63651bea141070367880f7b1b938da86c92f3
3
+ metadata.gz: d62a43330834f293718993ab3227796dbb76d3ae57b4c0efca10df5dc1e35a6b
4
+ data.tar.gz: 1b310813d75fe054a993a02d258a05fa52ba4f15db249d2e269c1871fdf2a025
5
5
  SHA512:
6
- metadata.gz: 81cea339f39b95a17bd43e18f294944327744c81601fb03d1d7e7975e397973ebfa488350540eeb008e587c74ae3805ce8ff3ec15aa193277bd68bae03cee201
7
- data.tar.gz: 021f7fc3b32d02a16c1c08a695e915b8beca73219b86da9766c58077afa5b13e205d337fd2ccace80378905076ccb821d2c9c244313ace1c1c32c38752258045
6
+ metadata.gz: a0b4c95ba751e8a2f5718b08bcd389ebe055f087271a1ee77f06c2e9292088bc4a770aeeacdb31c304349ac372373cd40ae5fd90f498eef15a1c6502732028cc
7
+ data.tar.gz: 2579cf14a6d59153dc4e44781eba67b451513e50d551e59141be6f61be9d130862c6553643b150393941a40f7ea99f10f40ff10dd824489ea12d18afdaeff875
data/README.md CHANGED
@@ -9,6 +9,7 @@ This gem provides a set of ready-to-use [Phlex](https://github.com/phlex-ruby/ph
9
9
  ## Table of Contents
10
10
 
11
11
  - [Installation](#installation)
12
+ - [Configuration](#configuration)
12
13
  - [Usage](#usage)
13
14
  - [Breadcrumb](#breadcrumb)
14
15
  - [Button](#button)
@@ -20,7 +21,10 @@ This gem provides a set of ready-to-use [Phlex](https://github.com/phlex-ruby/ph
20
21
  - [Grid](#grid)
21
22
  - [Hero](#hero)
22
23
  - [Icon](#icon)
24
+ - [Image](#image)
23
25
  - [Level](#level)
26
+ - [Media Object](#media-object)
27
+ - [Menu](#menu)
24
28
  - [Message](#message)
25
29
  - [Modal](#modal)
26
30
  - [NavigationBar](#navigationbar)
@@ -74,6 +78,24 @@ This gem requires:
74
78
  require "bulma-phlex"
75
79
  ```
76
80
 
81
+ ## Configuration
82
+
83
+ Icons chosen internally by components can be configured globally. The default icons are Font Awesome 7 solid icons, but you can change them to other icons by providing the appropriate class names.
84
+
85
+ ```ruby
86
+ BulmaPhlex.configure do |config|
87
+ config.icons.sort = {
88
+ ascending: "fa-solid fa-arrow-up",
89
+ descending: "fa-solid fa-arrow-down",
90
+ inactive: "fa-solid fa-sort"
91
+ }
92
+ config.icons.dropdown = "fa-solid fa-chevron-down"
93
+ config.icons.file_upload = "fa-solid fa-upload"
94
+ config.icons.conditional = "fa-solid fa-check"
95
+ end
96
+ ```
97
+
98
+ Sortable columns also accept an `icons:` hash for a per-column override. Dropdowns accept `icon:` and conditional table columns accept `icon_class:`.
77
99
 
78
100
  ## Usage
79
101
 
@@ -93,7 +115,6 @@ end
93
115
 
94
116
  It supports options for **alignment** (centered, right-aligned), **separator** style (arrow, bullet, dot, or succeeds), and **size** (small, medium, large). Items also support an optional `icon` argument.
95
117
 
96
-
97
118
  ### Button
98
119
 
99
120
  Renders a [Bulma button](https://bulma.io/documentation/elements/button/) element with support for color, size, style modifiers, and left/right icons.
@@ -101,7 +122,7 @@ Renders a [Bulma button](https://bulma.io/documentation/elements/button/) elemen
101
122
  The component generates a `<button>` by default. Pass an `href:` attribute to generate an `<a>` element instead. Pass `input: "submit"` (or `"button"` or `"reset"`) to generate an `<input>` element.
102
123
 
103
124
  ```ruby
104
- BulmaPhlex::Button("Like", color: "primary", size: "large", icon: "fas fa-thumbs-up")
125
+ BulmaPhlex::Button("Like", color: "primary", size: "large", icon: "fa-solid fa-thumbs-up")
105
126
  BulmaPhlex::Button(href: "/profile") { "View Profile" }
106
127
  BulmaPhlex::Button(input: "submit", color: "success")
107
128
  ```
@@ -110,22 +131,24 @@ The button label can be passed in as a string or in a block.
110
131
 
111
132
  ### Card
112
133
 
113
- Renders a [Bulma card](https://bulma.io/documentation/components/card/) with an optional header, content area, and footer links. Each section is populated via builder methods on the yielded component.
134
+ Renders a [Bulma card](https://bulma.io/documentation/components/card/) with an optional header, image, content area, and footer links. Each section is populated via builder methods on the yielded component.
114
135
 
115
136
  ```ruby
116
137
  render BulmaPhlex::Card.new do |card|
117
- card.head("Card Title")
138
+ card.header("Card Title")
139
+ card.image(src: "image.jpg", alt: "Card image")
118
140
  card.content do
119
141
  "This is some card content"
120
142
  end
121
143
  card.footer_link("View", "/view", target: "_blank")
122
- card.footer_link("Edit", "/edit", icon: "fas fa-edit")
144
+ card.footer_link("Edit", "/edit", icon: "fa-solid fa-pen-to-square")
123
145
  end
124
146
  ```
125
147
 
126
- Use method `footer_item` for full control of the footer items. Class `footer-item` must be on the outer element
127
- of the block.
148
+ The `header` method accepts a string, for a simple title, or a block for custom content.
128
149
 
150
+ Use method `footer_item` for full control of the footer items. Class `footer-item` must be on the outer element
151
+ of the block.
129
152
 
130
153
  ### Columns
131
154
 
@@ -141,7 +164,6 @@ end
141
164
 
142
165
  The `gap:` option accepts either an integer (0–8) or a hash keyed by breakpoint for responsive gap sizes.
143
166
 
144
-
145
167
  ### Dropdown
146
168
 
147
169
  Renders a [Bulma dropdown](https://bulma.io/documentation/components/dropdown/) menu. Supports click-to-toggle (default, via a Stimulus controller) and hoverable (no JavaScript) modes.
@@ -158,6 +180,11 @@ end
158
180
 
159
181
  Set `click: false` to use hover mode instead of the Stimulus controller.
160
182
 
183
+ Use method `header` to add a non-clickable header item. The method takes an optional `divider` argument. (This is not in the Bulma library.)
184
+
185
+ ```ruby
186
+ dropdown.header("Actions", divider: true)
187
+ ```
161
188
 
162
189
  ### File Upload
163
190
 
@@ -171,7 +198,6 @@ end
171
198
 
172
199
  Set `name: true` to include a file name display element. When enabled, Stimulus data attributes are wired up automatically. The controller is not included in this gem; an [example implementation](https://github.com/RockSolt/bulma-phlex-rails/blob/main/app/javascript/controllers/bulma_phlex/file_input_display_controller.js) is available in the `bulma-phlex-rails` gem.
173
200
 
174
-
175
201
  ### Form Field
176
202
 
177
203
  Renders a [Bulma form field](https://bulma.io/documentation/form/general/#form-field) grouping a label, control, and optional help text. Set the label via the `label` method (string or block) and the input via the `control` method.
@@ -185,7 +211,6 @@ end
185
211
 
186
212
  The `column:` and `grid:` options integrate the field into Bulma's column and grid layout systems. Both accept `true`, a size string, or a breakpoint hash.
187
213
 
188
-
189
214
  ### Grid
190
215
 
191
216
  Renders a [Bulma smart grid](https://bulma.io/documentation/grid/smart-grid/) layout. Supports fixed column counts, auto-count, minimum column width, and independent gap control.
@@ -198,7 +223,6 @@ render BulmaPhlex::Grid.new(minimum_column_width: 16) do
198
223
  end
199
224
  ```
200
225
 
201
-
202
226
  ### Hero
203
227
 
204
228
  Renders a [Bulma hero](https://bulma.io/documentation/layout/hero/) section with support for color and size options.
@@ -215,16 +239,33 @@ render BulmaPhlex::Hero.new(color: "info", size: "large") do |hero|
215
239
  end
216
240
  ```
217
241
 
218
-
219
242
  ### Icon
220
243
 
221
244
  Renders a [Bulma icon](https://bulma.io/documentation/elements/icon/) element. Supports color, size, optional text alongside the icon, and left/right positioning for use inside form controls.
222
245
 
246
+ When text is rendered alongside the icon, Bulma allows the text and icon to wrap by default. Pass `nowrap: true` when they form a compact UI unit that should stay together, such as a sortable table heading, toolbar action, button label, or navigation item. Leave it unset when the text should be allowed to wrap in a constrained container.
247
+
223
248
  ```ruby
224
- render BulmaPhlex::Icon.new("fas fa-user")
225
- render BulmaPhlex::Icon.new("fas fa-home", size: :large, color: :primary, text_right: "Home")
249
+ render BulmaPhlex::Icon.new("fa-solid fa-user")
250
+ render BulmaPhlex::Icon.new("fa-solid fa-home", size: :large, color: :primary, text_right: "Home")
251
+ render BulmaPhlex::Icon.new("fa-solid fa-sort", text_left: "Revenue", nowrap: true)
226
252
  ```
227
253
 
254
+ ### Image
255
+
256
+ Renders a [Bulma image](https://bulma.io/documentation/elements/image/) element. Supports size, ratio, rounded, and additional HTML attributes (on either the wrapping `figure` element or the `img` element).
257
+
258
+ ```ruby
259
+ render BulmaPhlex::Image.new(src: "image.jpg", size: 64)
260
+
261
+ render BulmaPhlex::Image.new(
262
+ src: "image.jpg",
263
+ alt: "An image",
264
+ rounded: true,
265
+ ratio: "16by9",
266
+ img_attributes: { class: "custom-img-class", id: "image-id" },
267
+ class: "custom-class", id: "figure-image-id")
268
+ ```
228
269
 
229
270
  ### Level
230
271
 
@@ -237,6 +278,65 @@ render BulmaPhlex::Level.new do |level|
237
278
  end
238
279
  ```
239
280
 
281
+ ### Media Object
282
+
283
+ Renders a [Bulma media object](https://bulma.io/documentation/layout/media-object/) with an optional left image/icon, right content, and main body content. Each section is populated via builder methods on the yielded component.
284
+
285
+ ```ruby
286
+ render BulmaPhlex::MediaObject.new do |media|
287
+ media.left { img(src: "/avatar.png", alt: "Avatar") }
288
+ media.content { "This is the main content of the media object." }
289
+ media.right { button(class: "button is-small") { "Action" } }
290
+ end
291
+ ```
292
+
293
+ If the left content is an image, method `image` can be used instead of `left` to automatically build the supporting HTML structure.
294
+
295
+ ```ruby
296
+ render BulmaPhlex::MediaObject.new do |media|
297
+ media.image(src: "/avatar.png", alt: "Avatar", size: 64, rounded: true)
298
+ media.content { "This is the main content of the media object." }
299
+ end
300
+ ```
301
+
302
+ ### Menu
303
+
304
+ Renders a [Bulma menu](https://bulma.io/documentation/components/menu/) with support for labels, lists, and nested lists.
305
+
306
+ ```ruby
307
+ render BulmaPhlex::Menu.new do |menu|
308
+ menu.label("General")
309
+ menu.list do
310
+ menu.item("Dashboard", href: "/dashboard", active: true)
311
+ menu.item("Customers", href: "/customers")
312
+ end
313
+
314
+ menu.label("Administration")
315
+ menu.list do
316
+ menu.item("Team Settings", href: "/teams/settings")
317
+ menu.item("Manage Your Team", href: "/teams/dashboard") do
318
+ menu.item("Members", href: "/members")
319
+ menu.item("Plugins", href: "/plugins")
320
+ menu.item("Add a member", href: "/add-member")
321
+ end
322
+ end
323
+ end
324
+ ```
325
+
326
+ Nested lists can be created by passing a block to the `item` method. An expandable section (header opens and closes but is not a link) can be created with the `expandable_item` method.
327
+
328
+ ```ruby
329
+ render BulmaPhlex::Menu.new do |menu|
330
+ menu.label("General")
331
+ menu.list do
332
+ menu.item("Dashboard", href: "/dashboard", active: true)
333
+ menu.expandable_item("Customers") do
334
+ menu.item("Active Customers", href: "/customers/active")
335
+ menu.item("Inactive Customers", href: "/customers/inactive")
336
+ end
337
+ end
338
+ end
339
+ ```
240
340
 
241
341
  ### Message
242
342
 
@@ -248,7 +348,6 @@ render BulmaPhlex::Message.new("Hello World", color: "info", delete: true) do
248
348
  end
249
349
  ```
250
350
 
251
-
252
351
  ### Modal
253
352
 
254
353
  Renders a [Bulma modal](https://bulma.io/documentation/components/modal/) dialog overlay with a background, content area, and close button. Content is provided via a block.
@@ -264,7 +363,6 @@ end
264
363
 
265
364
  The modal is shown by adding the `is-active` class to the container — nothing in the component does this automatically. The default Stimulus controller (`bulma-phlex--modal`) handles open/close behavior. Pass a custom `data_attributes_builder:` to integrate with a different JavaScript setup.
266
365
 
267
-
268
366
  ### NavigationBar
269
367
 
270
368
  Renders a [Bulma navbar](https://bulma.io/documentation/components/navbar/) with support for branding, left/right nav links, and dropdown menus. Collapses automatically on mobile via the `bulma-phlex--navigation-bar` Stimulus controller (provided by the `bulma-phlex-rails` gem).
@@ -287,7 +385,6 @@ end
287
385
  > [!NOTE]
288
386
  > Setting `container: true` (or a constraint string like `"is-max-desktop"`) wraps the navbar content in a Bulma container for fixed-width layout. The navbar's background color still spans the full viewport width.
289
387
 
290
-
291
388
  ### Notification
292
389
 
293
390
  Renders a [Bulma notification](https://bulma.io/documentation/elements/notification/) alert box with support for color and mode options.
@@ -306,7 +403,6 @@ render BulmaPhlex::Notification.new(color: "warning", delete: { data: { action:
306
403
  end
307
404
  ```
308
405
 
309
-
310
406
  ### Pagination
311
407
 
312
408
  Renders the [Bulma pagination](https://bulma.io/documentation/components/pagination/) component with previous/next links, numbered page links, ellipses for skipped ranges, and an item count summary. Only renders when there is more than one page of results.
@@ -317,7 +413,6 @@ render BulmaPhlex::Pagination.new(@products, ->(page) { products_path(page: page
317
413
 
318
414
  The first argument must be a pager object responding to `current_page`, `total_pages`, `per_page`, `total_count`, `previous_page`, and `next_page`.
319
415
 
320
-
321
416
  ### Progress Bar
322
417
 
323
418
  Renders a [Bulma progress bar](https://bulma.io/documentation/elements/progress/) element. Supports color and size options.
@@ -328,7 +423,6 @@ render BulmaPhlex::ProgressBar.new(color: "success", size: "large", value: 75, m
328
423
 
329
424
  Omitting `value:` and `max:` produces an indeterminate (animated) progress bar.
330
425
 
331
-
332
426
  ### Table
333
427
 
334
428
  Renders a [Bulma table](https://bulma.io/documentation/elements/table/) for a collection of records. Columns are defined via builder methods on the yielded component.
@@ -346,7 +440,32 @@ end
346
440
  In addition to `column`, two specialized column methods are available:
347
441
 
348
442
  - `date_column(header, format: "%Y-%m-%d")` — formats the value with `strftime`
349
- - `conditional_icon(header, icon_class: "fas fa-check")` — shows an icon when the block returns truthy
443
+ - `conditional_icon(header, icon_class:)` — shows the configured conditional icon, or the supplied icon, when the block returns truthy
444
+
445
+ #### Sortable columns
446
+
447
+ Pass a sort hash with the URL for the next sort state to add a sortable header. The component renders the sort link, icon, and accessible sort state; your application remains responsible for interpreting the sort parameters and ordering the records.
448
+
449
+ ```ruby
450
+ render BulmaPhlex::Table.new(@users) do |table|
451
+ table.column(
452
+ "Name",
453
+ sort: { href: users_path(sort: "name") }
454
+ ) { |user| user.full_name }
455
+
456
+ table.date_column(
457
+ "Joined",
458
+ sort: {
459
+ href: users_path(sort: "created_at"),
460
+ current_direction: :descending
461
+ }
462
+ ) { |user| user.created_at }
463
+ end
464
+ ```
465
+
466
+ Set `current_direction:` to `:ascending` or `:descending` for the currently active column. Pass additional attributes for the generated link with `link_attributes:`. The component generates an accessible label from the column header and sort state.
467
+
468
+ #### Pagination
350
469
 
351
470
  To add pagination to the table, call `paginate` with a block that returns a path given a page number:
352
471
 
@@ -354,20 +473,23 @@ To add pagination to the table, call `paginate` with a block that returns a path
354
473
  table.paginate { |page| products_path(page: page) }
355
474
  ```
356
475
 
357
- Pass HTML attributes to the `tr` elements via the `row` method, using either keyword arguments or a block
476
+ #### Custom row attributes
477
+
478
+ Pass HTML attributes to the `tr` elements via the `row` method, using either keyword arguments or a block
358
479
  that receives the record for the row:
359
480
 
360
481
  ```ruby
361
482
  table.row(class: "custom-row-class") { |row| { id: "row-id-#{row.id}" } }
362
483
  ```
363
484
 
485
+ #### Responsive columns
486
+
364
487
  Hide columns on smaller screens with the column `hidden` option:
365
488
 
366
489
  ```ruby
367
490
  table.column("Email", hidden: { mobile: true }) { |user| user.email }
368
491
  ```
369
492
 
370
-
371
493
  ### Tabs
372
494
 
373
495
  Renders a [Bulma tabs](https://bulma.io/documentation/components/tabs/) component with tabbed navigation and associated content panels. Supports alignment, size, and style options (boxed, toggle, rounded, fullwidth).
@@ -375,7 +497,7 @@ Renders a [Bulma tabs](https://bulma.io/documentation/components/tabs/) componen
375
497
  ```ruby
376
498
  render BulmaPhlex::Tabs.new(boxed: true) do |tabs|
377
499
  tabs.tab(id: "profile", title: "Profile", active: true) { "Profile content" }
378
- tabs.tab(id: "settings", title: "Settings", icon: "fas fa-cog") { "Settings content" }
500
+ tabs.tab(id: "settings", title: "Settings", icon: "fa-solid fa-gear") { "Settings content" }
379
501
  end
380
502
  ```
381
503
 
@@ -383,7 +505,6 @@ Tab switching is handled by the `bulma-phlex--tabs` Stimulus controller. The con
383
505
 
384
506
  An optional right-side element (e.g. a button) can be placed alongside the tab bar via `right_content`.
385
507
 
386
-
387
508
  ### Tag
388
509
 
389
510
  Renders the [Bulma tag](https://bulma.io/documentation/elements/tag/) component. Supports color, size, rounded, and an optional inline delete button.
@@ -395,7 +516,6 @@ render BulmaPhlex::Tag.new("Sale", light: "danger")
395
516
 
396
517
  The element type is inferred from the attributes: an `href:` produces an `<a>`, a `data-action` or `delete: true` produces a `<button>`, and otherwise a `<span>` is used.
397
518
 
398
-
399
519
  ### Title and Subtitle
400
520
 
401
521
  Renders a [Bulma title](https://bulma.io/documentation/elements/title/) with an optional subtitle. Supports sizes 1–6 for both elements and a `spaced:` option to increase the gap between them.
@@ -407,13 +527,12 @@ render BulmaPhlex::Title.new("Dr. Strangelove", size: 2, subtitle: "Or: How I Le
407
527
 
408
528
  When `size:` is set but `subtitle_size:` is not, the subtitle size defaults to `size + 2`.
409
529
 
410
-
411
530
  ## Upgrading to 0.8
412
531
 
413
532
  > [!IMPORTANT]
414
533
  > The 0.8.0 release includes breaking changes. The namespace for the components has been changed from `Components::Bulma` to `Components::BulmaPhlex`.
415
534
 
416
- This can generally be handled with a *Find-and-Replace*:
535
+ This can generally be handled with a _Find-and-Replace_:
417
536
 
418
537
  **Find:** `Bulma::`
419
538
  **Replace:** `BulmaPhlex::`
@@ -422,7 +541,6 @@ Note that this change was also applied to the expected Stimulus controllers. Ref
422
541
 
423
542
  Finally, the optional Rails extensions for the Card and Table components have been moved to the `bulma-phlex-rails` gem. If you are using this with Rails, you should use [the `bulma-phlex-rails` gem](https://github.com/RockSolt/bulma-phlex-rails). It provides both the component extensions as well as JavaScript for the Dropdown, Navigation Bar, and Tabs components.
424
543
 
425
-
426
544
  ## Development
427
545
 
428
546
  After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -3,7 +3,7 @@
3
3
  module BulmaPhlex
4
4
  # Renders a [Bulma card](https://bulma.io/documentation/components/card/) component.
5
5
  #
6
- # Supports an optional **header** (with title and custom classes), a **content** area, and a
6
+ # Supports an optional **header** (with title and custom classes), an **image**, a **content** area, and a
7
7
  # **footer** with one or more link items (which can include icons). Each section is populated
8
8
  # via builder methods on the yielded component.
9
9
  #
@@ -32,8 +32,9 @@ module BulmaPhlex
32
32
  def view_template(&)
33
33
  vanish(&)
34
34
 
35
- div(**mix(class: "card", **@html_attributes)) do
35
+ div(**mix({ class: "card" }, **@html_attributes)) do
36
36
  card_header
37
+ card_image
37
38
  card_content
38
39
  card_footer
39
40
  end
@@ -44,8 +45,28 @@ module BulmaPhlex
44
45
  # - `title` — The text to display in the card header
45
46
  # - `classes` — Optional additional CSS classes for the header element
46
47
  def head(title, classes: nil)
48
+ warn "[DEPRECATION] `BulmaPhlex::Card#head` is deprecated. Use `header` instead.", category: :deprecated
49
+
50
+ header(title, class: classes)
51
+ end
52
+
53
+ # in order to use the method name `header`, we need to grab a reference to the method on the base class
54
+ # so it is still available to us
55
+ alias html_header header
56
+
57
+ # Sets the card header. Pass in the title as a string and/or provide a block to render custom content
58
+ # in the header.
59
+ #
60
+ # - `title` — The text to display in the card header (optional)
61
+ # - `**html_attributes` — Additional HTML attributes for the header element (optional)
62
+ def header(title = nil, **html_attributes, &block)
47
63
  @header_title = title
48
- @header_classes = classes
64
+ @header_attributes = html_attributes
65
+ @header_content = block
66
+ end
67
+
68
+ def image(src:, alt: nil, size: nil, rounded: false)
69
+ @image = BulmaPhlex::Image.new(src:, alt:, size:, rounded:)
49
70
  end
50
71
 
51
72
  # Sets the card body content.
@@ -59,7 +80,7 @@ module BulmaPhlex
59
80
  #
60
81
  # - `text` — The link label text
61
82
  # - `href` — The URL the link points to
62
- # - `icon:` — Optional icon class string (e.g. `"fas fa-edit"`) to render an icon alongside the text
83
+ # - `icon:` — Optional icon class string (e.g. `"fa-solid fa-pen-to-square"`) to render an icon alongside the text
63
84
  # - `**html_attributes` — Additional HTML attributes for the `<a>` element (e.g. `target:`, `class:`)
64
85
  def footer_link(text, href, **html_attributes)
65
86
  (@footer_items ||= []) << [text, href, html_attributes]
@@ -74,10 +95,19 @@ module BulmaPhlex
74
95
  private
75
96
 
76
97
  def card_header
77
- return if @header_title.nil?
98
+ return if @header_title.nil? && @header_content.nil?
99
+
100
+ html_header(**mix({ class: "card-header" }, @header_attributes)) do
101
+ p(class: "card-header-title") { plain @header_title } if @header_title
102
+ @header_content&.call
103
+ end
104
+ end
105
+
106
+ def card_image
107
+ return if @image.nil?
78
108
 
79
- header(class: "card-header #{@header_classes}") do
80
- p(class: "card-header-title") { plain @header_title }
109
+ div(class: "card-image") do
110
+ render @image
81
111
  end
82
112
  end
83
113
 
@@ -85,7 +115,7 @@ module BulmaPhlex
85
115
  return if @card_content.nil?
86
116
 
87
117
  div(class: "card-content") do
88
- div(class: "content") { @card_content.call }
118
+ @card_content.call
89
119
  end
90
120
  end
91
121
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BulmaPhlex
4
+ # Stores application-wide defaults for component-owned icons.
5
+ class Configuration
6
+ # Stores the icon classes used by components when no local override is supplied.
7
+ class Icons
8
+ attr_accessor :sort, :dropdown, :file_upload, :conditional
9
+
10
+ def initialize
11
+ @sort = {
12
+ ascending: "fa-solid fa-sort-up",
13
+ descending: "fa-solid fa-sort-down",
14
+ inactive: "fa-solid fa-sort"
15
+ }
16
+ @dropdown = "fa-solid fa-angle-down"
17
+ @file_upload = "fa-solid fa-upload"
18
+ @conditional = "fa-solid fa-check"
19
+ end
20
+ end
21
+
22
+ attr_reader :icons
23
+
24
+ def initialize
25
+ @icons = Icons.new
26
+ end
27
+ end
28
+ end
@@ -23,19 +23,19 @@ module BulmaPhlex
23
23
  # - `label` — The text displayed in the dropdown trigger button
24
24
  # - `click` — Stimulus controller name for toggling; set to `false` for hover mode instead
25
25
  # - `alignment` — Alignment of the dropdown menu: `"left"` (default), `"right"`, or `"up"`
26
- # - `icon` — Icon class for the trigger button (default: `"fas fa-angle-down"`)
26
+ # - `icon` — Icon class for the trigger button (default: configured dropdown icon)
27
27
  # - `**html_attributes` — Additional HTML attributes for the outermost dropdown element
28
- def self.new(label, click: "bulma-phlex--dropdown", alignment: "left", icon: "fas fa-angle-down",
28
+ def self.new(label, click: "bulma-phlex--dropdown", alignment: "left", icon: nil,
29
29
  **html_attributes)
30
30
  super
31
31
  end
32
32
 
33
- def initialize(label, click: "bulma-phlex--dropdown", alignment: "left", icon: "fas fa-angle-down",
33
+ def initialize(label, click: "bulma-phlex--dropdown", alignment: "left", icon: nil,
34
34
  **html_attributes)
35
35
  @label = label
36
36
  @click = click
37
37
  @alignment = alignment
38
- @icon = icon
38
+ @icon = icon || BulmaPhlex.config.icons.dropdown
39
39
  @html_attributes = html_attributes
40
40
  end
41
41
 
@@ -61,20 +61,28 @@ module BulmaPhlex
61
61
  end
62
62
  end
63
63
 
64
- def item(content = nil, &)
64
+ # Adds a non-clickable header item to the dropdown menu. Optionally add a divider before the header with
65
+ # the `divder: true` parameter.
66
+ def header(label, divider: false)
67
+ self.divider if divider
68
+ div(class: "dropdown-item has-text-weight-semibold is-unselectable") { label }
69
+ end
70
+
71
+ def item(content = nil, **html_attributes, &)
72
+ attributes = mix({ class: "dropdown-item" }, html_attributes)
65
73
  if block_given?
66
- div(class: "dropdown-item", &)
74
+ div(**attributes, &)
67
75
  else
68
- div(class: "dropdown-item") { content }
76
+ div(**attributes) { content }
69
77
  end
70
78
  end
71
79
 
72
- def link(label, path)
73
- a(class: "dropdown-item", href: path) { label }
80
+ def link(label, path, **html_attributes)
81
+ a(**mix({ class: "dropdown-item" }, html_attributes), href: path) { label }
74
82
  end
75
83
 
76
- def divider
77
- hr(class: "dropdown-divider")
84
+ def divider(**html_attributes)
85
+ hr(**mix({ class: "dropdown-divider" }, html_attributes))
78
86
  end
79
87
 
80
88
  private
@@ -25,6 +25,7 @@ module BulmaPhlex
25
25
  # - `align` — Aligns the file input: `"right"` or `"centered"`
26
26
  # - `fullwidth` — If `true`, makes the file input full width
27
27
  # - `boxed` — If `true`, makes the file input boxed
28
+ # - `icon` — Icon class for the upload control (default: configured file upload icon)
28
29
  # - `data_attributes_builder` — A custom builder for the data attributes used for Stimulus integration;
29
30
  # defaults to a builder with the controller name `"bulma-phlex--file-input-display"`
30
31
  def self.new(color: nil,
@@ -33,6 +34,7 @@ module BulmaPhlex
33
34
  align: nil,
34
35
  fullwidth: false,
35
36
  boxed: false,
37
+ icon: nil,
36
38
  data_attributes_builder: nil)
37
39
  super
38
40
  end
@@ -43,6 +45,7 @@ module BulmaPhlex
43
45
  align: nil,
44
46
  fullwidth: false,
45
47
  boxed: false,
48
+ icon: nil,
46
49
  data_attributes_builder: nil)
47
50
  @color = color
48
51
  @size = size
@@ -50,6 +53,7 @@ module BulmaPhlex
50
53
  @align = align
51
54
  @fullwidth = fullwidth
52
55
  @boxed = boxed
56
+ @icon = icon || BulmaPhlex.config.icons.file_upload
53
57
 
54
58
  return unless @name
55
59
 
@@ -63,7 +67,7 @@ module BulmaPhlex
63
67
  yield(@data_attributes_builder&.for_file_input)
64
68
  span(class: "file-cta") do
65
69
  span(class: "file-icon") do
66
- i(class: "fas fa-upload")
70
+ i(class: @icon)
67
71
  end
68
72
  span(class: "file-label") { plain " Choose a file… " }
69
73
  end
@@ -6,8 +6,8 @@ module BulmaPhlex
6
6
  class FormControl < Base
7
7
  # **Parameters**
8
8
  #
9
- # - `icon_left` — Icon class for the left side of the control (e.g. `"fas fa-check"`)
10
- # - `icon_right` — Icon class for the right side of the control (e.g. `"fas fa-check"`)
9
+ # - `icon_left` — Icon class for the left side of the control (e.g. `"fa-solid fa-check"`)
10
+ # - `icon_right` — Icon class for the right side of the control (e.g. `"fa-solid fa-check"`)
11
11
  # - `**html_attributes` — Additional HTML attributes for the control element
12
12
  def self.new(icon_left: nil, icon_right: nil, **html_attributes)
13
13
  super
@@ -21,8 +21,8 @@ module BulmaPhlex
21
21
  # **Parameters**
22
22
  #
23
23
  # - `help` — Optional help text displayed below the input
24
- # - `icon_left` — Icon class for an icon to the left of the input (e.g. `"fas fa-user"`)
25
- # - `icon_right` — Icon class for an icon to the right of the input (e.g. `"fas fa-check"`)
24
+ # - `icon_left` — Icon class for an icon to the left of the input (e.g. `"fa-solid fa-user"`)
25
+ # - `icon_right` — Icon class for an icon to the right of the input (e.g. `"fa-solid fa-check"`)
26
26
  # - `column` — If `true`, makes the field a column; a size string (e.g. `"half"`) sets the size for
27
27
  # all breakpoints; a hash (e.g. `{ mobile: "full", desktop: "half" }`) sets responsive sizes
28
28
  # - `grid` — If `true`, makes the field a grid cell; a size string sets the cell size