recourse 7.8.3 → 7.10.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: 94653dc38ca1c788fce69b9406faddc48c189cefcd9fbe7dbe11e4d87ddc3faf
4
- data.tar.gz: c1282266e1e70de6bc5438661f86321e52948dee028baa7128efe4fac4317719
3
+ metadata.gz: a5ba4a5b982928c2ee9ea80e400a2d95c9b59102224902787ea3b3d6b9f4c9a8
4
+ data.tar.gz: 0d44134228230f5a15f4bc7545df435407518a074ae34299605d90294a93a4bd
5
5
  SHA512:
6
- metadata.gz: dbed84f6d5071f7be79cdb87c499c6a9c4ae23ceb74c70390606cfe33652236f38e782ecbae67347c4873b270c23cba660395d2451034202517af60fe9a0a8d6
7
- data.tar.gz: e04c3108e911aba9d08555b8d696994cc56df42ee21ee207f96cc0f1cf326491fcd62d867f38953e22c3fdb76e14297a50d22e4444db0bae1c28746a20b546db
6
+ metadata.gz: 7db938770e4ecf06d930a89b965cc9790b2596b6c889be4f0a77cb0fff553739978408c22706da03abb4209964b17f6875b3c6703335e0bc9744d70c213768a1
7
+ data.tar.gz: b6a11dad45cae5cac88c42890b8551fd2f8a6c527c554672e7fe07221af251c1dbb1b59b35facc89b3c802ff57e1aec6ae01878d49e6424fe2b8d8b772eb85a2
data/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## 7.10.0 - 2026-09-26
11
+
12
+ * [FEATURE] The sidebar's foot says what each icon does
13
+
14
+ The moon, the sun, the way in and the way out each open a tooltip to the right, away
15
+ from the sidebar's edge, the way a column's icon opens one above it: `Dark theme`,
16
+ `Light theme`, `Sign in` and `Sign out`. A host that answers `exit_name` with whoever is
17
+ signed in earns `Sign out N-100L`. The words are `recourse.dark_theme`, `light_theme`,
18
+ `sign_out` and `sign_out_as` in the locale.
19
+
20
+ ## 7.9.0 - 2026-09-26
21
+
22
+ * [FEATURE] A pin or an area on a map leads to its row's page
23
+
24
+ Each point is sent as `[lat, lng, href, title]` and each place as `[id, href, title]`: the
25
+ record's own page wherever one is routed, and its label. bh's map controller makes a pin
26
+ or an area with an `href` clickable, following it through Turbo. Needs the bh that reads
27
+ these shapes: an older one still draws points, but takes a place's whole triple for its
28
+ ID and draws nothing, which is why this release requires bh 6.10.
29
+
30
+ * [FEATURE] A route named `enter` earns the sidebar a way in
31
+
32
+ The mirror of `exit`: `get 'session/new', to: 'sessions#new', as: :enter` draws a
33
+ `Sign in` link beside the toggle, to wherever the host signs people in. A host that
34
+ signs people in shows one or the other by answering `enter?` and `exit?` itself.
35
+
36
+ * [FEATURE] A bookmarks Proc answering nil draws no bookmark column
37
+
38
+ A visitor who never signed in has nowhere to keep a row, so `Recourse.bookmarks =
39
+ -> { Current.user&.bookmarks }` leaves them without the squares rather than raising.
40
+
10
41
  ## 7.8.3 - 2026-09-21
11
42
 
12
43
  * [FIX] A delete button in a table row is no taller than the row
data/README.md CHANGED
@@ -78,8 +78,8 @@ draws the route a dragged row reports its place to, and `retrievable: true` draw
78
78
  one a `Retrieve` button on the table posts to, for rows that came from somewhere else.
79
79
  Both are refused on a resource with no `index`, since there is nowhere for either to
