layered-ui-rails 0.19.0 → 0.20.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: 3300c4fe2f7e3d1fc8175af4985078888212de7a797482ffc12b5d60074b4b09
4
- data.tar.gz: a56fed6fd7b9013d7c0a2ca988194d5071713e137f1c4022b58e86e205992bf1
3
+ metadata.gz: f93bf15a80c67a460c8eada9407beb94507d3dce7fe511e935355abcd329a4d8
4
+ data.tar.gz: 06d49cc35c54fa21fddb6ced245abfa9ad4783003a03074beb9467e03f46cbb1
5
5
  SHA512:
6
- metadata.gz: 717f5fef1822f58376c54673e9c536be2d1509ee185d152f82e0bcce75e91930b2312d21d106b076ae58a0aff5c7c0f239fe7cff5147987b725dcaa433d773d2
7
- data.tar.gz: e99903b76852ebf675b7583a71572b0bcd0cad27eb12a9231fc4a9b7ad2b9a4e9d5a3e48970e71713fe780307f6e63fee398a9c25c1c37d94c7d1439abf42f5a
6
+ metadata.gz: f4435d521aa96f5e74b0d1183ae5d486b795d28eb6d6dc16728b8147599715a5e49b64c0a226a78b5aed479c6586dd73f02962321d2e976e693453f79d022fe6
7
+ data.tar.gz: 21030bae58aefc605b07370d6dcc615a1f1441566a72b28859068151109d41005b1fc333234878293a4c02d424d5896f9b3230886f1efcfc071ce3cbb493a7d4
@@ -181,6 +181,7 @@ Quick reference:
181
181
  | `l_ui_theme_toggle` | Default header theme toggle button |
182
182
  | `l_ui_authentication` | Default header login/register buttons (Devise) |
183
183
  | `l_ui_navigation_toggle` | Default header sidebar toggle button |
184
+ | `l_ui_popover(id: nil, placement: :bottom, align: :start, container: {})` | Floating panel anchored to a trigger, built on the native `popover` attribute |
184
185
 
185
186
  ## CSS classes
186
187
 
@@ -202,6 +203,7 @@ Key components:
202
203
  | Notices | `.l-ui-notice` (base), `--success`, `--warning`, `--error` |
203
204
  | Tabs | `.l-ui-tabs`, `.l-ui-tabs__list`, `.l-ui-tabs__tab`, `--active` |
204
205
  | Modal | `.l-ui-modal`, `.l-ui-modal__header`, `.l-ui-modal__body` |
206
+ | Popover | `.l-ui-popover` |
205
207
 
206
208
  ## Stimulus controllers
207
209
 
@@ -217,6 +219,7 @@ All controllers use the `l-ui--` namespace and are auto-registered via importmap
217
219
  | Panel resize | `l-ui--panel-resize` | Panel width drag handle |
218
220
  | Modal | `l-ui--modal` | Native `<dialog>` with focus trap |
219
221
  | Tabs | `l-ui--tabs` | Accessible tabbed interface |
222
+ | Popover | `l-ui--popover` | Positions a native `popover`-attribute element relative to its trigger, with auto-flip |
220
223
  | Search form | `l-ui--search-form` | Multi-scope search with Turbo support and pagination param preservation |
221
224
 
222
225
  ## Theming
@@ -106,6 +106,31 @@ Accessible tabbed interface with keyboard navigation.
106
106
  </div>
