keystone_ui 0.33.0 → 0.34.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/app/assets/javascripts/keystone_ui/column_picker_controller.js +49 -19
- data/app/components/keystone/ui/column_picker_component.html.erb +21 -9
- data/app/components/keystone/ui/column_picker_component.rb +2 -0
- data/app/components/keystone/ui/data_table_component.rb +10 -1
- data/lib/keystone_ui/version.rb +1 -1
- data/the_local/agents/keystone_ui-develop.md +33 -13
- data/the_local/agents/keystone_ui-info.md +15 -5
- data/the_local/agents/keystone_ui-install.md +36 -14
- 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: ac460729f806dac1343306018aab4560b2c75f39ad05ec5a775886c24f5084fb
|
|
4
|
+
data.tar.gz: 89e7107f63d2d0b6f4b2b27907c464b09cc44375d12fc59e7958bae8e99efbb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '097bdb96a57af1b3b1f886b522fa4bd3a5be40f89b433be36df0c5c74ae895ba4c5dda568a6bbea8c5e32743ee6d072cd61ba553ee9d381fc90c8917399b768f'
|
|
7
|
+
data.tar.gz: e0b238491fc14b93e2cb32e67871290bb8a5a0d606f3663c3c839a62964ce2c8a954d1f6242a158da01713eb7e1b3565f2b3371b46c7e54607cc9e4aae77967a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
|
2
2
|
|
|
3
3
|
export default class extends Controller {
|
|
4
|
-
static targets = ["menu"]
|
|
4
|
+
static targets = ["menu", "option"]
|
|
5
5
|
static values = { saveUrl: String }
|
|
6
6
|
|
|
7
7
|
connect() {
|
|
@@ -25,23 +25,53 @@ export default class extends Controller {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
save() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
this.send(this.columnOrder())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
moveUp(event) {
|
|
32
|
+
this.move(event, -1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
moveDown(event) {
|
|
36
|
+
this.move(event, 1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
move(event, step) {
|
|
40
|
+
const order = this.columnOrder()
|
|
41
|
+
const index = this.optionTargets.indexOf(event.currentTarget.closest('[data-column-picker-target="option"]'))
|
|
42
|
+
const [key] = order.splice(index, 1)
|
|
43
|
+
order.splice(index + step, 0, key)
|
|
44
|
+
this.send(order)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
columnOrder() {
|
|
48
|
+
return this.optionTargets.map(option => this.checkboxIn(option).value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
hiddenColumns() {
|
|
52
|
+
return this.optionTargets
|
|
53
|
+
.map(option => this.checkboxIn(option))
|
|
54
|
+
.filter(checkbox => !checkbox.checked)
|
|
55
|
+
.map(checkbox => checkbox.value)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
checkboxIn(option) {
|
|
59
|
+
return option.querySelector("input[type=checkbox]")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
send(columnOrder) {
|
|
63
|
+
if (!this.hasSaveUrlValue) return
|
|
64
|
+
|
|
65
|
+
const token = document.querySelector('meta[name="csrf-token"]')?.content
|
|
66
|
+
fetch(this.saveUrlValue, {
|
|
67
|
+
method: "PATCH",
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
"X-CSRF-Token": token
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({ hidden_columns: this.hiddenColumns(), column_order: columnOrder })
|
|
73
|
+
}).then(() => {
|
|
74
|
+
Turbo.visit(window.location.href, { action: "replace" })
|
|
75
|
+
})
|
|
46
76
|
}
|
|
47
77
|
}
|
|
@@ -6,15 +6,27 @@
|
|
|
6
6
|
Columns
|
|
7
7
|
</button>
|
|
8
8
|
<div data-column-picker-target="menu" class="<%= MENU_CLASSES %>">
|
|
9
|
-
<% hideable_columns.
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
<% hideable_columns.each_with_index do |col, index| %>
|
|
10
|
+
<div class="<%= OPTION_ROW_CLASSES %>" data-column-picker-target="option">
|
|
11
|
+
<label class="<%= OPTION_CLASSES %>">
|
|
12
|
+
<input type="checkbox"
|
|
13
|
+
value="<%= col.key %>"
|
|
14
|
+
<%= "checked" unless hidden?(col.key) %>
|
|
15
|
+
data-action="change->column-picker#save"
|
|
16
|
+
class="<%= CHECKBOX_CLASSES %>">
|
|
17
|
+
<%= col.header_text %>
|
|
18
|
+
</label>
|
|
19
|
+
<button type="button"
|
|
20
|
+
data-action="click->column-picker#moveUp"
|
|
21
|
+
aria-label="Move up"
|
|
22
|
+
<%= "disabled" if index.zero? %>
|
|
23
|
+
class="<%= MOVE_BUTTON_CLASSES %>">↑</button>
|
|
24
|
+
<button type="button"
|
|
25
|
+
data-action="click->column-picker#moveDown"
|
|
26
|
+
aria-label="Move down"
|
|
27
|
+
<%= "disabled" if index == hideable_columns.size - 1 %>
|
|
28
|
+
class="<%= MOVE_BUTTON_CLASSES %>">↓</button>
|
|
29
|
+
</div>
|
|
18
30
|
<% end %>
|
|
19
31
|
</div>
|
|
20
32
|
</div>
|
|
@@ -8,6 +8,8 @@ module Keystone
|
|
|
8
8
|
MENU_CLASSES = "ks-menu absolute right-0 z-10 w-56 hidden"
|
|
9
9
|
OPTION_CLASSES = "ks-menu-option flex items-center text-sm cursor-pointer"
|
|
10
10
|
CHECKBOX_CLASSES = "ks-menu-checkbox"
|
|
11
|
+
OPTION_ROW_CLASSES = "flex items-center"
|
|
12
|
+
MOVE_BUTTON_CLASSES = "ks-menu-move text-sm"
|
|
11
13
|
|
|
12
14
|
COLUMNS_ICON = <<~SVG.freeze
|
|
13
15
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4">
|
|
@@ -146,11 +146,20 @@ module Keystone
|
|
|
146
146
|
saved = KeystoneUi.configuration.supplied_preference(helpers, @key)
|
|
147
147
|
return unless saved
|
|
148
148
|
|
|
149
|
-
@hidden_columns = saved[:value]
|
|
149
|
+
@hidden_columns = saved[:value].to_h.fetch("hidden_columns", @hidden_columns)
|
|
150
|
+
@all_columns = ordered_columns(saved[:value].to_h["column_order"])
|
|
150
151
|
@columns = visible_columns(@hidden_columns)
|
|
151
152
|
@save_url = saved[:save_url]
|
|
152
153
|
end
|
|
153
154
|
|
|
155
|
+
def ordered_columns(column_order)
|
|
156
|
+
return @all_columns unless column_order
|
|
157
|
+
|
|
158
|
+
positions = Array(column_order).map(&:to_sym).each_with_index.to_h
|
|
159
|
+
hideable = @all_columns.select(&:hideable?).sort_by.with_index { |col, index| [ positions.fetch(col.key, positions.size + index) ] }
|
|
160
|
+
@all_columns.map { |col| col.hideable? ? hideable.shift : col }
|
|
161
|
+
end
|
|
162
|
+
|
|
154
163
|
def visible_columns(hidden_columns)
|
|
155
164
|
hidden_keys = Array(hidden_columns).map(&:to_sym).to_set
|
|
156
165
|
@all_columns.reject { |col| col.hideable? && hidden_keys.include?(col.key) }
|
data/lib/keystone_ui/version.rb
CHANGED
|
@@ -224,21 +224,39 @@ outer element. See Conventions before using it.
|
|
|
224
224
|
server-side and only affects columns declared `hideable: true`. `key:` (a
|
|
225
225
|
symbol or string) names the table so it looks up a saved layout through the
|
|
226
226
|
app's preference supplier. When the supplier returns a saved value for the
|
|
227
|
-
key
|
|
228
|
-
ones passed in `hidden_columns:`, and
|
|
229
|
-
|
|
227
|
+
key that lists `"hidden_columns"`, the hideable columns in that list replace
|
|
228
|
+
the ones passed in `hidden_columns:`, and a saved empty list shows every
|
|
229
|
+
column. When the saved value is `nil` or lists no `"hidden_columns"`, the
|
|
230
|
+
table keeps the columns `hidden_columns:` hides. When the saved value lists
|
|
231
|
+
`"column_order"`, an array of column keys, the hideable columns render in
|
|
232
|
+
that order, followed by any hideable columns the list leaves out in the order
|
|
233
|
+
they were declared. Columns that are not hideable keep their declared place,
|
|
234
|
+
and the hideable ones fill the remaining places. With no `"column_order"` the
|
|
235
|
+
columns keep their declared order. Whenever the supplier
|
|
236
|
+
returns a save address, the table renders a "Columns" menu above itself that
|
|
237
|
+
saves to it, including for a person with nothing saved yet. When the
|
|
230
238
|
supplier returns nothing, when no supplier is set, or when no `key:` is
|
|
231
|
-
passed, the table renders from `hidden_columns:` with no Columns menu
|
|
232
|
-
`hidden_columns:` is the table's default layout.
|
|
239
|
+
passed, the table renders from `hidden_columns:` with no Columns menu. In
|
|
240
|
+
every case `hidden_columns:` is the table's default layout.
|
|
233
241
|
- `Keystone::Ui::Column.new(key, header_text, mobile_hidden: false, sortable: false, hideable: false)`
|
|
234
242
|
— a column with per-column options, for when a `{ key: "Label" }` hash is not
|
|
235
243
|
enough. `mobile_hidden:` hides the column below `sm:`; `sortable:` opts it into
|
|
236
|
-
sort headers; `hideable:` lets the
|
|
244
|
+
sort headers; `hideable:` lets the Columns menu hide it and move it, and a
|
|
245
|
+
saved `"column_order"` place it.
|
|
237
246
|
- `ui_column_picker(columns:, hidden_columns: [], save_url: nil)` — a "Columns"
|
|
238
|
-
dropdown
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
dropdown with one row per `hideable` column, in the order `columns:` lists
|
|
248
|
+
them. Each row has a checkbox and an up and a down button that move the
|
|
249
|
+
column one place; the first row's up button and the last row's down button
|
|
250
|
+
are disabled. Pass it the same columns, in the order the table shows them,
|
|
251
|
+
and the same hidden keys as the table. On a checkbox change or a move it
|
|
252
|
+
sends `PATCH save_url` with JSON
|
|
253
|
+
`{ "hidden_columns": ["key", ...], "column_order": ["key", ...] }` and a
|
|
254
|
+
`X-CSRF-Token` header, then reloads the page. `column_order` lists every
|
|
255
|
+
hideable column's key in the menu's order after the move. With no
|
|
256
|
+
`save_url:` it sends nothing. The app must provide that endpoint and persist
|
|
257
|
+
both lists. The picker does not reorder the table: beside a table without
|
|
258
|
+
`key:`, the app must pass the table and the picker its columns in the saved
|
|
259
|
+
order itself. A table
|
|
242
260
|
given `key:` renders its own Columns menu when the supplier gives a save
|
|
243
261
|
address, so never add `ui_column_picker` beside such a table.
|
|
244
262
|
|
|
@@ -515,9 +533,11 @@ outer element. See Conventions before using it.
|
|
|
515
533
|
For a table whose hideable columns a user should be able to choose and keep,
|
|
516
534
|
check whether the app's Keystone UI initializer sets a
|
|
517
535
|
`preference_supplier`. If it does, pass `key:` and the default
|
|
518
|
-
`hidden_columns:`, and add no `ui_column_picker`.
|
|
519
|
-
|
|
520
|
-
|
|
536
|
+
`hidden_columns:`, and add no `ui_column_picker`. The order the columns are
|
|
537
|
+
declared in is the default order, and only columns declared
|
|
538
|
+
`hideable: true` can be hidden or moved from the Columns menu. Which key
|
|
539
|
+
names the table, which columns are hideable, and which it hides by default
|
|
540
|
+
are the developer's choice, so ask rather than pick. If the app sets no supplier, either use `ui_column_picker`
|
|
521
541
|
with an endpoint the app owns, as in step 7, or hand setting up a supplier to
|
|
522
542
|
`keystone_ui-install`, and ask the developer which.
|
|
523
543
|
|
|
@@ -127,11 +127,21 @@ holds the catalog.
|
|
|
127
127
|
- **Saved table layouts.** A data table can be given a key naming it. A table
|
|
128
128
|
with a key asks the app, or another gem such as keystone_ui-preferences, for
|
|
129
129
|
the layout saved under that key for the request. A saved layout lists the
|
|
130
|
-
columns to hide, and only columns marked hideable can be hidden.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
columns to hide, and only columns marked hideable can be hidden. The columns
|
|
131
|
+
the table's own call hides are its default layout. A saved layout that lists
|
|
132
|
+
columns to hide replaces that default, and a saved empty list shows every
|
|
133
|
+
column. When nothing is saved, or the saved layout lists no columns to hide,
|
|
134
|
+
the table keeps its default. A saved layout can also list an order for the
|
|
135
|
+
columns. Only hideable columns move: they fill the places hideable columns
|
|
136
|
+
held in the table's own call, in the saved order, and any the order leaves
|
|
137
|
+
out follow the ones it names. Every other column keeps its place, and a
|
|
138
|
+
layout with no order keeps the order the columns were declared in. When the
|
|
139
|
+
supplier gives an address to save to, a "Columns" menu appears above the
|
|
140
|
+
table and saves the user's choice there, even before anything has been
|
|
141
|
+
saved. The menu lists the hideable columns in the order the table shows
|
|
142
|
+
them, each with a box to show or hide it and buttons to move it up or down.
|
|
143
|
+
Ticking a box or moving a column saves the hidden columns and the order
|
|
144
|
+
together, then reloads the page.
|
|
135
145
|
A table with no key, or an app with no supplier, renders from its own call
|
|
136
146
|
alone. Setting up the supplier belongs to the install local.
|
|
137
147
|
- **Suggestions.** A form field can carry a list of suggested values. The
|
|
@@ -26,8 +26,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
26
26
|
`look_supplier` (which registered look a page gets),
|
|
27
27
|
`trail_supplier` (a callable that supplies the breadcrumb trail for form and
|
|
28
28
|
show pages that pass none, and their Back link when they pass none either),
|
|
29
|
-
`preference_supplier` (a callable that supplies the saved
|
|
30
|
-
data table given a `key:`, and the address its Columns menu
|
|
29
|
+
`preference_supplier` (a callable that supplies the saved hidden columns and
|
|
30
|
+
column order of a data table given a `key:`, and the address its Columns menu
|
|
31
|
+
saves to),
|
|
31
32
|
and the `tailwind_imports` and `tailwind_sources` lists (extra CSS files and
|
|
32
33
|
scan paths added to the Tailwind build).
|
|
33
34
|
- `keystone_theme_attributes` — a layout helper placed inside the `<html>` tag.
|
|
@@ -233,19 +234,40 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
233
234
|
where their Back link should come from before enabling the supplier or
|
|
234
235
|
leaving it out.
|
|
235
236
|
- `preference_supplier` — a callable that receives the view and a data
|
|
236
|
-
table's key and returns `{ value:, save_url: }`, or `nil` when
|
|
237
|
-
|
|
238
|
-
table with no `key:`, a supplier returning
|
|
239
|
-
renders with the hidden columns its own call
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
table's key and returns `{ value:, save_url: }`, or `nil` when the person
|
|
238
|
+
can neither see nor save a layout for that key. It is asked only for
|
|
239
|
+
tables rendered with a `key:`. A table with no `key:`, a supplier returning
|
|
240
|
+
`nil`, or no supplier at all renders with the hidden columns its own call
|
|
241
|
+
passes and shows no Columns menu.
|
|
242
|
+
- `value` holds the hidden column names under the string key
|
|
243
|
+
`"hidden_columns"`, such as `{ "hidden_columns" => ["sku"] }`. That list
|
|
244
|
+
replaces the hidden columns the table's own call passes, and an empty list
|
|
245
|
+
shows every column. Only columns marked hideable are hidden.
|
|
246
|
+
- When `value` is `nil`, or has no `"hidden_columns"` string key, the table
|
|
247
|
+
keeps the hidden columns its own call passes. A symbol key is not read, so
|
|
248
|
+
`{ hidden_columns: [...] }` also keeps them.
|
|
249
|
+
- `value` may also hold a list of hideable column names under the string
|
|
250
|
+
key `"column_order"`, such as
|
|
251
|
+
`{ "hidden_columns" => ["sku"], "column_order" => ["price", "sku"] }`.
|
|
252
|
+
The table renders its hideable columns in that order, in the places
|
|
253
|
+
hideable columns hold in its own call, then any hideable columns the list
|
|
254
|
+
leaves out in the order they were declared. Columns that are not hideable
|
|
255
|
+
keep their place, and names that match no hideable column are ignored.
|
|
256
|
+
With no `"column_order"` string key the columns keep their declared order.
|
|
257
|
+
- When nothing is saved yet for a person who may save a layout, return
|
|
258
|
+
`{ value: nil, save_url: }`. The table then shows its own default layout
|
|
259
|
+
with the Columns menu, so the person can save a first layout.
|
|
244
260
|
- When `save_url` is present, the table shows a Columns menu above itself.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
It lists the hideable columns in the order the table shows them, with the
|
|
262
|
+
currently hidden columns unchecked, and gives each column an up and a down
|
|
263
|
+
button. Ticking or unticking a column, or moving one, sends a `PATCH` to
|
|
264
|
+
`save_url` with the page's CSRF token and the JSON body
|
|
265
|
+
`{"hidden_columns": [...], "column_order": [...]}`, then reloads the page.
|
|
266
|
+
`column_order` lists every hideable column's name in the menu's order.
|
|
267
|
+
- The host must have a route and action at `save_url` that store both lists
|
|
268
|
+
for that key. The supplier must return them in `value` under the same
|
|
269
|
+
string keys, `"hidden_columns"` and `"column_order"`, or the saved order
|
|
270
|
+
is not applied. With no `save_url`, the saved layout applies and no menu
|
|
249
271
|
is shown.
|
|
250
272
|
- A companion preferences gem may set this supplier for the app. Ask the
|
|
251
273
|
developer whether the app uses one, or which code stores each user's table
|