80
80
  stand. The [routes guide](https://claudiob.github.io/recourse/guides/routes.html) has
81
- the rest: namespaces, the defaults a nested resource takes, and the `exit` route that
82
- earns the sidebar a log-out button.
81
+ the rest: namespaces, the defaults a nested resource takes, and the `enter` and `exit`
82
+ routes that earn the sidebar a sign-in link and a log-out button.
83
83
 
84
84
  ## Step 2. Enhance your models
85
85
 
@@ -211,7 +211,8 @@ Recourse.bookmarks = -> { Keepsake.where agent: Current.agent }
211
211
 
212
212
  A Proc rather than a relation, because at boot the viewer is nobody. Every model with a
213
213
  `has_many` at that class then opens its table with a square to keep a row by, and kept
214
- rows come first. A model that cannot hold a bookmark gets no column.
214
+ rows come first. A model that cannot hold a bookmark gets no column, and neither does a
215
+ visitor the Proc answers nil for — somebody who never signed in has nowhere to keep one.
215
216
 
216
217
  <img width="3824" height="1220" alt="Image" src="https://github.com/user-attachments/assets/d1db4adb-0b5c-4e41-8bb6-cf72a35288f0" />
217
218
 
@@ -11,7 +11,8 @@
11
11
 
12
12
  <%# The foot of the sidebar: a reader's own say in how the page looks and, where the
13
13
  host drew a route named `exit`, the way out beside it — a button, since ending a
14
- session is never a GET. Each takes half the row and centers its icon in it, so the
14
+ session is never a GET. Each icon says what it does in a tooltip that opens to the right,
15
+ away from the sidebar's edge. Each takes half the row and centers its icon in it, so the
15
16
  toggle alone sits in the middle and the two together sit at a quarter and at three
16
17
  quarters. Which side of the fold the foot lands on is the layout's business, and so
17
18
  is which of the two mode icons shows — the mode may be the system's, which only CSS
@@ -40,21 +41,29 @@
40
41
  <%= tag.button type: 'button', class: 'nav-link border-0 bg-transparent',
41
42
  data: scheme_data do %>
42
43
  <span class='recourse-scheme-light'>
43
- <i class='bi bi-moon-fill'></i>
44
+ <%= tag.i class: 'bi bi-moon-fill', data: tooltip_data(t('recourse.dark_theme'), placement: 'right') %>
44
45
  <span class='recourse-foot-word'><%= t 'recourse.darken' %></span>
45
46
  </span>
46
47
  <span class='recourse-scheme-dark'>
47
- <i class='bi bi-sun-fill'></i>
48
+ <%= tag.i class: 'bi bi-sun-fill', data: tooltip_data(t('recourse.light_theme'), placement: 'right') %>
48
49
  <span class='recourse-foot-word'><%= t 'recourse.lighten' %></span>
49
50
  </span>
50
51
  <% end %>
51
52
  </div>
52
53
  <%# Submitted by the browser rather than by Turbo: a log out ends with a redirect to
53
54
  wherever the host signs people in, which is another origin a fetch cannot follow. -%>
55
+ <%# The way in, where the host drew a route named `enter`: a link, since what it
56
+ leads to is a form rather than a change. -%>
57
+ <% if enter? %>
58
+ <%= link_to enter_path, class: 'nav-link border-0 bg-transparent' do %>
59
+ <%= tag.i class: 'bi bi-box-arrow-in-right', data: tooltip_data(t('recourse.enter'), placement: 'right') %>
60
+ <span class='recourse-foot-word'><%= t 'recourse.enter' %></span>
61
+ <% end %>
62
+ <% end %>
54
63
  <% if exit? %>
55
64
  <%= button_to exit_path, method: :delete, class: 'nav-link border-0 bg-transparent',
56
65
  form: { data: { turbo: false } } do %>
57
- <i class='bi bi-box-arrow-right'></i>
66
+ <%= tag.i class: 'bi bi-box-arrow-right', data: tooltip_data(exit_tooltip, placement: 'right') %>
58
67
  <span class='recourse-foot-word'><%= t 'recourse.exit' %></span>
59
68
  <% end %>
60
69
  <% end %>
@@ -47,12 +47,16 @@ en:
47
47
  create: Create
48
48
  created: "%{record} was created."
49
49
  created_error: "%{model} could not be created."
50
+ dark_theme: Dark theme
50
51
  darken: Dark
51
52
  delete: Delete %{model}
52
53
  deleted: "%{model} was deleted."
53
54
  deleted_error: "%{model} could not be deleted."
54
55
  destroy: Delete
55
56
  exit: Exit
57
+ sign_out: Sign out
58
+ sign_out_as: "Sign out %{name}"
59
+ enter: Sign in
56
60
  displaying:
57
61
  one: Displaying 1 item
58
62
  other: Displaying %{count} items
@@ -66,6 +70,7 @@ en:
66
70
  items:
67
71
  one: 1 item
68
72
  other: "%{count} items"
73
+ light_theme: Light theme
69
74
  lighten: Light
70
75
  missing_model: You declared `recourses :%{name}` in your routes file, but this app
71
76
  has no %{model} model.
@@ -17,7 +17,8 @@ module Recourse
17
17
  # would run a host's Proc long before there is a request for it to read.
18
18
  def self.bookmarks? = !@declared_bookmarks.nil?
19
19
 
20
- # The viewer's bookmarks, resolved for this request.
20
+ # The viewer's bookmarks, resolved for this request, or nil where the Proc answered
21
+ # nil: nobody is looking whose rows could be kept, a visitor who never signed in.
21
22
  def self.bookmarks
22
23
  bookmarks = @declared_bookmarks
23
24
  bookmarks = bookmarks.call if bookmarks.respond_to? :call
@@ -28,10 +29,11 @@ module Recourse
28
29
  # How a model's bookmarks point back at it: its own `has_many` at the bookmark
29
30
  # class, which is both the opt-in and everything needed to write one. A model that
30
31
  # cannot hold a bookmark has not declared one, so it gets no column.
32
+ # And none for a viewer the Proc named nobody, who has nowhere to keep a row.
31
33
  def self.bookmarks_for(model)
32
34
  return unless bookmarks?
33
35
 
34
- klass = bookmarks.klass
36
+ klass = bookmarks&.klass or return
35
37
  model.reflect_on_all_associations(:has_many).find { |one| one.klass == klass }
36
38
  end
37
39
 
@@ -15,23 +15,38 @@ module Recourse
15
15
  end
16
16
 
17
17
  # The places, with the layer they are areas on — pins where the model is no geography
18
- # — or the points, where a model keeps no place.
18
+ # — or the points, where a model keeps no place. Each carries where its row leads and
19
+ # what it is called, so a pin or an area is clicked through to the record the way the
20
+ # row's own link in the table is.
19
21
  def map_rows(recourses)
20
22
  return { map_points_value: map_points(recourses) } unless Recourse.placed? resource_model
21
23
 
22
24
  {
23
25
  map_boundary_value: Recourse.boundary(resource_model)&.to_s&.upcase,
24
- map_places_value: recourses.filter_map { |one| one.attributes[Recourse::PLACE_COLUMN] },
26
+ map_places_value: map_places(recourses),
25
27
  }
26
28
  end
27
29
 
28
- # Only a row with both halves is a point; one with either missing is nowhere.
30
+ # Each place as `[id, href, title]`, for a row that names one.
31
+ def map_places(recourses)
32
+ recourses.filter_map do |one|
33
+ place = one.attributes[Recourse::PLACE_COLUMN]
34
+ [place, *map_lead(one)] if place
35
+ end
36
+ end
37
+
38
+ # Each point as `[lat, lng, href, title]`. Only a row with both halves is a point; one
39
+ # with either missing is nowhere.
29
40
  def map_points(recourses)
30
41
  recourses.filter_map do |one|
31
42
  point = one.attributes.values_at(*Recourse::POINT_COLUMNS)
32
- point.map(&:to_f) if point.all?
43
+ [*point.map(&:to_f), *map_lead(one)] if point.all?
33
44
  end
34
45
  end
46
+
47
+ # Where a row's pin leads, the record's own page wherever one is routed, and what the
48
+ # pin says on hover: nothing to lead to where no page is.
49
+ def map_lead(record) = [resource_action_path(:show, record), Recourse.record_title(record)]
35
50
  end
36
51
  end
37
52
  end
@@ -10,6 +10,22 @@ module Recourse
10
10
  # convention, and its helper existing is what the sidebar asks.
11
11
  def exit? = respond_to? :exit_path
12
12
 
13
+ # And the way in, its mirror: a route named `enter` — `resource :session, only:
14
+ # :new, as: :enter`, or a `get` of the host's own — earns a link beside the toggle
15
+ # to wherever the host signs people in. A link rather than a button, since asking
16
+ # for a form to sign in with changes nothing. Whether a viewer who already signed
17
+ # in still sees it is the host's to say, by answering this for them.
18
+ def enter? = respond_to? :enter_path
19
+
20
+ # Who is signed in, as the way out names them: `Sign out N-100L`. Nil, as it is where
21
+ # the host does not say, leaves the tooltip at `Sign out`.
22
+ def exit_name = nil
23
+
24
+ # What the way out's tooltip says.
25
+ def exit_tooltip
26
+ exit_name.present? ? t('recourse.sign_out_as', name: exit_name) : t('recourse.sign_out')
27
+ end
28
+
13
29
  # Sidebar entries as [name, title, path, key], in the order routes.rb declares
14
30
  # them, `key` being where in the title the letter that reaches it sits.
15
31
  def sidebar_resources
@@ -1,4 +1,4 @@
1
1
  module Recourse
2
2
  # Version of the gem, read by the gemspec and by hosts checking compatibility.
3
- VERSION = '7.8.3'
3
+ VERSION = '7.10.0'
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recourse
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.8.3
4
+ version: 7.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '6.7'
46
+ version: '6.10'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '6.7'
53
+ version: '6.10'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: pagy
56
56
  requirement: !ruby/object:Gem::Requirement