view_primitives 0.2.3 → 0.2.4

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 (20) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/lib/generators/view_primitives/add/templates/alert_dialog/alert_dialog_component.rb.tt +4 -1
  4. data/lib/generators/view_primitives/add/templates/combobox/combobox_component.rb.tt +31 -12
  5. data/lib/generators/view_primitives/add/templates/combobox/combobox_controller.js +69 -4
  6. data/lib/generators/view_primitives/add/templates/command/command_component.rb.tt +4 -1
  7. data/lib/generators/view_primitives/add/templates/dialog/dialog_component.rb.tt +4 -1
  8. data/lib/generators/view_primitives/add/templates/drawer/drawer_component.rb.tt +4 -1
  9. data/lib/generators/view_primitives/add/templates/dropdown_menu/dropdown_controller.js +8 -3
  10. data/lib/generators/view_primitives/add/templates/dropdown_menu/dropdown_menu_component.rb.tt +15 -1
  11. data/lib/generators/view_primitives/add/templates/hover_card/hover_card_component.rb.tt +1 -1
  12. data/lib/generators/view_primitives/add/templates/menubar/menubar_controller.js +10 -5
  13. data/lib/generators/view_primitives/add/templates/menubar/menubar_menu_component.rb.tt +11 -1
  14. data/lib/generators/view_primitives/add/templates/popover/popover_component.rb.tt +14 -1
  15. data/lib/generators/view_primitives/add/templates/popover/popover_controller.js +8 -3
  16. data/lib/generators/view_primitives/add/templates/sheet/sheet_component.rb.tt +4 -1
  17. data/lib/generators/view_primitives/add/templates/sidebar/sidebar_component.rb.tt +8 -2
  18. data/lib/generators/view_primitives/add/templates/sidebar/sidebar_controller.js +10 -3
  19. data/lib/view_primitives/version.rb +1 -1
  20. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf3e09110faf5ce30ed81d7760ab59bbf397fcdd1bfd907748e481d2ad31ae82
4
- data.tar.gz: bfc5afe878bd677f57a02cf6a69169125a081490702f3194cd95113b0a8901ad
3
+ metadata.gz: 8a5109ecc704c72bc80c4ae941a18ddfeb527aa2fc0a16b2608727837877d5fd
4
+ data.tar.gz: 82e5650f28e6c1d13cd42b12026fdf3d770e0ef0e036382165fd39e02b3b1dcc
5
5
  SHA512:
