recourse 5.1.0 → 5.2.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 +17 -0
- data/README.md +18 -0
- data/app/controllers/recourse/base_controller.rb +2 -0
- data/app/views/layouts/recourses.html.erb +2 -2
- data/app/views/recourses/_footer.html.erb +6 -0
- data/app/views/recourses/_map.html.erb +13 -0
- data/app/views/recourses/_results.html.erb +1 -1
- data/config/locales/recourse.en.yml +2 -0
- data/lib/recourse/engine.rb +6 -0
- data/lib/recourse/helpers/maps.rb +52 -0
- data/lib/recourse/helpers.rb +2 -1
- data/lib/recourse/maps.rb +31 -0
- data/lib/recourse/themes.rb +1 -1
- data/lib/recourse/version.rb +1 -1
- data/lib/recourse.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70333646b284ff615c653da393325f5ea94bb7bd04f73f46b9bff27e5d693a1c
|
|
4
|
+
data.tar.gz: 1b386bf8262585fa5d3d5493e4297ea3521bac6105f8f9fffba64ecbdc7bd12d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88b168334941dfe01535414620b28dffea6ceedf1583e023f586b71f33c5f6eda669c900a45bd293b28c0d2b2f262c89bfd276f6bc396f08f05bc1c9a2e062c9
|
|
7
|
+
data.tar.gz: b890abc1502680ad0fcbf1ba27bfcbca86743bbb051d2e30bc9ea171cc87f495885baa8158c3b672efb7549b64370fc2e9068883a9ecbc2c2f522d8d2ffe6c86
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 5.2.0 - 2026-09-14
|
|
11
|
+
|
|
12
|
+
* [Feature] A table of places can be read as a map
|
|
13
|
+
|
|
14
|
+
A model keeping a `google_place_id`, or a `latitude` and a `longitude`, earns a second
|
|
15
|
+
shape for its index: `/counties.map` draws the page on a Google map, in the frame and
|
|
16
|
+
over the footer the table has, so the search, the sort and the pages work the same on
|
|
17
|
+
either. The footer under the table offers `Display as map` and the one under the map
|
|
18
|
+
`Display as table`, each keeping the page the other was on. A place ID is filled in as
|
|
19
|
+
an area where the model is a geography Google draws boundaries for — a `State`, a `County`,
|
|
20
|
+
a `City` or a `ZIP`, by name — and pinned at the place for any other model; a point is
|
|
21
|
+
pinned as it is. The key and the map style, with those
|
|
22
|
+
layers turned on, are read from the host's credentials under `google_maps` as `api_key`
|
|
23
|
+
and `map_id`. `.map` is registered as a name for HTML, and the index renders its HTML
|
|
24
|
+
templates for it. The design bundle is pinned at v0.5.0, which carries the `map`
|
|
25
|
+
controller.
|
|
26
|
+
|
|
10
27
|
## 5.1.0 - 2026-09-14
|
|
11
28
|
|
|
12
29
|
* [Feature] The head past the gem's own tags is the host's, through a `recourses/head` partial
|
data/README.md
CHANGED
|
@@ -144,6 +144,24 @@ Recourse.bookmarks = -> { Keepsake.where agent: Current.agent }
|
|
|
144
144
|
|
|
145
145
|
<img width="3824" height="1220" alt="Image" src="https://github.com/user-attachments/assets/d1db4adb-0b5c-4e41-8bb6-cf72a35288f0" />
|
|
146
146
|
|
|
147
|
+
A table whose model keeps a `google_place_id`, or a `latitude` and a `longitude`, can be
|
|
148
|
+
read as a Google map of the same page: the footer under it offers `Display as map`, and
|
|
149
|
+
`/counties.map` draws this page's rows in the frame and over the footer the table has, so
|
|
150
|
+
search, sort and pages work the same on either shape. A place ID is filled in as an area
|
|
151
|
+
where the model is a geography Google draws boundaries for — a `State`, a `County`, a `City`
|
|
152
|
+
or a `ZIP`, by name — and pinned at the place for any other model; a point is a pin. The key
|
|
153
|
+
and the map ID are the host's credentials, and the map's style has the matching **Feature
|
|
154
|
+
layers** turned on in the Cloud console — Postal code for ZIPs, Administrative area level 2
|
|
155
|
+
for counties. A host with a Content Security Policy allows `maps.googleapis.com` for scripts
|
|
156
|
+
and connections and Google's tile hosts for images.
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
# config/credentials.yml.enc
|
|
160
|
+
google_maps:
|
|
161
|
+
api_key: AIza…
|
|
162
|
+
map_id: 4f2a…
|
|
163
|
+
```
|
|
164
|
+
|
|
147
165
|
|
|
148
166
|
## Development
|
|
149
167
|
|
|
@@ -24,6 +24,8 @@ module Recourse
|
|
|
24
24
|
search = Search.new recourse_relation, params[:q]
|
|
25
25
|
@q = search.query
|
|
26
26
|
@pagy, @resources = pagy search.scope, limit: recourse_limit
|
|
27
|
+
# A map is the same page in another shape, so the HTML templates draw it.
|
|
28
|
+
render :index, formats: :html if request.format.map?
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
# Builds a blank record under the name Rails would use: @contact for contacts, with
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
over. -%>
|
|
26
26
|
<link rel='preconnect' href='https://design.houseaccount.com'>
|
|
27
27
|
<link rel='preconnect' href='https://design.houseaccount.com' crossorigin>
|
|
28
|
-
<link rel='stylesheet' href='https://design.houseaccount.com/v0.
|
|
28
|
+
<link rel='stylesheet' href='https://design.houseaccount.com/v0.5.0/css/houseaccount.css'>
|
|
29
29
|
<%# The one thing these screens set that the house's stylesheet does not: system faces
|
|
30
30
|
at a compact size, for pages dense with figures. 14px as rem, so a browser's own
|
|
31
31
|
text-size setting still scales it. After the stylesheet, since both are `:root`. -%>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<%# Turbo, Bootstrap and a Stimulus application with every controller this markup
|
|
50
50
|
names, started once under `window.Stimulus`. In the head, so a controller exists
|
|
51
51
|
before the body's first `data-controller`. -%>
|
|
52
|
-
<script type='module' src='https://design.houseaccount.com/v0.
|
|
52
|
+
<script type='module' src='https://design.houseaccount.com/v0.5.0/js/houseaccount.js'></script>
|
|
53
53
|
</head>
|
|
54
54
|
|
|
55
55
|
<%# The reader's zone is told to the server here rather than from a page of its
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
<% else %>
|
|
19
19
|
<%= t 'recourse.displaying', count: pagy.count %>
|
|
20
20
|
<% end %>
|
|
21
|
+
<%# And the other shape the same rows can take, where the model keeps a place or
|
|
22
|
+
a point: a table becomes a map of this page's rows, and a map a table again. -%>
|
|
23
|
+
<% if (other = view_link) %>
|
|
24
|
+
<span class='fg-2'>·</span>
|
|
25
|
+
<%= other %>
|
|
26
|
+
<% end %>
|
|
21
27
|
</div>
|
|
22
28
|
<%# Centered on the row of their own a phone gives them, and at the end of the shared row from `sm` up. -%>
|
|
23
29
|
<div class='px-2 mt-2 sm:mt-0 d-flex justify-content-center sm:justify-content-end'><%== pagy.series_nav(:bootstrap, classes: 'pagination mb-0') if pagy.last > 1 %></div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<%# locals: (recourses:, pagy:) %>
|
|
2
|
+
<%# This page of the rows on a Google map, each place's area filled in or pinned, in the
|
|
3
|
+
frame and over the footer the table has — so the search, the sort and the pages all
|
|
4
|
+
work the same on either shape. Never cached: the map is drawn by the browser. In a
|
|
5
|
+
card, which gives it the border and the corners the page's other card has —
|
|
6
|
+
`overflow` since the map is a canvas that would otherwise square the corners off —
|
|
7
|
+
and under it the margin a table keeps, so the footer stands as far from the map as
|
|
8
|
+
it does from the rows. -%>
|
|
9
|
+
<div class='card overflow-hidden mb-3'>
|
|
10
|
+
<div class='recourse-map' <%= tag.attributes data: map_data(recourses) %>></div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<%= render 'footer', pagy: pagy %>
|
data/lib/recourse/engine.rb
CHANGED
|
@@ -14,5 +14,11 @@ module Recourse
|
|
|
14
14
|
initializer 'recourse.routes' do
|
|
15
15
|
ActionDispatch::Routing::Mapper.include Scopes, Routes
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
# `/counties.map` is the same page in another shape, so the format is a name for
|
|
19
|
+
# HTML: without it Rails has no type for the extension and answers 406.
|
|
20
|
+
initializer 'recourse.formats' do
|
|
21
|
+
Mime::Type.register_alias 'text/html', :map
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Recourse
|
|
2
|
+
module Helpers
|
|
3
|
+
# The map a table of places is read as, and the link under either that leads to
|
|
4
|
+
# the other.
|
|
5
|
+
module Maps
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# Whether this page is the map rather than the table.
|
|
9
|
+
def map_view? = request.format.map?
|
|
10
|
+
|
|
11
|
+
# The link under the rows to the other shape of the page, or nothing where the
|
|
12
|
+
# model keeps neither a place nor a point to draw one by. The query goes with it —
|
|
13
|
+
# the search, the sort and the page — so the map shows the rows the table did.
|
|
14
|
+
def view_link
|
|
15
|
+
return unless Recourse.mappable? resource_model
|
|
16
|
+
|
|
17
|
+
params = request.query_parameters
|
|
18
|
+
return link_to t('recourse.as_table'), url_for(params.merge(format: nil)) if map_view?
|
|
19
|
+
|
|
20
|
+
link_to t('recourse.as_map'), url_for(params.merge(format: :map))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# What the map controller needs: the key and the map from the host's credentials,
|
|
24
|
+
# and this page's rows as the places or the points they keep.
|
|
25
|
+
def map_data(recourses)
|
|
26
|
+
keys = Recourse.google_maps
|
|
27
|
+
|
|
28
|
+
{ controller: 'map', map_key_value: keys[:api_key], map_id_value: keys[:map_id] }
|
|
29
|
+
.merge map_rows(recourses)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The places, with the layer they are areas on — pins where the model is no geography
|
|
33
|
+
# — or the points, where a model keeps no place.
|
|
34
|
+
def map_rows(recourses)
|
|
35
|
+
return { map_points_value: map_points(recourses) } unless Recourse.placed? resource_model
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
map_boundary_value: Recourse.boundary(resource_model)&.to_s&.upcase,
|
|
39
|
+
map_places_value: recourses.filter_map { |one| one.attributes[Recourse::PLACE_COLUMN] },
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Only a row with both halves is a point; one with either missing is nowhere.
|
|
44
|
+
def map_points(recourses)
|
|
45
|
+
recourses.filter_map do |one|
|
|
46
|
+
point = one.attributes.values_at(*Recourse::POINT_COLUMNS)
|
|
47
|
+
point.map(&:to_f) if point.all?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/recourse/helpers.rb
CHANGED
|
@@ -19,6 +19,7 @@ require_relative 'helpers/formats'
|
|
|
19
19
|
require_relative 'helpers/inputs'
|
|
20
20
|
require_relative 'helpers/kinds'
|
|
21
21
|
require_relative 'helpers/limits'
|
|
22
|
+
require_relative 'helpers/maps'
|
|
22
23
|
require_relative 'helpers/names'
|
|
23
24
|
require_relative 'helpers/navigation'
|
|
24
25
|
require_relative 'helpers/parents'
|
|
@@ -44,7 +45,7 @@ module Recourse
|
|
|
44
45
|
include Actions, Bookmarks, Buttons, Cards,
|
|
45
46
|
Cells, Choices, Colors, Comboboxes, Constraints, Counters, Deletions, Densities,
|
|
46
47
|
Details,
|
|
47
|
-
Examples, Fields, Filters, Flashes, Formats, Inputs, Kinds, Limits,
|
|
48
|
+
Examples, Fields, Filters, Flashes, Formats, Inputs, Kinds, Limits, Maps,
|
|
48
49
|
Names, Navigation, Parents, Pictures, References, Refreshes,
|
|
49
50
|
Routing,
|
|
50
51
|
Resources, Rows, Searches, Shortcuts, Sidebars, Sorts, Tabs, Themes,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Reopened for the map a table of places can be read as.
|
|
2
|
+
module Recourse
|
|
3
|
+
# The column a row is drawn on a Google map by: the ID Google gives a place.
|
|
4
|
+
PLACE_COLUMN = 'google_place_id'
|
|
5
|
+
|
|
6
|
+
# The two a row is pinned by where it keeps no place: a point needs no lookup.
|
|
7
|
+
POINT_COLUMNS = %w[latitude longitude].freeze
|
|
8
|
+
|
|
9
|
+
# The boundary layer a model's places are areas on, by the name the model goes by —
|
|
10
|
+
# the four geographies a host is likely to keep, in Google's words for each. A model
|
|
11
|
+
# named otherwise has its places pinned.
|
|
12
|
+
BOUNDARIES = {
|
|
13
|
+
state: :administrative_area_level_1, county: :administrative_area_level_2,
|
|
14
|
+
city: :locality, zip: :postal_code,
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# Whether a model's rows can be drawn on a map: by the place each keeps, or its point.
|
|
18
|
+
def self.mappable?(model) = placed?(model) || (POINT_COLUMNS - model.column_names).empty?
|
|
19
|
+
|
|
20
|
+
# The layer a model's places are drawn on, or nil for a model that is no geography.
|
|
21
|
+
def self.boundary(model) = BOUNDARIES[model.model_name.singular.to_sym]
|
|
22
|
+
|
|
23
|
+
# Whether a model's rows keep a place ID, which an area or a pin at a place is drawn from.
|
|
24
|
+
def self.placed?(model) = model.column_names.include? PLACE_COLUMN
|
|
25
|
+
|
|
26
|
+
# The key the Maps API is called with and the map the rows are drawn on, read from the
|
|
27
|
+
# host's own credentials under `google_maps` as `api_key` and `map_id`.
|
|
28
|
+
def self.google_maps
|
|
29
|
+
Rails.application.credentials[:google_maps] || {}
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/recourse/themes.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Recourse
|
|
|
25
25
|
# Where the palettes are served from, named once: the layout links one, the layout's
|
|
26
26
|
# own script puts back the one a reader chose, and the sidebar's controller swaps it.
|
|
27
27
|
# The version is the one the layout links its stylesheet and script at.
|
|
28
|
-
THEMES_PATH = 'https://design.houseaccount.com/v0.
|
|
28
|
+
THEMES_PATH = 'https://design.houseaccount.com/v0.5.0/theme'
|
|
29
29
|
|
|
30
30
|
# Where a reader's chosen palette and mode are kept in their browser. Named once for
|
|
31
31
|
# the same reason: the sidebar's controller writes it and the layout reads it back.
|
data/lib/recourse/version.rb
CHANGED
data/lib/recourse.rb
CHANGED
|
@@ -13,6 +13,7 @@ require_relative 'recourse/controllers'
|
|
|
13
13
|
require_relative 'recourse/densities'
|
|
14
14
|
require_relative 'recourse/helpers'
|
|
15
15
|
require_relative 'recourse/limits'
|
|
16
|
+
require_relative 'recourse/maps'
|
|
16
17
|
require_relative 'recourse/orders'
|
|
17
18
|
require_relative 'recourse/writes'
|
|
18
19
|
require_relative 'recourse/zones'
|
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: 5.
|
|
4
|
+
version: 5.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- claudiob
|
|
@@ -141,6 +141,7 @@ files:
|
|
|
141
141
|
- app/views/recourses/_flash.html.erb
|
|
142
142
|
- app/views/recourses/_footer.html.erb
|
|
143
143
|
- app/views/recourses/_form.html.erb
|
|
144
|
+
- app/views/recourses/_map.html.erb
|
|
144
145
|
- app/views/recourses/_none.html.erb
|
|
145
146
|
- app/views/recourses/_results.html.erb
|
|
146
147
|
- app/views/recourses/_row.html.erb
|
|
@@ -184,6 +185,7 @@ files:
|
|
|
184
185
|
- lib/recourse/helpers/inputs.rb
|
|
185
186
|
- lib/recourse/helpers/kinds.rb
|
|
186
187
|
- lib/recourse/helpers/limits.rb
|
|
188
|
+
- lib/recourse/helpers/maps.rb
|
|
187
189
|
- lib/recourse/helpers/names.rb
|
|
188
190
|
- lib/recourse/helpers/navigation.rb
|
|
189
191
|
- lib/recourse/helpers/parents.rb
|
|
@@ -204,6 +206,7 @@ files:
|
|
|
204
206
|
- lib/recourse/helpers/zones.rb
|
|
205
207
|
- lib/recourse/icons.rb
|
|
206
208
|
- lib/recourse/limits.rb
|
|
209
|
+
- lib/recourse/maps.rb
|
|
207
210
|
- lib/recourse/orders.rb
|
|
208
211
|
- lib/recourse/recoursive.rb
|
|
209
212
|
- lib/recourse/registry.rb
|