107
107
  ```
108
108
 
109
+ ## Popover (`l-ui--popover`)
110
+
111
+ Positions a native `popover`-attribute element relative to its trigger, with auto-flip near viewport edges. Showing, hiding, light-dismiss (outside click), and Escape-to-close are all handled by the browser via the `popover` attribute - this controller only handles placement.
112
+
113
+ **Targets:** `trigger`, `popover`
114
+ **Values:** `placement` (String, default `"bottom"`; one of `"top"`, `"bottom"`, `"left"`, `"right"`), `align` (String, default `"start"`; `"start"` or `"end"` - which edge of the popover flushes with the trigger on the cross axis)
115
+
116
+ ```html
117
+ <div data-controller="l-ui--popover">
118
+ <button type="button" popovertarget="my-popover"
119
+ data-l-ui--popover-target="trigger"
120
+ class="l-ui-button l-ui-button--outline">
121
+ Open popover
122
+ </button>
123
+ <div id="my-popover" popover="auto" class="l-ui-popover"
124
+ data-l-ui--popover-target="popover">
125
+ Content here.
126
+ </div>
127
+ </div>
128
+ ```
129
+
130
+ Features:
131
+ - Repositions on open, and while open on window resize/scroll
132
+ - Flips to the opposite side if the preferred placement would overflow the viewport
133
+
109
134
  ## Panel (`l-ui--panel`)
110
135
 
111
136
  Resizable side panel. Full-width overlay on mobile, docked sidebar on desktop.
@@ -287,6 +287,16 @@ Always combine the base block with a modifier, e.g. `<span class="l-ui-badge l-u
287
287
  .l-ui-modal__body Scrollable modal content
288
288
  ```
289
289
 
290
+ ## Popover
291
+
292
+ ```
293
+ .l-ui-popover popover-attribute element; positioned by the l-ui--popover Stimulus controller
294
+ .l-ui-popover__menu Full-width list wrapper for a menu of actions inside a popover
295
+ .l-ui-popover__menu-item Full-width action link/button inside l-ui-popover__menu
296
+ .l-ui-popover__menu-item--danger Danger styling for a destructive menu item
297
+ .l-ui-popover__menu-divider Horizontal rule separating groups of items (apply to an <hr>)
298
+ ```
299
+
290
300
  ## Breadcrumbs
291
301
 
292
302
  ```
@@ -322,6 +322,51 @@ The button does not need to be inside the helper's wrapper, and no `data-control
322
322
 
323
323
  Calling `dialog.showModal()` directly is not supported - it bypasses the `l-ui--modal` controller and skips scroll lock, focus restoration, open-count tracking, and the screen-reader announcement.
324
324
 
325
+ ## Popover
326
+
327
+ ```ruby
328
+ l_ui_popover(id: nil, placement: :bottom, align: :start, container: {}, &block)
329
+ ```
330
+
331
+ - `id` (String, optional) - DOM id for the popover element; defaults to an auto-generated id
332
+ - `placement` (Symbol, optional) - `:top`, `:bottom` (default), `:left`, or `:right`. Flips automatically if it would overflow the viewport
333
+ - `align` (Symbol, optional) - `:start` (default) flushes the popover's leading edge with the trigger's; `:end` flushes its trailing edge instead. For example, `placement: :bottom, align: :end` hangs the popover down and to the left of a trigger at the right end of a row
334
+ - `container` (Hash, optional) - extra HTML attributes for the wrapping `<div>` (e.g. `class:`)
335
+ - `&block` - the block's content is the popover body; call `p.trigger(**options, &block)` inside it to render the trigger button
336
+
337
+ ```erb
338
+ <%= l_ui_popover(placement: :bottom) do |p| %>
339
+ <% p.trigger(class: "l-ui-button l-ui-button--outline") do %>
340
+ Options
341
+ <% end %>
342
+ <p>Popover content.</p>
343
+ <% end %>
344
+ ```
345
+
346
+ Renders the trigger `<button popovertarget="...">` and the `popover="auto"` element (`class="l-ui-popover"`), and wires the `l-ui--popover` Stimulus controller for placement. Showing, hiding, light-dismiss (outside click), and Escape-to-close are all handled natively by the browser via the `popover` attribute - no Stimulus action is needed to open or close it.
347
+
348
+ If the trigger contains only an icon (no visible text), pass `aria-label:` to `p.trigger` so the button has an accessible name.
349
+
350
+ For a list of actions (e.g. a "more" menu on a table row), wrap the items in a `<div class="l-ui-popover__menu">` and give each link/button `l-ui-popover__menu-item` (add `l-ui-popover__menu-item--danger` for destructive actions, and an `<hr class="l-ui-popover__menu-divider">` to separate groups of items):
351
+
352
+ ```erb
353
+ <%= l_ui_table(@users, columns: [...],
354
+ actions: ->(r) {
355
+ l_ui_popover(align: :end) do |p|
356
+ p.trigger(class: "l-ui-button l-ui-button--outline l-ui-button--icon l-ui-button--small", "aria-label" => "Actions for #{r.name}") do
357
+ image_tag "layered_ui/icon_more.svg", alt: "", class: "l-ui-icon l-ui-icon--sm", aria: { hidden: true }
358
+ end
359
+ tag.div(class: "l-ui-popover__menu") do
360
+ safe_join([
361
+ link_to("Edit", edit_user_path(r), class: "l-ui-popover__menu-item"),
362
+ tag.hr(class: "l-ui-popover__menu-divider"),
363
+ button_to("Delete", user_path(r), method: :delete, class: "l-ui-popover__menu-item l-ui-popover__menu-item--danger")
364
+ ])
365
+ end
366
+ end
367
+ }) %>
368
+ ```
369
+
325
370
  ## Header