6
- metadata.gz: c3f2433c648728f95e0dceb2f22352866d7e868aff1b302527d6cc16298bd4251dae28a6c2753b91fd67fb4511edf5b6c38d1dc71a3f7ff1384bb65c1c93a55d
7
- data.tar.gz: 10711ae4e2f413503f34621b3a3c0ffcf77b0ea80e9162fd2a38e2d2265690eaed83d16dccfd84b38d79d4c686f55eabe502e6c661cfb507c1379404cc12849e
6
+ metadata.gz: a65f979e1a4dd2b36870109dc289ffde247517da993b2a09d51281a013a16a4af3df8871437bd9b7ebffa306551a90a0465f92710a3b4af8edb25860843651d6
7
+ data.tar.gz: dccadc722af4f1a3fe69202f1442174966598d4bfc5269c524319b8b23bf3ced177651a518b62ad976085d465546ace0601912cbf1df53acb0a6f97263c0b29d
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.4] - 2026-09-18
11
+
12
+ ### Fixed
13
+
14
+ - **DropdownMenu** trigger now carries `aria-haspopup="menu"`, `aria-controls`, and syncs `aria-expanded` on open/close/click-outside — screen readers previously had no way to know the trigger toggled a menu (#10)
15
+ - **Sidebar** collapse toggle carries `aria-expanded`/`aria-controls`, kept in sync by the Stimulus controller
16
+ - **Menubar** menu triggers carry `aria-haspopup="menu"`, `aria-controls`, and synced `aria-expanded`, matching the pattern already used by `MegaMenu`/`NavigationMenu`/`Navbar`
17
+ - **Popover** trigger carries `aria-controls` and a synced `aria-expanded`, same pattern as `DropdownMenu`
18
+ - **Dialog**, **AlertDialog**, **Drawer**, **Sheet**, **Command** triggers carry `aria-haspopup="dialog"`
19
+ - **HoverCard** content now also reveals on keyboard focus (`:focus-within`), not just mouse hover
20
+ - **Combobox** input carries the full ARIA 1.2 combobox pattern — `role="combobox"`, `aria-autocomplete`, `aria-expanded`, `aria-controls`, `aria-activedescendant` — and the option list is keyboard-navigable with <kbd>↑</kbd>/<kbd>↓</kbd>/<kbd>Enter</kbd>/<kbd>Esc</kbd>
21
+
10
22
  ## [0.2.3] - 2026-09-02
11
23
 
12
24
  ### Fixed
@@ -19,7 +19,10 @@ module UI
19
19
 
20
20
  def call
21
21
  content_tag(:div, data: { controller: "dialog" }, **@html_attrs) do
22
- concat content_tag(:span, trigger, data: { action: "click->dialog#open" }, class: "contents") if trigger
22
+ concat content_tag(:span, trigger,
23
+ class: "contents",
24
+ "aria-haspopup": "dialog",
25
+ data: { action: "click->dialog#open" }) if trigger
23
26
  concat panel
24
27
  end
25
28
  end
@@ -5,7 +5,8 @@ module UI
5
5
  INPUT = UI::Styles::INPUT
6
6
  PANEL = "#{UI::Styles::POPOVER_PANEL} top-full left-0 mt-1 w-full overflow-hidden"
7
7
  LIST = "max-h-[200px] overflow-y-auto p-1"
8
- OPTION = "#{UI::Styles::MENU_ITEM} w-full cursor-pointer hover:bg-accent hover:text-accent-foreground"
8
+ OPTION = "#{UI::Styles::MENU_ITEM} w-full cursor-pointer hover:bg-accent hover:text-accent-foreground " \
9
+ "data-[active=true]:bg-accent data-[active=true]:text-accent-foreground"
9
10
  EMPTY = "py-4 text-center text-sm text-muted-foreground"
10
11
 
11
12
  def initialize(name:, options: [], value: nil, placeholder: "Select...", **html_attrs)
@@ -41,13 +42,19 @@ module UI
41
42
  selected_label = @options.find { |o| o[:value].to_s == @value }&.dig(:label)
42
43
  tag.input(
43
44
  type: "text",
45
+ role: "combobox",
44
46
  placeholder: @placeholder,
45
47
  value: selected_label,
46
48
  autocomplete: "off",
49
+ "aria-autocomplete": "list",
50
+ "aria-expanded": "false",
51
+ "aria-controls": list_id,
47
52
  class: INPUT,
48
53
  data: {
49
54
  combobox_target: "input",
50
- action: "focus->combobox#open input->combobox#filter"
55
+ action: "focus->combobox#open input->combobox#filter " \
56
+ "keydown.down->combobox#next keydown.up->combobox#prev " \
57
+ "keydown.enter->combobox#choose keydown.esc->combobox#close"
51
58
  }
52
59
  )
53
60
  end
@@ -57,20 +64,24 @@ module UI
57
64
  data: { combobox_target: "panel" },
58
65
  hidden: true,
59
66
  class: PANEL) do
60
- concat content_tag(:div, class: LIST) {
61
- concat options_list
62
- concat content_tag(:div, "No results.",
63
- class: EMPTY,
64
- data: { combobox_target: "empty" },
65
- hidden: true)
66
- }
67
+ concat content_tag(:div, options_list,
68
+ id: list_id,
69
+ role: "listbox",
70
+ class: LIST)
71
+ concat content_tag(:div, "No results.",
72
+ class: EMPTY,
73
+ role: "status",
74
+ data: { combobox_target: "empty" },
75
+ hidden: true)
67
76
  end
68
77
  end
69
78
 
70
79
  def options_list
