recourse 4.3.0 → 4.4.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +81 -95
- data/app/javascript/recourse/tooltip_controller.js +6 -1
- data/app/views/layouts/recourses.html.erb +35 -7
- data/app/views/recourses/_breadcrumb.html.erb +2 -2
- data/lib/recourse/helpers/cards.rb +3 -1
- data/lib/recourse/helpers/counters.rb +3 -0
- data/lib/recourse/helpers/navigation.rb +11 -0
- data/lib/recourse/helpers/tabs.rb +18 -15
- data/lib/recourse/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 063f5551f5b89bbb95660522a2a0177b9570f96961c5d5e28cfa83877ece91bf
|
|
4
|
+
data.tar.gz: 9f778ea100e50837483382e814936c1fc0697e462151445b144b7472e115132b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8538e6114d90e004f0b2f895bb5a0431c3569ddb6c05e4c93e3618aaa5056d7b44d24e974a197f546a387bfde58922ade7a25f5697444fce60637ff60864ce0c
|
|
7
|
+
data.tar.gz: 1b7776b83766984e18861aade3f0f79938358c24cba295c4bedca84c8f21b0aaefb1bad095da258bbcead01ea9a2ac6a1d5a2598d155935335189482a78283a0
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
|
|
6
6
|
[Vandamme](http://tech-angels.github.io/vandamme).
|
|
7
7
|
|
|
8
|
+
## 4.4.0 - 2026-09-08
|
|
9
|
+
|
|
10
|
+
* [Fix] Tapping the light/dark toggle or the log out on a phone leaves no tint behind
|
|
11
|
+
* [Feature] On a phone a resource's breadcrumb is its icon alone, leaving the row to the search box and the button
|
|
12
|
+
* [Feature] On a phone a card's tabs are their icons and figures: Show and Edit their
|
|
13
|
+
icons, a counted tab its icon and its number
|
|
14
|
+
* [Feature] On a phone the whole `Franchises: 21` of a stacked counter cell is the link
|
|
15
|
+
* [Fix] A touch screen shows no tooltips, a tap being what would open the link beneath one
|
|
16
|
+
* [Fix] Safari no longer zooms in on the search box or a filter: the controls are 16px on a phone, where the body itself is 14
|
|
17
|
+
* [Fix] A sidebar link tapped on a phone leaves no hover tint on it or on the link the next page puts under the finger
|
|
18
|
+
|
|
8
19
|
## 4.3.0 - 2026-09-08
|
|
9
20
|
|
|
10
21
|
* [Feature] On a phone a stacked cell reads as one line, `Estimate hi: 600`
|
data/README.md
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
# Recourse
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
serves a resource's seven actions — a paginated, searchable, sortable, filterable index
|
|
5
|
-
table, a record's page, its form and its delete — with no controller, view or partial
|
|
6
|
-
added to the app. What each screen shows is read off the model: its columns, its
|
|
7
|
-
validators, its associations and its indexes.
|
|
8
|
-
|
|
9
|
-
Everything a host may call is listed below with a link to its reference on
|
|
10
|
-
[RubyDoc](https://rubydoc.info/gems/recourse). Every default can be overridden by
|
|
11
|
-
defining the same thing in the app.
|
|
3
|
+
Swap two characters in your `config/routes.rb` and get awesome admin screens for your Rails app.
|
|
12
4
|
|
|
13
5
|
## How to install
|
|
14
6
|
|
|
@@ -26,80 +18,44 @@ breaking change. Rails 8.1 and Ruby 3.2 are the minimum; the pages need Turbo, w
|
|
|
26
18
|
`turbo-rails` brings, and nothing else — Bootstrap and its icons are vendored and served
|
|
27
19
|
by the engine, so a host without an asset pipeline gets the same screens.
|
|
28
20
|
|
|
29
|
-
##
|
|
21
|
+
## Step 1. Edit your routes
|
|
30
22
|
|
|
31
|
-
[`recourses`](https://rubydoc.info/gems/recourse/Recourse/Routes#recourses-instance_method)
|
|
32
|
-
is `resources` with a controller and views supplied. It takes the same `only:` and
|
|
33
|
-
`except:`, and a block nests children under the parent:
|
|
23
|
+
This is as easy as replacing any call to `resources` to use [`recourses`](https://rubydoc.info/gems/recourse/Recourse/Routes#recourses-instance_method) instead:
|
|
34
24
|
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
end
|
|
41
|
-
recourses :searches, only: %i[index show new create]
|
|
25
|
+
```diff
|
|
26
|
+
- resources :posts, only: %i[ index show ] do
|
|
27
|
+
+ recourses :posts, only: %i[ index show ] do
|
|
28
|
+
- resources :comments, except: :destroy
|
|
29
|
+
+ recourses :comments, except: :destroy
|
|
42
30
|
end
|
|
43
31
|
```
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- A nested `index` is a tab on the parent's card, counted where a `counter_cache` exists.
|
|
48
|
-
- A nested `create` with no index is a button on the parent's page, named after the
|
|
49
|
-
route. The host answers it in a controller of its own, or the gem creates the record.
|
|
50
|
-
- Nested controllers are namespaced after the parent (`Admin::Providers::MarketsController`),
|
|
51
|
-
and a nested index lists only the parent's rows.
|
|
52
|
-
- Do you name a route `exit`? The sidebar ends with a log-out button submitting a
|
|
53
|
-
`DELETE` to it:
|
|
33
|
+
Every recourse with `index` gets a sidebar entry (in routes order), a keyboard shortcut on its
|
|
34
|
+
first letter, and a paginated, searchable, sortable, filterable index table:
|
|
54
35
|
|
|
55
|
-
|
|
56
|
-
resource :session, only: :destroy, as: :exit
|
|
57
|
-
```
|
|
36
|
+
[image]
|
|
58
37
|
|
|
59
|
-
|
|
38
|
+
Every recourse with `show` gets a detail page where attributes are displayed with the appropriate
|
|
39
|
+
formatting, or masked if sensitive:
|
|
60
40
|
|
|
61
|
-
|
|
62
|
-
[`RecoursesController`](https://rubydoc.info/gems/recourse/Recourse/BaseController),
|
|
63
|
-
which the host defines to put behavior above every screen at once:
|
|
41
|
+
[image]
|
|
64
42
|
|
|
65
|
-
|
|
66
|
-
class RecoursesController < Recourse::BaseController
|
|
67
|
-
before_action :authenticate!
|
|
68
|
-
end
|
|
69
|
-
```
|
|
43
|
+
Every recourse with `new` or `edit` gets a form with appropriate browser formatting and validation:
|
|
70
44
|
|
|
71
|
-
|
|
45
|
+
[image]
|
|
72
46
|
|
|
73
|
-
|
|
47
|
+
Every recourse with `destroy` gets a button with a detailed confirmation message:
|
|
74
48
|
|
|
75
|
-
|
|
76
|
-
which Bootstrap family is primary on every page. Readers switch light and dark themselves.
|
|
77
|
-
- [`Recourse.bookmarks=`](https://rubydoc.info/gems/recourse/Recourse#bookmarks%3D-class_method):
|
|
78
|
-
a Proc answering the viewer's bookmark rows. Every table of a model with a `has_many`
|
|
79
|
-
at that class then opens with a square to keep a row by, kept rows first.
|
|
49
|
+
[image]
|
|
80
50
|
|
|
81
|
-
|
|
82
|
-
Recourse.color = :orange
|
|
83
|
-
Recourse.bookmarks = -> { Keepsake.where agent: Current.agent }
|
|
84
|
-
```
|
|
51
|
+
Every nested recourse gets namespaced after the parent:
|
|
85
52
|
|
|
86
|
-
|
|
53
|
+
[image]
|
|
87
54
|
|
|
88
|
-
Every Active Record model answers these; a model overrides one in a
|
|
89
|
-
[`Recoursive`](https://rubydoc.info/gems/recourse/Recourse/Recoursive) concern of its own.
|
|
90
55
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class_methods do
|
|
95
|
-
def recourse_label = :slug # what a menu and a cell call one
|
|
96
|
-
def recourse_hidden = %i[callback_url payload] # off every screen
|
|
97
|
-
def recourse_displayed = :created_at # back on the table
|
|
98
|
-
def recourse_order = { size: :desc } # how the index sorts
|
|
99
|
-
def recourse_icon = :question # a Unicon concept
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
```
|
|
56
|
+
## Step 2. Enhance your models
|
|
57
|
+
|
|
58
|
+
To change the content displayed in the `index` table of a model, override any of these class methods:
|
|
103
59
|
|
|
104
60
|
| Hook | Default | Decides |
|
|
105
61
|
| --- | --- | --- |
|
|
@@ -109,44 +65,74 @@ end
|
|
|
109
65
|
| [`recourse_order`](https://rubydoc.info/gems/recourse/Recourse/Recoursive#recourse_order-instance_method) | `:id` | the index's order, a Symbol or a Hash; rows with nothing in the column come last |
|
|
110
66
|
| [`recourse_icon`](https://rubydoc.info/gems/recourse/Recourse/Recoursive#recourse_icon-instance_method) | the model's name | the icon on the sidebar, the crumbs and the tabs |
|
|
111
67
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Do you want your own cells? Add `app/views/<resources>/_row.html.erb` and build it from
|
|
132
|
-
[`column`](https://rubydoc.info/gems/recourse/Recourse/Helpers/Cells#column-instance_method),
|
|
68
|
+
For instance, this would yield a more compact `index` than the default configuration:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
class Post < ActiveRecord::Base
|
|
72
|
+
def self.recourse_label = :title # Label a post with its title
|
|
73
|
+
def self.recourse_hidden = %i[ip_address] # Hide the IP address from the views
|
|
74
|
+
def self.recourse_displayed = :created_at # Display the created_at in the views
|
|
75
|
+
def self.recourse_order = { published_at: :desc } # Sort posts by last published first
|
|
76
|
+
def self.recourse_icon = :question # Represent Posts with a question icon
|
|
77
|
+
end
|
|
78
|
+
````
|
|
79
|
+
|
|
80
|
+
[image]
|
|
81
|
+
|
|
82
|
+
## Step 3. Delete your views
|
|
83
|
+
|
|
84
|
+
If you are happy with the generated HTML files, then be happy to skip this step!
|
|
85
|
+
If you want even more configuration, then just add a `app/views/<resources>/_row.html.erb` partial.
|
|
86
|
+
You can use [`column`](https://rubydoc.info/gems/recourse/Recourse/Helpers/Cells#column-instance_method),
|
|
133
87
|
[`sort_header`](https://rubydoc.info/gems/recourse/Recourse/Helpers/Sorts#sort_header-instance_method)
|
|
134
|
-
and [`search_highlight`](https://rubydoc.info/gems/recourse/Recourse/Helpers/Searches#search_highlight-instance_method)
|
|
88
|
+
and [`search_highlight`](https://rubydoc.info/gems/recourse/Recourse/Helpers/Searches#search_highlight-instance_method)
|
|
89
|
+
to define the columns you want:
|
|
135
90
|
|
|
136
91
|
```erb
|
|
137
92
|
<%# app/views/admin/providers/_row.html.erb %>
|
|
138
93
|
<%# locals: (provider:) -%>
|
|
94
|
+
|
|
139
95
|
<%= column header: sort_header(:name) do %>
|
|
140
96
|
<%= search_highlight provider.name, :name %>
|
|
141
97
|
<% end %>
|
|
142
|
-
|
|
98
|
+
|
|
99
|
+
<%= column header: 'Phone' do %>
|
|
100
|
+
<%= number_to_phone provider.phone %>
|
|
101
|
+
<% end %>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
[image]
|
|
105
|
+
|
|
106
|
+
## Step 4. Secure your controllers
|
|
107
|
+
|
|
108
|
+
Every controller the gem defines inherits
|
|
109
|
+
[`RecoursesController`](https://rubydoc.info/gems/recourse/Recourse/BaseController),
|
|
110
|
+
which the host defines to put behavior above every screen at once:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
class RecoursesController < Recourse::BaseController
|
|
114
|
+
before_action :authenticate!
|
|
115
|
+
end
|
|
143
116
|
```
|
|
144
117
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
118
|
+
A controller the app already defines is left alone.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Step 5. Enjoy the extras
|
|
122
|
+
|
|
123
|
+
Here are some bonus features the gem provides. Customize them by editing their values in
|
|
124
|
+
your `config/initializers/recourse.rb` file:
|
|
125
|
+
|
|
126
|
+
- [`Recourse.color=`](https://rubydoc.info/gems/recourse/Recourse#color%3D-class_method):
|
|
127
|
+
which Bootstrap family is primary on every page. Readers switch light and dark themselves.
|
|
128
|
+
- [`Recourse.bookmarks=`](https://rubydoc.info/gems/recourse/Recourse#bookmarks%3D-class_method):
|
|
129
|
+
a Proc answering the viewer's bookmark rows. Every table of a model with a `has_many`
|
|
130
|
+
at that class then opens with a square to keep a row by, kept rows first.
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
Recourse.color = :orange
|
|
134
|
+
Recourse.bookmarks = -> { Keepsake.where agent: Current.agent }
|
|
135
|
+
```
|
|
150
136
|
|
|
151
137
|
## Development
|
|
152
138
|
|
|
@@ -5,12 +5,17 @@ import { Tooltip } from '/recourse/bootstrap.bundle.min.js'
|
|
|
5
5
|
// and a Stimulus lifecycle is what survives Turbo redrawing the table: connect
|
|
6
6
|
// makes it, disconnect takes it down before the element goes, so a sorted or
|
|
7
7
|
// searched table never strands one over an element that left.
|
|
8
|
+
//
|
|
9
|
+
// None where nothing can hover: on a touch screen the tap that would open a link
|
|
10
|
+
// is also what shows a tooltip, and a word popping up over a tap reads as a hitch.
|
|
8
11
|
export default class extends Controller {
|
|
9
12
|
connect() {
|
|
13
|
+
if (matchMedia('(hover: none)').matches) return
|
|
14
|
+
|
|
10
15
|
this.tooltip = Tooltip.getOrCreateInstance(this.element)
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
disconnect() {
|
|
14
|
-
this.tooltip
|
|
19
|
+
this.tooltip?.dispose()
|
|
15
20
|
}
|
|
16
21
|
}
|
|
@@ -107,13 +107,15 @@
|
|
|
107
107
|
scrolled to it is a page Safari zooms out of and draws the navbar short on. */
|
|
108
108
|
html, .recourse-shell { overflow-x: hidden; }
|
|
109
109
|
|
|
110
|
-
/* A finger does not hover. The tint a pointer leaves on the row under it
|
|
111
|
-
stay on whatever
|
|
110
|
+
/* A finger does not hover. The tint a pointer leaves on the row under it, or on
|
|
111
|
+
the link under it, would stay on whatever was last touched — or land on whatever
|
|
112
|
+
the redrawn page put under the finger — so a touch screen gets none. */
|
|
112
113
|
@media (hover: none) {
|
|
113
114
|
.table-hover > tbody > tr:hover > * {
|
|
114
115
|
--bs-table-color-state: initial;
|
|
115
116
|
--bs-table-bg-state: initial;
|
|
116
117
|
}
|
|
118
|
+
.nav-link:hover:not(.active) { background-color: transparent; }
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
/* The navbar and the sidebar stack over the main column: a menu opened from a
|
|
@@ -148,6 +150,13 @@
|
|
|
148
150
|
font-weight: var(--bs-font-weight-semibold);
|
|
149
151
|
}
|
|
150
152
|
.sm\:table-stacked > tbody > tr > td:first-child { font-weight: inherit; }
|
|
153
|
+
/* A counter's heading goes inside its link, so the whole `Franchises: 21` is
|
|
154
|
+
one thing to tap; the cell's own heading steps aside for it. */
|
|
155
|
+
.sm\:table-stacked > tbody > tr > td.recourse-counter::before { content: none; }
|
|
156
|
+
.sm\:table-stacked > tbody > tr > td.recourse-counter > [data-cell]::before {
|
|
157
|
+
content: attr(data-cell) ': ';
|
|
158
|
+
font-weight: var(--bs-font-weight-semibold);
|
|
159
|
+
}
|
|
151
160
|
.sm\:table-stacked > tbody > tr > td:not(:first-child) + td::before { margin-block-start: 0; }
|
|
152
161
|
.sm\:table-stacked > tbody > tr > td { overflow-wrap: anywhere; }
|
|
153
162
|
}
|
|
@@ -260,9 +269,10 @@
|
|
|
260
269
|
.recourse-search { max-width: none; }
|
|
261
270
|
.recourse-search-box,
|
|
262
271
|
.recourse-search .combobox-toggle { flex: 1 1 100%; }
|
|
263
|
-
/* Safari zooms in on a field whose text is under 16px,
|
|
264
|
-
are 14px
|
|
265
|
-
|
|
272
|
+
/* Safari zooms in on a field whose text is under 16px, an absolute figure: the
|
|
273
|
+
small controls are 14px, and so is the body on a phone, so the controls say
|
|
274
|
+
16px themselves and focusing one moves nothing. */
|
|
275
|
+
.recourse-search .form-control { font-size: 16px; }
|
|
266
276
|
}
|
|
267
277
|
|
|
268
278
|
/* Held Option marks the letter each sidebar link answers to. Marked rather than
|
|
@@ -382,8 +392,12 @@
|
|
|
382
392
|
.recourse-foot { display: flex; }
|
|
383
393
|
.recourse-foot > * { flex: 1 1 0; display: flex; justify-content: center; }
|
|
384
394
|
.recourse-foot .nav-link { border-radius: var(--bs-radius-5); }
|
|
385
|
-
/* A hand cursor alone says little on an icon: a tint under it says it is a control.
|
|
386
|
-
|
|
395
|
+
/* A hand cursor alone says little on an icon: a tint under it says it is a control.
|
|
396
|
+
Only where something can hover: a finger's tap leaves `:hover` set until the next
|
|
397
|
+
tap elsewhere, and a tint that stays reads as a button stuck down. */
|
|
398
|
+
@media (hover: hover) {
|
|
399
|
+
.recourse-foot .nav-link:hover { background-color: var(--bs-bg-1); color: var(--bs-fg-body); }
|
|
400
|
+
}
|
|
387
401
|
|
|
388
402
|
/* The foot sits at the bottom of the sidebar and stretches across it while the
|
|
389
403
|
sidebar is a column, and is simply the last item of the row while it is not
|
|
@@ -415,6 +429,20 @@
|
|
|
415
429
|
}
|
|
416
430
|
}
|
|
417
431
|
|
|
432
|
+
/* A phone's card has room for a row of tabs only as pictures and figures: Show
|
|
433
|
+
and Edit are their icons, a counted tab its icon and its number. A tab with
|
|
434
|
+
neither keeps its word, there being nothing else to stand for it. */
|
|
435
|
+
@media (width < 768px) {
|
|
436
|
+
.recourse-tab-word { display: none; }
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* And the trail at the top: a resource's crumb is its icon alone on a phone, so the
|
|
440
|
+
search box and the button beside it have the row. A record's crumb has no icon
|
|
441
|
+
and keeps its words. */
|
|
442
|
+
@media (width < 768px) {
|
|
443
|
+
.recourse-crumb-word { display: none; }
|
|
444
|
+
}
|
|
445
|
+
|
|
418
446
|
/* While the sidebar is a row its foot has room for words, so each icon is followed
|
|
419
447
|
by its own; as a column the words are read but not seen, the way Bootstrap's
|
|
420
448
|
`.visually-hidden` does it. */
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<%# A crumb naming a record — the parent a nested page is under — links
|
|
10
10
|
without an icon: the crumb before it already carries the resource's. -%>
|
|
11
11
|
<li class='breadcrumb-item'>
|
|
12
|
-
<%= link_to resource ?
|
|
12
|
+
<%= link_to resource ? crumb_label(resource, title) : title, path,
|
|
13
13
|
class: class_names('breadcrumb-link', 'gap-2': resource) %>
|
|
14
14
|
</li>
|
|
15
15
|
<% elsif index < crumbs.size - 1 %>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</li>
|
|
22
22
|
<% elsif resource %>
|
|
23
23
|
<li class='breadcrumb-item' aria-current='page'>
|
|
24
|
-
<span class='breadcrumb-link active gap-2'><%=
|
|
24
|
+
<span class='breadcrumb-link active gap-2'><%= crumb_label resource, title %></span>
|
|
25
25
|
</li>
|
|
26
26
|
<% else %>
|
|
27
27
|
<%# A leaf names a page rather than a resource — `New market`, or a record by
|
|
@@ -60,7 +60,9 @@ module Recourse
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def tab_label(action)
|
|
63
|
-
|
|
63
|
+
word = tag.span t("recourse.#{action}"), class: 'recourse-tab-word'
|
|
64
|
+
|
|
65
|
+
safe_join [icon_tag(Actions::ICONS[action]), word], ' '
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
end
|
|
@@ -21,6 +21,9 @@ module Recourse
|
|
|
21
21
|
count = number_with_delimiter value
|
|
22
22
|
counted = counter_counted count, value, association
|
|
23
23
|
named = counter_naming count, association
|
|
24
|
+
# The heading again, for a stacked table to draw inside the link rather than
|
|
25
|
+
# before it: `Franchises: 21` is then one thing to tap.
|
|
26
|
+
named[:data] = { cell: Recourse.model_title(association.klass) }
|
|
24
27
|
path = resource_controller_path
|
|
25
28
|
nested = nested_path_of path, association
|
|
26
29
|
return tag.span(counted, **named) unless nested && routed?(nested, 'index')
|
|
@@ -63,6 +63,17 @@ module Recourse
|
|
|
63
63
|
safe_join [icon && tag.i(class: "bi bi-#{icon}"), name].compact, ' '
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# The same for a crumb, whose words a phone drops where the icon stands for them:
|
|
67
|
+
# the row at the top has a search box and a button to fit beside the trail.
|
|
68
|
+
def crumb_label(resource, title)
|
|
69
|
+
icon = Recourse.known_icon resource
|
|
70
|
+
return title unless icon
|
|
71
|
+
|
|
72
|
+
word = tag.span title, class: 'recourse-crumb-word'
|
|
73
|
+
|
|
74
|
+
safe_join [tag.i(class: "bi bi-#{icon}"), word], ' '
|
|
75
|
+
end
|
|
76
|
+
|
|
66
77
|
# Only a page beneath the index names itself, and names what it is showing.
|
|
67
78
|
def breadcrumb_leaf
|
|
68
79
|
case controller.action_name
|
|
@@ -29,27 +29,30 @@ module Recourse
|
|
|
29
29
|
[lead.presence&.upcase_first, title].compact.join ' '
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
# The icon is the counted model's own, whatever leads the words beside it.
|
|
32
|
+
# The icon is the counted model's own, whatever leads the words beside it. The
|
|
33
|
+
# words are wrapped where a picture or a figure stands beside them, so a phone,
|
|
34
|
+
# which has room for neither, keeps the picture and the figure and drops the words.
|
|
33
35
|
def association_tab_label(record, association, namespace)
|
|
34
36
|
icon = Recourse.known_model_icon association.klass
|
|
37
|
+
count = tab_count record, association
|
|
38
|
+
words = tab_words association, namespace, count
|
|
39
|
+
words = tag.span words, class: 'recourse-tab-word' if icon || count
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
safe_join words.compact, ' '
|
|
41
|
+
safe_join [icon && tag.i(class: "bi bi-#{icon}"), count, words].compact, ' '
|
|
39
42
|
end
|
|
40
43
|
|
|
41
|
-
# `
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
lead =
|
|
50
|
-
title = Recourse.model_title association.klass, count: count, lower: lead.present?
|
|
44
|
+
# `ZIPs` where the record keeps no count, and `places` after the `8` where it
|
|
45
|
+
# does — read off the record itself, no query, like the column. A namespace the
|
|
46
|
+
# routes drew leads either, so two nestings of one model read apart: `10 visited
|
|
47
|
+
# places` beside `4 booked places`, `Visited places` beside `Booked places`.
|
|
48
|
+
# Whatever leads earns the downcase, and a title that leads keeps its capital —
|
|
49
|
+
# which is what puts these beside Show and Edit.
|
|
50
|
+
def tab_words(association, namespace, count)
|
|
51
|
+
lead = namespace_words namespace
|
|
52
|
+
lead = lead.upcase_first unless count
|
|
53
|
+
title = Recourse.model_title association.klass, count: count, lower: count || lead.present?
|
|
51
54
|
|
|
52
|
-
[lead.presence
|
|
55
|
+
[lead.presence, title].compact.join ' '
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
def tab_count(record, association)
|
data/lib/recourse/version.rb
CHANGED