326
371
 
327
372
  ```ruby
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. This project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [0.20.0] - 2026-07-08
6
+
7
+ ### Added
8
+
9
+ - Popovers: floating panels anchored to a trigger button, built on the native HTML `popover` attribute. Showing, hiding, light-dismiss (outside click), and Escape-to-close are all handled by the browser; the new `l-ui--popover` Stimulus controller only computes placement, flipping to the opposite side if the preferred side would overflow the viewport. Use the `l_ui_popover` helper (options: `id:`, `placement:` of `:top`/`:bottom`/`:left`/`:right`, `align:` of `:start`/`:end`, `container:`) with `p.trigger` for the button, or wire the `l-ui-popover` class and controller targets by hand for full control over markup.
10
+ - Popover menus: `l-ui-popover__menu`, `l-ui-popover__menu-item`, `l-ui-popover__menu-item--danger`, and `l-ui-popover__menu-divider` style a list of actions inside a popover - e.g. a "more" menu on a table row via `l_ui_table`'s `actions:` proc. Give an icon-only trigger an `aria-label` so it has an accessible name.
11
+ - `icon_more.svg` (vertical ellipsis) added to the bundled icon set, for "more actions" popover triggers.
12
+
13
+ ### Changed
14
+
15
+ - Navigation items now show a `surface` background on hover, matching the hover treatment of other menu rows. The active item keeps its highlighted background on hover.
16
+ - The overrides generator now stamps the gem version it was generated from into the overrides file's header comment.
17
+
5
18
  ## [0.19.0] - 2026-06-15
6
19
 
7
20
  ### Added
data/README.md CHANGED
@@ -36,7 +36,7 @@ An open source, Rails 8+ engine that provides WCAG 2.2 AA compliant design token
36
36
  - **Dark/light theme** - system preference detection with localStorage persistence and manual toggle
37
37
  - **Responsive layout** - header, sidebar navigation, main content area, and optional resizable panel
38
38
  - **WCAG 2.2 AA compliant** - skip links, focus indicators, ARIA attributes, and 4.5:1 contrast ratios
39
- - **Components** - buttons, forms, surfaces, tables, tabs, notices, badges, conversations, modals, and pagination
39
+ - **Components** - buttons, forms, surfaces, tables, tabs, notices, badges, conversations, modals, popovers, and pagination
40
40
  - **Optional integrations** - Devise authentication and Pagy pagination with styled views
41
41
  - **Customisable branding** - Override the default logos and icons and colors
42
42
  - **Google Lighthouse** - `layered-ui-rails` scores a [perfect 100](https://github.com/layered-ai-public/layered-ui-rails/raw/refs/heads/main/test/dummy/app/assets/images/lighthouse.webp) across all four Google Lighthouse categories - performance, accessibility, best practices, and SEO
@@ -0,0 +1,5 @@
1
+ <svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="12" cy="5" r="2" />
3
+ <circle cx="12" cy="12" r="2" />
4
+ <circle cx="12" cy="19" r="2" />
5
+ </svg>
@@ -1035,12 +1035,16 @@
1035
1035
  w-full min-h-[40px]
1036
1036
  gap-3 px-3
1037
1037
  text-sm font-medium text-foreground-muted
1038
+ hover:bg-surface
1038
1039
  rounded-sm
1039
1040
  focus-ring
1040
1041
  transition-colors;
1041
1042
  }
1042
1043
 
1043
- .l-ui-navigation__item--active {
1044
+ /* Repeats hover so the active background wins over the base item's
1045
+ hover:bg-surface - equal specificity, but this rule comes later. */
1046
+ .l-ui-navigation__item--active,
1047
+ .l-ui-navigation__item--active:hover {
1044
1048
  @apply text-foreground
1045
1049
  bg-surface-highlighted;
1046
1050
  }
@@ -2242,6 +2246,45 @@ pre.l-ui-surface {
2242
2246
  px-5 py-4;
2243
2247
  }
2244
2248
 
2249
+ /* Popover */
2250
+
2251
+ .l-ui-popover {
2252
+ @apply fixed inset-auto
2253
+ max-w-xs
2254
+ m-0 p-3
2255
+ text-sm text-foreground
2256
+ bg-background
2257
+ border border-border rounded-sm;
2258
+ }
2259
+
2260
+ /* A list of actions inside a popover (e.g. a table row's "more" menu).
2261
+ Negative margin cancels l-ui-popover's own padding; px-1/py-1 restore a
2262
+ small inset for the list itself. */
2263
+ .l-ui-popover__menu {
2264
+ @apply flex flex-col
2265
+ -m-3 px-1 py-1;
2266
+ }
2267
+
2268
+ .l-ui-popover__menu-item {
2269
+ @apply block w-full
2270
+ px-3 py-2
2271
+ text-sm text-left text-foreground
2272
+ hover:bg-surface focus:bg-surface
2273
+ rounded-sm
2274
+ focus-ring
2275
+ transition-colors
2276
+ cursor-pointer;
2277
+ }
2278
+
2279
+ .l-ui-popover__menu-item--danger {
2280
+ @apply text-danger;
2281
+ }
2282
+
2283
+ .l-ui-popover__menu-divider {
2284
+ @apply my-1
2285
+ border-t border-border;
2286
+ }
2287
+
2245
2288
  /* Safari */
2246
2289
  @media (max-width: 767px) {
2247
2290
  /* iOS Safari ignores overflow:hidden on body; position:fixed is required */
@@ -0,0 +1,81 @@
1
+ module Layered
2
+ module Ui
3
+ module PopoverHelper
4
+ # Renders a floating panel anchored to a trigger button, built on the
5
+ # native HTML +popover+ attribute. Showing, hiding, light-dismiss, and
6
+ # Escape-to-close are all handled by the browser; the +l-ui--popover+
7
+ # Stimulus controller only computes placement (with auto-flip near
8
+ # viewport edges).
9
+ #
10
+ # <%= l_ui_popover(placement: :bottom) do |p| %>
11
+ # <% p.trigger(class: "l-ui-button l-ui-button--outline") do %>
12
+ # Options
13
+ # <% end %>
14
+ # <p>Popover content.</p>
15
+ # <% end %>
16
+ #
17
+ # Note: use +<% p.trigger %>+ (without the equals sign) so its content is
18
+ # captured by the builder rather than written to the body buffer.
19
+ #
20
+ # Options:
21
+ # id: (String) DOM id for the popover element; defaults to an auto-generated id.
22
+ # placement: (Symbol) :top, :bottom (default), :left, or :right. Flips automatically if it would overflow the viewport.
23
+ # align: (Symbol) :start (default) flushes the popover's leading edge with the trigger's; :end flushes its trailing edge instead
24
+ # (e.g. placement: :bottom, align: :end hangs the popover down and to the left of a trigger at the right end of a row).
25
+ # container: (Hash) Extra HTML attributes for the wrapping <div>.
26
+ def l_ui_popover(id: nil, placement: :bottom, align: :start, container: {}, &block)
27
+ id ||= "l-ui-popover-#{SecureRandom.hex(4)}"
28
+ builder = PopoverBuilder.new(self, id: id)
29
+ body_content = capture { block.call(builder) }
30
+
31
+ container_attrs = container.deep_dup
32
+ container_data = container_attrs[:data] || {}
33
+ existing_controller = container_data.delete(:controller) || container_data.delete("controller")
34
+ container_data[:controller] = [existing_controller, "l-ui--popover"].compact.reject(&:empty?).join(" ")
35
+ container_data[:"l-ui--popover-placement-value"] = placement
36
+ container_data[:"l-ui--popover-align-value"] = align
37
+ container_attrs[:data] = container_data
38
+
39
+ tag.div(**container_attrs) do
40
+ safe_join([
41
+ builder.trigger_html || ActiveSupport::SafeBuffer.new,
42
+ render_popover(builder, body_content)
43
+ ])
44
+ end
45
+ end
46
+
47
+ class PopoverBuilder
48
+ attr_reader :id, :trigger_html
49
+
50
+ def initialize(view, id:)
51
+ @view = view
52
+ @id = id
53
+ end
54
+
55
+ def trigger(**options, &block)
56
+ options = options.deep_dup
57
+ options[:type] ||= "button"
58
+ options[:popovertarget] = id
59
+ data = options[:data] || {}
60
+ data[:"l-ui--popover-target"] = "trigger"
61
+ options[:data] = data
62
+ content = @view.capture(&block)
63
+ @trigger_html = @view.tag.button(content, **options)
64
+ nil
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def render_popover(builder, body_content)
71
+ tag.div(
72
+ body_content,
73
+ id: builder.id,
74
+ popover: "auto",
75
+ class: "l-ui-popover",
76
+ data: { "l-ui--popover-target" => "popover" }
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,112 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Positions a native `popover`-attribute element relative to its trigger.
4
+ // Showing, hiding, light-dismiss, and Escape-to-close are all handled by the
5
+ // browser via the popover attribute; this controller only computes placement
6
+ // (with auto-flip near viewport edges), since CSS anchor positioning isn't
7
+ // yet reliable across all supported browsers.
8
+ export default class extends Controller {
9
+ static targets = ["trigger", "popover"]
10
+ static values = {
11
+ placement: { type: String, default: "bottom" },
12
+ align: { type: String, default: "start" }
13
+ }
14
+
15
+ static GAP = 8
16
+
17
+ popoverTargetConnected(element) {
18
+ this._toggleHandler = (event) => {
19
+ if (event.newState === "open") {
20
+ this.position()
21
+ this._addRepositionListeners()
22
+ } else {
23
+ this._removeRepositionListeners()
24
+ }
25
+ }
26
+ element.addEventListener("toggle", this._toggleHandler)
27
+ }
28
+
29
+ popoverTargetDisconnected(element) {
30
+ element.removeEventListener("toggle", this._toggleHandler)
31
+ this._removeRepositionListeners()
32
+ }
33
+
34
+ disconnect() {
35
+ this._removeRepositionListeners()
36
+ }
37
+
38
+ // Compute and apply the popover's fixed position relative to the trigger,
39
+ // flipping to the opposite side if the preferred placement would overflow
40
+ // the viewport.
41
+ position() {
42
+ if (!this.hasTriggerTarget || !this.hasPopoverTarget) return
43
+
44
+ const gap = this.constructor.GAP
45
+ const trigger = this.triggerTarget.getBoundingClientRect()
46
+ const popover = this.popoverTarget.getBoundingClientRect()
47
+ const align = this.alignValue
48
+
49
+ // The cross-axis coordinate (horizontal for top/bottom, vertical for
50
+ // left/right): "start" flushes the popover's leading edge with the
51
+ // trigger's, "end" flushes its trailing edge instead - e.g. a "bottom"
52
+ // placement with "end" align hangs the popover down and to the left of
53
+ // a trigger sitting at the right end of a row.
54
+ const crossAxisStart = (placement) =>
55
+ placement === "top" || placement === "bottom"
56
+ ? align === "end" ? trigger.right - popover.width : trigger.left
57
+ : align === "end" ? trigger.bottom - popover.height : trigger.top
58
+
59
+ const coordsFor = (placement) => {
60
+ switch (placement) {
61
+ case "top":
62
+ return { top: trigger.top - popover.height - gap, left: crossAxisStart(placement) }
63
+ case "left":
64
+ return { top: crossAxisStart(placement), left: trigger.left - popover.width - gap }
65
+ case "right":
66
+ return { top: crossAxisStart(placement), left: trigger.right + gap }
67
+ default:
68
+ return { top: trigger.bottom + gap, left: crossAxisStart(placement) }
69
+ }
70
+ }
71
+
72
+ let placement = this.placementValue
73
+ let coords = coordsFor(placement)
74
+
75
+ if (placement === "bottom" && coords.top + popover.height > window.innerHeight) {
76
+ placement = "top"
77
+ coords = coordsFor(placement)
78
+ } else if (placement === "top" && coords.top < 0) {
79
+ placement = "bottom"
80
+ coords = coordsFor(placement)
81
+ } else if (placement === "right" && coords.left + popover.width > window.innerWidth) {
82
+ placement = "left"
83
+ coords = coordsFor(placement)
84
+ } else if (placement === "left" && coords.left < 0) {
85
+ placement = "right"
86
+ coords = coordsFor(placement)
87
+ }
88
+
89
+ let left = coords.left
90
+ if (placement === "top" || placement === "bottom") {
91
+ left = Math.min(Math.max(gap, left), window.innerWidth - popover.width - gap)
92
+ }
93
+
94
+ this.popoverTarget.style.top = `${Math.max(gap, coords.top)}px`
95
+ this.popoverTarget.style.left = `${left}px`
96
+ }
97
+
98
+ // Private
99
+
100
+ _addRepositionListeners() {
101
+ this._repositionHandler = () => this.position()
102
+ window.addEventListener("resize", this._repositionHandler)
103
+ window.addEventListener("scroll", this._repositionHandler, true)
104
+ }
105
+
106
+ _removeRepositionListeners() {
107
+ if (!this._repositionHandler) return
108
+ window.removeEventListener("resize", this._repositionHandler)
109
+ window.removeEventListener("scroll", this._repositionHandler, true)
110
+ this._repositionHandler = null
111
+ }
112
+ }
@@ -6,6 +6,7 @@ import PanelController from "layered_ui/controllers/l_ui/panel_controller"
6
6
  import PanelResizeController from "layered_ui/controllers/l_ui/panel_resize_controller"
7
7
  import PanelButtonController from "layered_ui/controllers/l_ui/panel_button_controller"
8
8
  import ModalController from "layered_ui/controllers/l_ui/modal_controller"
9
+ import PopoverController from "layered_ui/controllers/l_ui/popover_controller"
9
10
  import SearchFormController from "layered_ui/controllers/l_ui/search_form_controller"
10
11
  import TabsController from "layered_ui/controllers/l_ui/tabs_controller"
11
12
 
@@ -17,4 +18,5 @@ application.register("l-ui--panel", PanelController)
17
18
  application.register("l-ui--panel-resize", PanelResizeController)
18
19
  application.register("l-ui--panel-button", PanelButtonController)
19
20
  application.register("l-ui--modal", ModalController)
21
+ application.register("l-ui--popover", PopoverController)
20
22
  application.register("l-ui--tabs", TabsController)
data/config/importmap.rb CHANGED
@@ -13,6 +13,7 @@ pin "layered_ui/controllers/l_ui/navigation_section_controller", to: "layered_ui
13
13
  pin "layered_ui/controllers/l_ui/panel_controller", to: "layered_ui/controllers/l_ui/panel_controller.js"
14
14
  pin "layered_ui/controllers/l_ui/panel_resize_controller", to: "layered_ui/controllers/l_ui/panel_resize_controller.js"
15
15
  pin "layered_ui/controllers/l_ui/panel_button_controller", to: "layered_ui/controllers/l_ui/panel_button_controller.js"
16
+ pin "layered_ui/controllers/l_ui/popover_controller", to: "layered_ui/controllers/l_ui/popover_controller.js"
16
17
  pin "layered_ui/controllers/l_ui/search_form_controller", to: "layered_ui/controllers/l_ui/search_form_controller.js"
17
18
  pin "layered_ui/controllers/l_ui/tabs_controller", to: "layered_ui/controllers/l_ui/tabs_controller.js"
18
19
  pin "layered_ui/controllers/l_ui/theme_controller", to: "layered_ui/controllers/l_ui/theme_controller.js"
@@ -23,7 +23,7 @@ module Layered
23
23
  def overrides_content
24
24
  <<~CSS
25
25
  /*
26
- * layered-ui-rails color overrides
26
+ * layered-ui-rails color overrides (generated from v#{Layered::Ui::VERSION})
27
27
  *
28
28
  * Uncomment and edit any variable below to customise the UI.
29
29
  * This file is NOT overwritten by the install generator, so your
@@ -36,6 +36,7 @@ module Layered
36
36
  helper Layered::Ui::FormHelper
37
37
  helper Layered::Ui::HeaderHelper
38
38
  helper Layered::Ui::ModalHelper
39
+ helper Layered::Ui::PopoverHelper
39
40
  helper Layered::Ui::RansackHelper
40
41
  end
41
42
  end
@@ -1,5 +1,5 @@
1
1
  module Layered
2
2
  module Ui
3
- VERSION = "0.19.0"
3
+ VERSION = "0.20.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: layered-ui-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - layered.ai
@@ -203,6 +203,7 @@ files:
203
203
  - app/assets/images/layered_ui/icon_linkedin.svg
204
204
  - app/assets/images/layered_ui/icon_mail.svg
205
205
  - app/assets/images/layered_ui/icon_moon.svg
206
+ - app/assets/images/layered_ui/icon_more.svg
206
207
  - app/assets/images/layered_ui/icon_panel_close.svg
207
208
  - app/assets/images/layered_ui/icon_sun.svg
208
209
  - app/assets/images/layered_ui/icon_x.svg
@@ -220,6 +221,7 @@ files:
220
221
  - app/helpers/layered/ui/modal_helper.rb
221
222
  - app/helpers/layered/ui/navigation_helper.rb
222
223
  - app/helpers/layered/ui/pagy_helper.rb
224
+ - app/helpers/layered/ui/popover_helper.rb
223
225
  - app/helpers/layered/ui/ransack_helper.rb
224
226
  - app/helpers/layered/ui/table_helper.rb
225
227
  - app/helpers/layered/ui/title_bar_helper.rb
@@ -229,6 +231,7 @@ files:
229
231
  - app/javascript/layered_ui/controllers/l_ui/panel_button_controller.js
230
232
  - app/javascript/layered_ui/controllers/l_ui/panel_controller.js
231
233
  - app/javascript/layered_ui/controllers/l_ui/panel_resize_controller.js
234
+ - app/javascript/layered_ui/controllers/l_ui/popover_controller.js
232
235
  - app/javascript/layered_ui/controllers/l_ui/search_form_controller.js
233
236
  - app/javascript/layered_ui/controllers/l_ui/tabs_controller.js
234
237
  - app/javascript/layered_ui/controllers/l_ui/theme_controller.js