71
- safe_join(@options.map { |opt|
72
- content_tag(:button, opt[:label],
73
- type: "button",
80
+ safe_join(@options.each_with_index.map { |opt, index|
81
+ content_tag(:div, opt[:label],
82
+ id: option_id(index),
83
+ role: "option",
84
+ "aria-selected": (opt[:value].to_s == @value).to_s,
74
85
  class: OPTION,
75
86
  data: {
76
87
  combobox_target: "option",
@@ -80,5 +91,13 @@ module UI
80
91
  })
81
92
  })
82
93
  end
94
+
95
+ def option_id(index)
96
+ "#{list_id}-option-#{index}"
97
+ end
98
+
99
+ def list_id
100
+ @list_id ||= "combobox-list-#{object_id}"
101
+ end
83
102
  end
84
103
  end
@@ -3,13 +3,24 @@ import { Controller } from "@hotwired/stimulus"
3
3
  export default class extends Controller {
4
4
  static targets = ["input", "hidden", "panel", "option", "empty"]
5
5
 
6
+ connect() {
7
+ this.activeIndex = -1
8
+ }
9
+
10
+ get visibleOptions() {
11
+ return this.optionTargets.filter(o => !o.hidden)
12
+ }
13
+
6
14
  open() {
15
+ this.inputTarget.setAttribute("aria-expanded", "true")
7
16
  this.panelTarget.hidden = false
8
17
  this.filter()
9
18
  }
10
19
 
11
20
  close() {
21
+ this.inputTarget.setAttribute("aria-expanded", "false")
12
22
  this.panelTarget.hidden = true
23
+ this._setActive(-1)
13
24
  const selected = this.optionTargets.find(o => o.dataset.comboboxValue === this.hiddenTarget.value)
14
25
  this.inputTarget.value = selected ? selected.dataset.comboboxLabel : ""
15
26
  }
@@ -23,16 +34,70 @@ export default class extends Controller {
23
34
  if (match) visible++
24
35
  })
25
36
  this.emptyTarget.hidden = visible > 0
37
+ this._setActive(-1)
38
+ }
39
+
40
+ next(event) {
41
+ event.preventDefault()
42
+ this._move(1)
43
+ }
44
+
45
+ prev(event) {
46
+ event.preventDefault()
47
+ this._move(-1)
48
+ }
49
+
50
+ choose(event) {
51
+ if (this.panelTarget.hidden) return
52
+ event.preventDefault()
53
+ const option = this.visibleOptions[this.activeIndex]
54
+ if (option) this._select(option)
26
55
  }
27
56
 
28
57
  select(event) {
29
- const { comboboxValue, comboboxLabel } = event.currentTarget.dataset
30
- this.hiddenTarget.value = comboboxValue
31
- this.inputTarget.value = comboboxLabel
32
- this.panelTarget.hidden = true
58
+ this._select(event.currentTarget)
33
59
  }
34
60
 
35
61
  closeOnClickOutside({ target }) {
36
62
  if (!this.element.contains(target)) this.close()
37
63
  }
64
+
65
+ _move(step) {
66
+ if (this.panelTarget.hidden) {
67
+ this.open()
68
+ return
69
+ }
70
+
71
+ const options = this.visibleOptions
72
+ if (options.length === 0) return
73
+
74
+ const next = this.activeIndex === -1
75
+ ? (step > 0 ? 0 : options.length - 1)
76
+ : (this.activeIndex + step + options.length) % options.length
77
+
78
+ this._setActive(next)
79
+ }
80
+
81
+ _setActive(index) {
82
+ const options = this.visibleOptions
83
+ options.forEach(o => { o.dataset.active = "false" })
84
+
85
+ this.activeIndex = index
86
+ const active = options[index]
87
+ if (active) {
88
+ active.dataset.active = "true"
89
+ active.scrollIntoView({ block: "nearest" })
90
+ this.inputTarget.setAttribute("aria-activedescendant", active.id)
91
+ } else {
92
+ this.inputTarget.removeAttribute("aria-activedescendant")
93
+ }
94
+ }
95
+
96
+ _select(option) {
97
+ const { comboboxValue, comboboxLabel } = option.dataset
98
+ this.hiddenTarget.value = comboboxValue
99
+ this.inputTarget.value = comboboxLabel
100
+ this.optionTargets.forEach(o => o.setAttribute("aria-selected", String(o === option)))
101
+ this.close()
102
+ }
38
103
  }
@@ -34,7 +34,10 @@ module UI
34
34
 
35
35
  def call
36
36
  content_tag(:div, data: { controller: "command" }, **@html_attrs) do
37
- concat content_tag(:span, trigger, data: { action: "click->command#open" }, class: "contents") if trigger
37
+ concat content_tag(:span, trigger,
38
+ class: "contents",
39
+ "aria-haspopup": "dialog",
40
+ data: { action: "click->command#open" }) if trigger
38
41
  concat panel
39
42
  end
40
43
  end
@@ -22,7 +22,10 @@ module UI
22
22
 
23
23
  def call
24
24
  content_tag(:div, data: { controller: "dialog" }, **@html_attrs) do
25
- concat content_tag(:span, trigger, data: { action: "click->dialog#open" }, class: "contents") if trigger
25
+ concat content_tag(:span, trigger,
26
+ class: "contents",
27
+ "aria-haspopup": "dialog",
28
+ data: { action: "click->dialog#open" }) if trigger
26
29
  concat panel
27
30
  end
28
31
  end
@@ -18,7 +18,10 @@ module UI
18
18
 
19
19
  def call
20
20
  content_tag(:div, data: { controller: "dialog" }, **@html_attrs) do
21
- concat content_tag(:span, trigger, data: { action: "click->dialog#open" }, class: "contents") if trigger
21
+ concat content_tag(:span, trigger,
22
+ class: "contents",
23
+ "aria-haspopup": "dialog",
24
+ data: { action: "click->dialog#open" }) if trigger
22
25
  concat panel
23
26
  end
24
27
  end
@@ -1,17 +1,22 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["panel"]
4
+ static targets = ["trigger", "panel"]
5
5
 
6
6
  toggle() {
7
- this.panelTarget.hidden = !this.panelTarget.hidden
7
+ this._setOpen(this.panelTarget.hidden)
8
8
  }
9
9
 
10
10
  close() {
11
- this.panelTarget.hidden = true
11
+ this._setOpen(false)
12
12
  }
13
13
 
14
14
  closeOnClickOutside({ target }) {
15
15
  if (!this.element.contains(target)) this.close()
16
16
  }
17
+
18
+ _setOpen(open) {
19
+ this.panelTarget.hidden = !open
20
+ if (this.hasTriggerTarget) this.triggerTarget.setAttribute("aria-expanded", String(open))
21
+ }
17
22
  }
