keystone_ui 0.32.0 → 0.33.1
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/components/keystone/ui/data_table_component.html.erb +3 -0
- data/app/components/keystone/ui/data_table_component.rb +26 -4
- data/lib/keystone_ui/configuration.rb +5 -1
- data/lib/keystone_ui/version.rb +1 -1
- data/the_local/agents/keystone_ui-develop.md +25 -3
- data/the_local/agents/keystone_ui-info.md +15 -2
- data/the_local/agents/keystone_ui-install.md +30 -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: af9ebb3caf69815bf6272cf2f67028d1f4e7a98b44f5e8fb0c70654a54b84303
|
|
4
|
+
data.tar.gz: a0465bf4038d273df4d29aca184443640f113a73260842c7590e12d040557a18
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 986eb9e5c25dfafa87579cec226b9a7ec3511478a0a402158e2a5279f290311e3e6d513a2f082f331d01e5f2d0fd73463f7bf108a401276530963a2cecbec977
|
|
7
|
+
data.tar.gz: 1a0c99faa3fd4c3c03863adf0a467cd53de595367af6cf015c6ffe6afab5e2b31b7713110341b26aad4dd11b6ecd626146020a9aef9549b7c6e600886e383cfa
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
<% if column_picker %>
|
|
2
|
+
<%= render column_picker %>
|
|
3
|
+
<% end %>
|
|
1
4
|
<div class="<%= Keystone::Ui::DataTableComponent::WRAPPER_CLASSES %>">
|
|
2
5
|
<table class="relative min-w-full divide-y divide-gray-300 dark:divide-white/15">
|
|
3
6
|
<thead class="<%= Keystone::Ui::DataTableComponent::HEAD_CLASSES %>">
|
|
@@ -26,11 +26,12 @@ module Keystone
|
|
|
26
26
|
SORT_DESC_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 3a.75.75 0 0 1 .75.75v10.638l3.96-4.158a.75.75 0 1 1 1.08 1.04l-5.25 5.5a.75.75 0 0 1-1.08 0l-5.25-5.5a.75.75 0 0 1 1.08-1.04l3.96 4.158V3.75A.75.75 0 0 1 10 3Z" clip-rule="evenodd" /></svg>'
|
|
27
27
|
SORT_NEUTRAL_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 3a.75.75 0 0 1 .55.24l3.25 3.5a.75.75 0 1 1-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 0 1-1.1-1.02l3.25-3.5A.75.75 0 0 1 10 3Zm-3.76 9.2a.75.75 0 0 1 1.06.04l2.7 2.908 2.7-2.908a.75.75 0 1 1 1.1 1.02l-3.25 3.5a.75.75 0 0 1-1.1 0l-3.25-3.5a.75.75 0 0 1 .04-1.06Z" clip-rule="evenodd" /></svg>'
|
|
28
28
|
|
|
29
|
-
def initialize(items:, columns:, empty_message: nil, sort: nil, sort_direction: nil, sort_url: nil, hidden_columns: [])
|
|
29
|
+
def initialize(items:, columns:, empty_message: nil, sort: nil, sort_direction: nil, sort_url: nil, hidden_columns: [], key: nil)
|
|
30
30
|
@items = items.to_a
|
|
31
|
-
all_columns = columns.map { |col| normalize_column(col) }
|
|
32
|
-
|
|
33
|
-
@columns =
|
|
31
|
+
@all_columns = columns.map { |col| normalize_column(col) }
|
|
32
|
+
@hidden_columns = hidden_columns
|
|
33
|
+
@columns = visible_columns(hidden_columns)
|
|
34
|
+
@key = key
|
|
34
35
|
@empty_message = empty_message
|
|
35
36
|
@sort = sort&.to_sym
|
|
36
37
|
@sort_direction = sort_direction&.to_sym
|
|
@@ -41,6 +42,7 @@ module Keystone
|
|
|
41
42
|
|
|
42
43
|
def before_render
|
|
43
44
|
content
|
|
45
|
+
apply_saved_layout if @key
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
def link(column_key, &block)
|
|
@@ -55,6 +57,12 @@ module Keystone
|
|
|
55
57
|
!!@actions_block
|
|
56
58
|
end
|
|
57
59
|
|
|
60
|
+
def column_picker
|
|
61
|
+
return unless @save_url
|
|
62
|
+
|
|
63
|
+
@column_picker ||= ColumnPickerComponent.new(columns: @all_columns, hidden_columns: @hidden_columns, save_url: @save_url)
|
|
64
|
+
end
|
|
65
|
+
|
|
58
66
|
def column_keys
|
|
59
67
|
@column_keys ||= @columns.map(&:key)
|
|
60
68
|
end
|
|
@@ -134,6 +142,20 @@ module Keystone
|
|
|
134
142
|
|
|
135
143
|
private
|
|
136
144
|
|
|
145
|
+
def apply_saved_layout
|
|
146
|
+
saved = KeystoneUi.configuration.supplied_preference(helpers, @key)
|
|
147
|
+
return unless saved
|
|
148
|
+
|
|
149
|
+
@hidden_columns = saved[:value].to_h.fetch("hidden_columns", @hidden_columns)
|
|
150
|
+
@columns = visible_columns(@hidden_columns)
|
|
151
|
+
@save_url = saved[:save_url]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def visible_columns(hidden_columns)
|
|
155
|
+
hidden_keys = Array(hidden_columns).map(&:to_sym).to_set
|
|
156
|
+
@all_columns.reject { |col| col.hideable? && hidden_keys.include?(col.key) }
|
|
157
|
+
end
|
|
158
|
+
|
|
137
159
|
def visual_column_count
|
|
138
160
|
@columns.length + (actions? ? 1 : 0)
|
|
139
161
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module KeystoneUi
|
|
4
4
|
class Configuration
|
|
5
|
-
attr_accessor :accent, :surface, :theme_mode_supplier, :look_supplier, :trail_supplier
|
|
5
|
+
attr_accessor :accent, :surface, :theme_mode_supplier, :look_supplier, :trail_supplier, :preference_supplier
|
|
6
6
|
attr_reader :tailwind_imports, :tailwind_sources, :looks, :default_look
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
@@ -33,6 +33,10 @@ module KeystoneUi
|
|
|
33
33
|
def supplied_trail(view)
|
|
34
34
|
trail_supplier&.call(view)
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
def supplied_preference(view, key)
|
|
38
|
+
preference_supplier&.call(view, key)
|
|
39
|
+
end
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def self.configuration
|
data/lib/keystone_ui/version.rb
CHANGED
|
@@ -207,7 +207,7 @@ outer element. See Conventions before using it.
|
|
|
207
207
|
|
|
208
208
|
### Tables
|
|
209
209
|
|
|
210
|
-
- `ui_data_table(items:, columns:, empty_message: nil, sort: nil, sort_direction: nil, sort_url: nil, hidden_columns: [])`
|
|
210
|
+
- `ui_data_table(items:, columns:, empty_message: nil, sort: nil, sort_direction: nil, sort_url: nil, hidden_columns: [], key: nil)`
|
|
211
211
|
— takes a block yielding the table. `items:` are records or hashes; each cell
|
|
212
212
|
value is read by calling the column key on the item, falling back to `item[key]`.
|
|
213
213
|
`columns:` accepts plain `{ key: "Label" }` hashes or `Keystone::Ui::Column`
|
|
@@ -221,7 +221,18 @@ outer element. See Conventions before using it.
|
|
|
221
221
|
current column key), `sort_direction:` (`:asc`/`:desc`), and `sort_url:` (a
|
|
222
222
|
lambda taking `(column_key, direction)` and returning a URL); headers then
|
|
223
223
|
render as links that flip direction. `hidden_columns:` drops columns
|
|
224
|
-
server-side and only affects columns declared `hideable: true`.
|
|
224
|
+
server-side and only affects columns declared `hideable: true`. `key:` (a
|
|
225
|
+
symbol or string) names the table so it looks up a saved layout through the
|
|
226
|
+
app's preference supplier. When the supplier returns a saved value for the
|
|
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. Whenever the supplier
|
|
231
|
+
returns a save address, the table renders a "Columns" menu above itself that
|
|
232
|
+
saves to it, including for a person with nothing saved yet. When the
|
|
233
|
+
supplier returns nothing, when no supplier is set, or when no `key:` is
|
|
234
|
+
passed, the table renders from `hidden_columns:` with no Columns menu. In
|
|
235
|
+
every case `hidden_columns:` is the table's default layout.
|
|
225
236
|
- `Keystone::Ui::Column.new(key, header_text, mobile_hidden: false, sortable: false, hideable: false)`
|
|
226
237
|
— a column with per-column options, for when a `{ key: "Label" }` hash is not
|
|
227
238
|
enough. `mobile_hidden:` hides the column below `sm:`; `sortable:` opts it into
|
|
@@ -230,7 +241,9 @@ outer element. See Conventions before using it.
|
|
|
230
241
|
dropdown of checkboxes for every `hideable` column. Pass it the same columns
|
|
231
242
|
and hidden keys as the table. On toggle it sends `PATCH save_url` with JSON
|
|
232
243
|
`{ "hidden_columns": ["key", ...] }` and a `X-CSRF-Token` header, then reloads
|
|
233
|
-
the page. The app must provide that endpoint and persist the list.
|
|
244
|
+
the page. The app must provide that endpoint and persist the list. A table
|
|
245
|
+
given `key:` renders its own Columns menu when the supplier gives a save
|
|
246
|
+
address, so never add `ui_column_picker` beside such a table.
|
|
234
247
|
|
|
235
248
|
### Content and status
|
|
236
249
|
|
|
@@ -502,6 +515,15 @@ outer element. See Conventions before using it.
|
|
|
502
515
|
objects as soon as one column needs `mobile_hidden:`, `sortable:`, or
|
|
503
516
|
`hideable:`.
|
|
504
517
|
|
|
518
|
+
For a table whose hideable columns a user should be able to choose and keep,
|
|
519
|
+
check whether the app's Keystone UI initializer sets a
|
|
520
|
+
`preference_supplier`. If it does, pass `key:` and the default
|
|
521
|
+
`hidden_columns:`, and add no `ui_column_picker`. Which key names the table
|
|
522
|
+
and which columns it hides by default are the developer's choice, so ask
|
|
523
|
+
rather than pick. If the app sets no supplier, either use `ui_column_picker`
|
|
524
|
+
with an endpoint the app owns, as in step 7, or hand setting up a supplier to
|
|
525
|
+
`keystone_ui-install`, and ask the developer which.
|
|
526
|
+
|
|
505
527
|
7. Wire up anything that posts back. Several helpers render controls whose
|
|
506
528
|
endpoints the app must own — the column picker's save URL, the pipeline's box
|
|
507
529
|
and link URLs, the swipe deck's outcome events, the modal's open trigger, the
|
|
@@ -47,7 +47,8 @@ shell also uses the page container.
|
|
|
47
47
|
This local declares no commands. The two working surfaces belong elsewhere:
|
|
48
48
|
|
|
49
49
|
- **Getting the gem into a host app** — adding it, wiring Tailwind, Stimulus and
|
|
50
|
-
the theme attributes on the layout, setting the palette
|
|
50
|
+
the theme attributes on the layout, setting the palette, and the suppliers
|
|
51
|
+
for trails, themes, looks and saved table layouts → **`keystone_ui-install`**.
|
|
51
52
|
- **Building UI with it** — which helper renders what, what keywords it takes,
|
|
52
53
|
how helpers nest → **`keystone_ui-develop`**.
|
|
53
54
|
|
|
@@ -123,6 +124,18 @@ holds the catalog.
|
|
|
123
124
|
- **The last table column is right-aligned.** In a data table with no actions
|
|
124
125
|
column, the last column's header is right-aligned like the cells beneath it,
|
|
125
126
|
so a figure or total placed in that header lines up with the figures below.
|
|
127
|
+
- **Saved table layouts.** A data table can be given a key naming it. A table
|
|
128
|
+
with a key asks the app, or another gem such as keystone_ui-preferences, for
|
|
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. 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. When the supplier gives an address to save to,
|
|
135
|
+
a "Columns" menu appears above the table and saves the user's choice there,
|
|
136
|
+
even before anything has been saved.
|
|
137
|
+
A table with no key, or an app with no supplier, renders from its own call
|
|
138
|
+
alone. Setting up the supplier belongs to the install local.
|
|
126
139
|
- **Suggestions.** A form field can carry a list of suggested values. The
|
|
127
140
|
browser offers them while the field's text is typed, and the user can still
|
|
128
141
|
enter a value that is not on the list. This applies to a field with a typed
|
|
@@ -208,5 +221,5 @@ holds the catalog.
|
|
|
208
221
|
and the theme toggle ship with the gem as Stimulus controllers registered once
|
|
209
222
|
at install. A host writes no JavaScript to use them. Components that post
|
|
210
223
|
somewhere, such as the column picker and the pipeline, post to endpoints the
|
|
211
|
-
host app owns.
|
|
224
|
+
host app or another gem owns.
|
|
212
225
|
- Ruby >= 3.2. ViewComponent >= 2.0 and < 5.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keystone_ui-install
|
|
3
|
-
description: Use to hook Keystone UI into a project — adding the gem, running the install generator to wire Tailwind, the Stimulus controllers and the layout's theme attributes, and configuring the palette, the theme mode supplier, registered looks, the breadcrumb trail supplier and extra Tailwind imports and sources.
|
|
3
|
+
description: Use to hook Keystone UI into a project — adding the gem, running the install generator to wire Tailwind, the Stimulus controllers and the layout's theme attributes, and configuring the palette, the theme mode supplier, registered looks, the breadcrumb trail supplier, the saved table layout supplier and extra Tailwind imports and sources.
|
|
4
4
|
tools: Bash, Read, Edit
|
|
5
5
|
scope: UI — pages, forms, tables, navigation, dashboards
|
|
6
6
|
---
|
|
@@ -26,6 +26,8 @@ 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 column layout of a
|
|
30
|
+
data table given a `key:`, and the address its Columns menu saves to),
|
|
29
31
|
and the `tailwind_imports` and `tailwind_sources` lists (extra CSS files and
|
|
30
32
|
scan paths added to the Tailwind build).
|
|
31
33
|
- `keystone_theme_attributes` — a layout helper placed inside the `<html>` tag.
|
|
@@ -179,6 +181,7 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
179
181
|
config.default_look = :compact
|
|
180
182
|
config.look_supplier = ->(view) { view.current_user&.look }
|
|
181
183
|
config.trail_supplier = ->(view) { view.breadcrumb_trail }
|
|
184
|
+
config.preference_supplier = ->(view, key) { TablePreferences.for(view.current_user, key) }
|
|
182
185
|
config.tailwind_imports << "/absolute/path/to/extra.css"
|
|
183
186
|
config.tailwind_sources << "/absolute/path/to/components/**/*.{erb,rb}"
|
|
184
187
|
end
|
|
@@ -229,6 +232,32 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
229
232
|
writing the callable. If some form or show pages pass no `back_url:`, ask
|
|
230
233
|
where their Back link should come from before enabling the supplier or
|
|
231
234
|
leaving it out.
|
|
235
|
+
- `preference_supplier` — a callable that receives the view and a data
|
|
236
|
+
table's key and returns `{ value:, save_url: }`, or `nil` when the person
|
|
237
|
+
can neither see nor save a layout for that key. It is asked only for
|
|
238
|
+
tables rendered with a `key:`. A table with no `key:`, a supplier returning
|
|
239
|
+
`nil`, or no supplier at all renders with the hidden columns its own call
|
|
240
|
+
passes and shows no Columns menu.
|
|
241
|
+
- `value` holds the hidden column names under the string key
|
|
242
|
+
`"hidden_columns"`, such as `{ "hidden_columns" => ["sku"] }`. That list
|
|
243
|
+
replaces the hidden columns the table's own call passes, and an empty list
|
|
244
|
+
shows every column. Only columns marked hideable are hidden.
|
|
245
|
+
- When `value` is `nil`, or has no `"hidden_columns"` string key, the table
|
|
246
|
+
keeps the hidden columns its own call passes. A symbol key is not read, so
|
|
247
|
+
`{ hidden_columns: [...] }` also keeps them.
|
|
248
|
+
- When nothing is saved yet for a person who may save a layout, return
|
|
249
|
+
`{ value: nil, save_url: }`. The table then shows its own default layout
|
|
250
|
+
with the Columns menu, so the person can save a first layout.
|
|
251
|
+
- When `save_url` is present, the table shows a Columns menu above itself,
|
|
252
|
+
with the currently hidden columns unchecked.
|
|
253
|
+
Toggling a column sends a `PATCH` to `save_url` with the JSON body
|
|
254
|
+
`{"hidden_columns": [...]}` and the page's CSRF token, then reloads the
|
|
255
|
+
page. The host must have a route and action at that address that store the
|
|
256
|
+
list for that key. With no `save_url`, the saved layout applies and no menu
|
|
257
|
+
is shown.
|
|
258
|
+
- A companion preferences gem may set this supplier for the app. Ask the
|
|
259
|
+
developer whether the app uses one, or which code stores each user's table
|
|
260
|
+
layouts, before writing the callable.
|
|
232
261
|
- `tailwind_imports` and `tailwind_sources` — lists to append to, never
|
|
233
262
|
assign. Each import becomes an `@import` line and each source becomes an
|
|
234
263
|
`@source` line in `keystone_source.css` on the next boot. They are for
|