@@ -33,20 +33,34 @@ module UI
33
33
  action: "click@document->dropdown#closeOnClickOutside"
34
34
  },
35
35
  **@html_attrs) do
36
- concat content_tag(:span, trigger, data: { action: "click->dropdown#toggle" }, class: "contents") if trigger
36
+ concat trigger_wrapper if trigger
37
37
  concat panel
38
38
  end
39
39
  end
40
40
 
41
41
  private
42
42
 
43
+ def trigger_wrapper
44
+ content_tag(:span, trigger,
45
+ class: "contents",
46
+ data: { dropdown_target: "trigger", action: "click->dropdown#toggle" },
47
+ "aria-haspopup": "menu",
48
+ "aria-expanded": "false",
49
+ "aria-controls": panel_id)
50
+ end
51
+
43
52
  def panel
44
53
  content_tag(:div,
54
+ id: panel_id,
45
55
  data: { dropdown_target: "panel" },
46
56
  hidden: true,
47
57
  class: cn(PANEL, ALIGN.fetch(@align, ALIGN[:start]), @extra_class)) do
48
58
  concat content
49
59
  end
50
60
  end
61
+
62
+ def panel_id
63
+ @panel_id ||= "dropdown-panel-#{object_id}"
64
+ end
51
65
  end
52
66
  end
@@ -5,7 +5,7 @@ module UI
5
5
  renders_one :trigger
6
6
 
7
7
  CARD_BASE = "#{UI::Styles::POPOVER_PANEL} w-64 p-4 " \
8
- "opacity-0 group-hover:opacity-100 pointer-events-none " \
8
+ "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 pointer-events-none " \
9
9
  "transition-opacity duration-200"
10
10
 
11
11
  POSITIONS = {
@@ -1,7 +1,7 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["menu", "panel"]
4
+ static targets = ["menu", "trigger", "panel"]
5
5
 
6
6
  get openIndex() {
7
7
  return this.panelTargets.findIndex(p => !p.hidden)
@@ -10,10 +10,9 @@ export default class extends Controller {
10
10
  toggle(event) {
11
11
  const menu = event.currentTarget.closest("[data-menubar-target='menu']")
12
12
  const index = this.menuTargets.indexOf(menu)
13
- const panel = this.panelTargets[index]
14
- const wasOpen = !panel.hidden
13
+ const wasOpen = !this.panelTargets[index].hidden
15
14
  this.closeAll()
16
- if (!wasOpen) panel.hidden = false
15
+ if (!wasOpen) this._open(index)
17
16
  }
18
17
 
19
18
  openOnHover(event) {
@@ -21,14 +20,20 @@ export default class extends Controller {
21
20
  const menu = event.currentTarget.closest("[data-menubar-target='menu']")
22
21
  const index = this.menuTargets.indexOf(menu)
23
22
  this.closeAll()
24
- this.panelTargets[index].hidden = false
23
+ this._open(index)
25
24
  }
26
25
 
27
26
  closeAll() {
28
27
  this.panelTargets.forEach(p => p.hidden = true)
28
+ this.triggerTargets.forEach(t => t.setAttribute("aria-expanded", "false"))
29
29
  }
30
30
 
31
31
  closeOnClickOutside({ target }) {
32
32
  if (!this.element.contains(target)) this.closeAll()
33
33
  }
34
+
35
+ _open(index) {
36
+ this.panelTargets[index].hidden = false
37
+ this.triggerTargets[index].setAttribute("aria-expanded", "true")
38
+ }
34
39
  }
@@ -20,8 +20,12 @@ module UI
20
20
  @label,
21
21
  type: "button",
22
22
  class: TRIGGER,
23
- data: { action: "click->menubar#toggle mouseenter->menubar#openOnHover" })
23
+ "aria-haspopup": "menu",
24
+ "aria-expanded": "false",
25
+ "aria-controls": panel_id,
26
+ data: { menubar_target: "trigger", action: "click->menubar#toggle mouseenter->menubar#openOnHover" })
24
27
  concat content_tag(:div,
28
+ id: panel_id,
25
29
  data: { menubar_target: "panel" },
26
30
  hidden: true,
27
31
  class: cn(PANEL, @extra_class)) {
@@ -29,5 +33,11 @@ module UI
29
33
  }
30
34
  end
31
35
  end
36
+
37
+ private
38
+
39
+ def panel_id
40
+ @panel_id ||= "menubar-panel-#{object_id}"
41
+ end
32
42
  end
33
43
  end
@@ -31,15 +31,24 @@ module UI
31
31
  class: "relative inline-block",
32
32
  data: { controller: "popover", action: "click@document->popover#closeOnClickOutside" },
33
33
  **@html_attrs) do
34
- concat content_tag(:span, trigger, data: { action: "click->popover#toggle" }, class: "contents") if trigger
34
+ concat trigger_wrapper if trigger
35
35
  concat panel
36
36
  end
37
37
  end
38
38
 
39
39
  private
40
40
 
41
+ def trigger_wrapper
42
+ content_tag(:span, trigger,
43
+ class: "contents",
44
+ data: { popover_target: "trigger", action: "click->popover#toggle" },
45
+ "aria-expanded": "false",
46
+ "aria-controls": panel_id)
47
+ end
48
+
41
49
  def panel
42
50
  content_tag(:div,
51
+ id: panel_id,
43
52
  class: cn(
44
53
  PANEL_BASE,
45
54
  ALIGN.fetch(@align, ALIGN[:start]),
@@ -51,5 +60,9 @@ module UI
51
60
  content
52
61
  end
53
62
  end
63
+
64
+ def panel_id
65
+ @panel_id ||= "popover-panel-#{object_id}"
66
+ end
54
67
  end
55
68
  end
@@ -1,17 +1,22 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["panel"]
4
+ static targets = ["trigger", "panel"]
5
5
 
6
6
  toggle() {
7
- this.panelTarget.hidden = !this.panelTarget.hidden
7
+ this._setOpen(this.panelTarget.hidden)
8
8
  }
9
9
 
10
10
  close() {
11
- this.panelTarget.hidden = true
11
+ this._setOpen(false)
12
12
  }
13
13
 
14
14
  closeOnClickOutside({ target }) {
15
15
  if (!this.element.contains(target)) this.close()
16
16
  }
17
+
18
+ _setOpen(open) {
19
+ this.panelTarget.hidden = !open
20
+ if (this.hasTriggerTarget) this.triggerTarget.setAttribute("aria-expanded", String(open))
21
+ }
17
22
  }
@@ -30,7 +30,10 @@ module UI
30
30
 
31
31
  def call
32
32
  content_tag(:div, data: { controller: "dialog" }, **@html_attrs) do
33
- concat content_tag(:span, trigger, data: { action: "click->dialog#open" }, class: "contents") if trigger
33
+ concat content_tag(:span, trigger,
34
+ class: "contents",
35
+ "aria-haspopup": "dialog",
36
+ data: { action: "click->dialog#open" }) if trigger
34
37
  concat panel
35
38
  end
36
39
  end
@@ -82,17 +82,23 @@ module UI
82
82
  content_tag(:button, type: "button",
83
83
  class: TOGGLE_CLS,
84
84
  "aria-label": "Toggle sidebar",
85
- data: { action: "click->sidebar#toggle" }) { chevron_icon }
85
+ "aria-expanded": (!@collapsed).to_s,
86
+ "aria-controls": nav_id,
87
+ data: { action: "click->sidebar#toggle", sidebar_target: "toggle" }) { chevron_icon }
86
88
  end
87
89
 
88
90
  def nav_body
89
- content_tag(:nav, class: NAV_CLS) do
91
+ content_tag(:nav, id: nav_id, class: NAV_CLS) do
90
92
  concat safe_join(groups) if groups.any?
91
93
  concat content_tag(:div, safe_join(items), class: "flex flex-col gap-0.5") if items.any?
92
94
  concat content if content?
93
95
  end
94
96
  end
95
97
 
98
+ def nav_id
99
+ @nav_id ||= "sidebar-nav-#{object_id}"
100
+ end
101
+
96
102
  def chevron_icon
97
103
  content_tag(:svg,
98
104
  content_tag(:path, nil, d: "m15 18-6-6 6-6", "stroke-linecap": "round", "stroke-linejoin": "round"),
@@ -1,11 +1,18 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
+ static targets = ["toggle"]
5
+
4
6
  toggle() {
5
7
  const collapsed = this.element.dataset.collapsed === "true"
6
- this.element.dataset.collapsed = String(!collapsed)
8
+ this._setCollapsed(!collapsed)
7
9
  }
8
10
 
9
- open() { this.element.dataset.collapsed = "false" }
10
- close() { this.element.dataset.collapsed = "true" }
11
+ open() { this._setCollapsed(false) }
12
+ close() { this._setCollapsed(true) }
13
+
14
+ _setCollapsed(collapsed) {
15
+ this.element.dataset.collapsed = String(collapsed)
16
+ if (this.hasToggleTarget) this.toggleTarget.setAttribute("aria-expanded", String(!collapsed))
17
+ }
11
18
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ViewPrimitives
4
- VERSION = "0.2.3"
4
+ VERSION = "0.2.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_primitives
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Poimtsev
@@ -269,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
269
269
  - !ruby/object:Gem::Version
270
270
  version: '0'
271
271
  requirements: []
272
- rubygems_version: 4.0.19
272
+ rubygems_version: 4.0.20
273
273
  specification_version: 4
274
274
  summary: Primitive view components and helpers for Rails applications
275
275
  test_files: []