keystone_ui 0.80.0 → 0.81.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: 89de110dfb32394a6012de91de4120517f04870224e7ccb13afc436d6174b9ac
4
- data.tar.gz: 6310807810ad3de4e4021fda9bdcec1d0c3d284872a9e70799aeb9419a81425b
3
+ metadata.gz: d3516b268acea8768fce1e6047f13603e698cd447fe6e3fa0f22fed56fca71fb
4
+ data.tar.gz: 2da41656eefaec66eea22dca56955a7410b91b6cce7c5d5f4459172a86f42de0
5
5
  SHA512:
6
- metadata.gz: c2b1f52471717cbe9e10a6a1ea9bf5c2e0656257f7d82cdaf1983ea94ff15a9c62261215f4fdb53e8db934c14ff37638b398451f57da0948ea113bdc54cb1f61
7
- data.tar.gz: 91cf44f8a528063da14e2319f4d1cae925a265bb298d64588ff9102f408821c5206e3ac71bafd291ac673fb29f33ea7a468d8f959c33984654b5d4a0e008513c
6
+ metadata.gz: 5d4b015f5d4821117e4af16452ac86a0f935b39b365730e093b1d7789641d878bcd691573e1c9efe5618d109743080b88f3389ac2d74b403adb1004ba131dc9e
7
+ data.tar.gz: 4baae133e97445b05547688c777a95d10415ad9f5adc0a1af8bd9a0efdbc6317a28a4437a4e449be2f62bb7d3450ea49fee8c959702bfccf1132813d927c80aa
@@ -25,7 +25,7 @@ module Keystone
25
25
  end
26
26
 
27
27
  def button
28
- ButtonComponent.new(label: content_for(:mobile_action_label), href: content_for(:mobile_action_url), method: action_method, full_width: true)
28
+ ButtonComponent.new(label: content_for(:mobile_action_label), href: content_for(:mobile_action_url), method: action_method, confirm: content_for(:mobile_action_confirm), full_width: true)
29
29
  end
30
30
 
31
31
  def action_method
@@ -20,7 +20,7 @@ module Keystone
20
20
 
21
21
  TEXT_CLASSES = "ks-text-button"
22
22
 
23
- def initialize(label:, href: nil, method: :get, variant: :primary, size: :md, full_width: false, type: :submit, data: nil, name: nil, value: nil, hidden: false)
23
+ def initialize(label:, href: nil, method: :get, variant: :primary, size: :md, full_width: false, type: :submit, data: nil, name: nil, value: nil, hidden: false, confirm: nil)
24
24
  @label = label
25
25
  @href = href
26
26
  @method = method.to_sym
@@ -32,6 +32,7 @@ module Keystone
32
32
  @name = name
33
33
  @value = value
34
34
  @hidden = hidden
35
+ @confirm = confirm
35
36
  end
36
37
 
37
38
  def classes
@@ -59,6 +60,7 @@ module Keystone
59
60
  options[:href] = @href
60
61
  end
61
62
  options[:data] = @data if @data
63
+ options[:data] = (@data || {}).merge(turbo_confirm: @confirm) if @confirm
62
64
  options[:hidden] = true if @hidden
63
65
  options
64
66
  end
@@ -68,11 +70,15 @@ module Keystone
68
70
  end
69
71
 
70
72
  def call
71
- return button_to(@href, method: @method, class: classes, form_class: form_classes) { @label } if request?
73
+ return button_to(@href, method: @method, class: classes, form_class: form_classes, form: form_options) { @label } if request?
72
74
 
73
75
  content_tag(tag_name, @label, **tag_options)
74
76
  end
75
77
 
78
+ def form_options
79
+ @confirm ? { data: { turbo_confirm: @confirm } } : {}
80
+ end
81
+
76
82
  def form_classes
77
83
  FULL_WIDTH_CLASSES if @full_width
78
84
  end
@@ -3,6 +3,7 @@
3
3
  <% content_for :mobile_action_url, action_url %>
4
4
  <% content_for :mobile_action_label, action_label %>
5
5
  <% content_for :mobile_action_method, action_method.to_s if action_method %>
6
+ <% content_for :mobile_action_confirm, action_confirm if action_confirm %>
6
7
  <% end %>
7
8
  <div class="<%= wrapper_classes %>">
8
9
  <div>
@@ -22,10 +23,17 @@
22
23
  </div>
23
24
  <% if actions? %>
24
25
  <div class="<%= actions_classes %>">
25
- <% if action? %>
26
- <%= @action_block.call %>
27
- <% elsif action_url %>
28
- <%= render Keystone::Ui::ButtonComponent.new(label: action_label, href: action_url, method: action_method || :get) %>
26
+ <% main_action = capture do %>
27
+ <% if action? %>
28
+ <%= @action_block.call %>
29
+ <% elsif action_url %>
30
+ <%= render Keystone::Ui::ButtonComponent.new(label: action_label, href: action_url, method: action_method || :get, confirm: action_confirm) %>
31
+ <% end %>
32
+ <% end %>
33
+ <% if main_action_classes %>
34
+ <div class="<%= main_action_classes %>"><%= main_action %></div>
35
+ <% else %>
36
+ <%= main_action %>
29
37
  <% end %>
30
38
  <% if menu? %>
31
39
  <%= render Keystone::Ui::ActionMenuComponent.new do %>
@@ -12,15 +12,18 @@ module Keystone
12
12
  PHONE_ACTIONS_CLASSES = "max-sm:flex max-sm:items-center max-sm:basis-full"
13
13
  ROW_CLASSES = "sm:flex sm:items-center flex-wrap max-w-full ks-grid-gap-sm"
14
14
  TITLE_ROW_CLASSES = "flex flex-wrap items-center ks-grid-gap-sm"
15
+ MAIN_ACTION_AT_BOTTOM_CLASSES = "contents max-sm:hidden"
15
16
 
16
- attr_reader :title, :action_url, :action_label, :action_method
17
+ attr_reader :title, :action_url, :action_label, :action_method, :action_confirm
17
18
 
18
- def initialize(title:, subtitle: nil, action_url: nil, action_label: "Add new", action_method: nil, badges: [], menu: [], class: nil)
19
+ def initialize(title:, subtitle: nil, action_url: nil, action_label: "Add new", action_method: nil, action_confirm: nil, action_at_bottom: false, badges: [], menu: [], class: nil)
19
20
  @title = title
20
21
  @subtitle = subtitle
21
22
  @action_url = action_url
22
23
  @action_label = action_label
23
24
  @action_method = action_method
25
+ @action_confirm = action_confirm
26
+ @action_at_bottom = action_at_bottom
24
27
  @badges = badges
25
28
  @menu = menu
26
29
  @action_block = nil
@@ -43,6 +46,10 @@ module Keystone
43
46
  !!@action_block
44
47
  end
45
48
 
49
+ def main_action_classes
50
+ MAIN_ACTION_AT_BOTTOM_CLASSES if @action_at_bottom
51
+ end
52
+
46
53
  def actions_classes
47
54
  [ ACTIONS_CLASSES, ROW_CLASSES, (PHONE_ACTIONS_CLASSES if phone?) ].compact.join(" ")
48
55
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeystoneUi
4
- VERSION = "0.80.0"
4
+ VERSION = "0.81.0"
5
5
  end
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: keystone_ui-develop
3
- description: Use PROACTIVELY for building or editing screens in a Rails app that has keystone_ui — page shells with Back links and breadcrumbs, forms and fields, a search field that picks a record or enters a new one, data tables with saved columns, desktop navigation, sidebars and bottom tabs, a page's main action as a phone top-bar icon or a button fixed to the bottom of the screen, action menus for a record's Edit and Delete, dashboards with stat cards, charts, funnels, goal buckets and timelines of timed steps with retries, queued time and failed tries, amounts shown in green or red as a gain or loss, status dots coloured by state, pictures held to half the screen, two versions of a text compared line by line side by side, info buttons and breakdowns, marketing sections, a light/dark theme switch — MUST BE USED instead of hand-writing ERB and Tailwind for UI.
3
+ description: Use PROACTIVELY for building or editing screens in a Rails app that has keystone_ui — page shells with Back links and breadcrumbs, forms and fields, a search field that picks a record or enters a new one, data tables with saved columns, desktop navigation, sidebars and bottom tabs, a page's main action as a phone top-bar icon or a button fixed to the bottom of the screen, a question asked before a button or main action goes ahead, action menus for a record's Edit and Delete, dashboards with stat cards, charts, funnels, goal buckets and timelines of timed steps with retries, queued time and failed tries, amounts shown in green or red as a gain or loss, status dots coloured by state, pictures held to half the screen, two versions of a text compared line by line side by side, info buttons and breakdowns, marketing sections, a light/dark theme switch — MUST BE USED instead of hand-writing ERB and Tailwind for UI.
4
4
  tools: Bash, Read, Write, Edit, Grep
5
5
  scope: UI — pages, forms, tables, navigation, dashboards
6
6
  ---
@@ -27,7 +27,7 @@ not available in the app yet, that is `keystone_ui-install`'s job.
27
27
  - `ui_page` — the outer wrapper of a screen, holding the page's content.
28
28
  - `ui_form_page` — marks a screen as a form screen and gives it its title, Back link and breadcrumbs.
29
29
  - `ui_show_page` — marks a screen as a detail screen and gives it its Back link and breadcrumbs.
30
- - `ui_page_header` — the title of a page, with optional badges beside it and a control and action menu on its right.
30
+ - `ui_page_header` — the title of a page, with optional badges beside it and a control and action menu on its right, and the screen's main action, which can ask a question first and can move to the bottom of a phone screen.
31
31
  - `ui_breadcrumbs` — a line of links to the pages above the current one, shown from `lg:` up.
32
32
  - `ui_section` — a titled block of content with an optional link and action menu in its header.
33
33
  - `ui_grid` — a responsive grid with a column count per breakpoint.
@@ -65,7 +65,7 @@ not available in the app yet, that is `keystone_ui-install`'s job.
65
65
  - `ui_data_table` — a table of records with links, row actions, sorting, hideable columns and the viewer's own row marked.
66
66
  - `Keystone::Ui::Column` — a table column with options a `{ key: "Label" }` hash cannot carry.
67
67
  - `ui_column_picker` — a Columns menu that saves which columns are hidden and their order to an app endpoint.
68
- - `ui_button` — a link or a button styled as a button, or a button sending a POST, PATCH or DELETE request.
68
+ - `ui_button` — a link or a button styled as a button, or a button sending a POST, PATCH or DELETE request, optionally asking a question before it goes ahead.
69
69
  - `ui_badge` — a coloured pill holding a short label.
70
70
  - `ui_status_dot` — a small dot coloured by state, with its label read only by screen readers.
71
71
  - `ui_figure` — one amount as inline text, coloured as a gain or a loss.
@@ -175,15 +175,28 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
175
175
  - Ask the developer whether a screen shows breadcrumbs and which parent
176
176
  screens its trail names. Ask which screens a navigation tab opens
177
177
  directly, since those pass `trail: []`.
178
- - `ui_page_header(title:, subtitle: nil, action_url: nil, action_label: "Add new", action_method: nil, badges: [], menu: [], class: nil)`
178
+ - `ui_page_header(title:, subtitle: nil, action_url: nil, action_label: "Add new", action_method: nil, action_confirm: nil, action_at_bottom: false, badges: [], menu: [], class: nil)`
179
179
  takes a block yielding the header. Call
180
180
  `header.action { ... }` in the block to place a control on its right.
181
181
  `action_url:` declares the screen's main action. It publishes that URL and
182
- label for `ui_mobile_action` to pick up, and with no `action` block the
183
- header draws them as a button. An `action` block is drawn alone when both
184
- are given. `action_method:`, such as `:post`, makes the main action send
185
- that method: the header's button and `ui_mobile_action`'s icon become
186
- forms posting to `action_url:`.
182
+ label for `ui_mobile_action` and `ui_bottom_action` to pick up, and with
183
+ no `action` block the header draws them as a button. An `action` block is
184
+ drawn alone when both are given. `action_method:`, such as `:post`, makes
185
+ the main action send that method: the header's button and
186
+ `ui_mobile_action`'s icon become forms posting to `action_url:`.
187
+ - `action_confirm:` is a question Turbo asks before the main action goes
188
+ ahead, on the header's button and on `ui_bottom_action`'s button, as
189
+ `ui_button`'s `confirm:` does. `ui_mobile_action`'s plus icon does not ask
190
+ it, so a screen whose action needs a question shows `ui_bottom_action` on
191
+ a phone, not the plus icon. It applies only with `action_url:` and is
192
+ ignored when an `action` block is given. Ask the developer whether a
193
+ screen's main action needs a question and how it is worded.
194
+ - `action_at_bottom: true` hides the header's main action, or its `action`
195
+ block, below `sm:`, for a screen whose phone layout draws the action with
196
+ `ui_bottom_action`. From `sm:` up the header shows it as before, and the
197
+ menu and badges are unaffected. With the default `false` the main action
198
+ shows at every width. Pass it on every screen whose layout calls
199
+ `ui_bottom_action`, so the phone does not show the action twice.
187
200
  - `badges:` is `[{ label:, variant: }, ...]`, each drawn as a `ui_badge`
188
201
  beside the title, with `variant:` taking the badge's values. `menu:` is
189
202
  `[{ label:, href:, method: }, ...]`, each hash taking the keywords of
@@ -193,8 +206,9 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
193
206
  action. A header given badges or a menu is shown there with its badges,
194
207
  actions and menu, and hides its title when the page holds
195
208
  `ui_mobile_header`'s title. Its action then shows below `sm:` beside
196
- `ui_mobile_action`'s plus icon, so ask the developer whether a screen
197
- with both keeps both. Below `sm:` the actions and menu sit side by side
209
+ `ui_mobile_action`'s plus icon or `ui_bottom_action`'s button unless
210
+ `action_at_bottom: true` hides it there, so ask the developer whether a
211
+ screen with both keeps both. Below `sm:` the actions and menu sit side by side
198
212
  on their own full-width row under the title and badges, and from `sm:` up
199
213
  they sit side by side on the right, with or without a menu. At every
200
214
  width buttons that do not fit wrap onto another line, so the header never
@@ -311,14 +325,17 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
311
325
  with its `action_label:`, and draws nothing when the screen gives none. It
312
326
  is hidden from `sm:` up, where the page header shows the action, and inside
313
327
  a Hotwire Native web view, so give native users another way to reach the
314
- action, such as `ui_mobile_actions`. The layout must also write the
315
- `hotwire-native` class on `<html>` and declare that Tailwind variant.
328
+ action, such as `ui_mobile_actions`. It never asks `action_confirm:`. The
329
+ layout must also write the `hotwire-native` class on `<html>` and declare
330
+ that Tailwind variant.
316
331
  - `ui_bottom_action` takes no keywords and no block. It draws the
317
332
  `action_url:` a screen gives `ui_page_header` as a full-width `ui_button`
318
333
  labelled with its `action_label:`, in a bar fixed to the bottom of the
319
334
  screen, above `ui_bottom_nav` on a page that has one. With
320
335
  `action_method:` the button sends that method as `ui_button` does, so it
321
- cannot sit inside a form. It draws nothing when the screen gives no
336
+ cannot sit inside a form. With `action_confirm:` the button asks that
337
+ question before it goes ahead. Give those screens' page headers
338
+ `action_at_bottom: true`. It draws nothing when the screen gives no
322
339
  `action_url:`, so call it after `ui_page_header` has run, such as in the
323
340
  layout after `yield`. It is hidden from `sm:` up, inside a Hotwire Native
324
341
  web view and in print, like `ui_mobile_action`. It also draws an invisible
@@ -578,7 +595,7 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
578
595
 
579
596
  9. Show content and status.
580
597
 
581
- - `ui_button(label:, href: nil, method: :get, variant: :primary, size: :md, full_width: false, type: :submit, data: nil, name: nil, value: nil, hidden: false)`
598
+ - `ui_button(label:, href: nil, method: :get, variant: :primary, size: :md, full_width: false, type: :submit, data: nil, name: nil, value: nil, hidden: false, confirm: nil)`
582
599
  renders a link with `href:` and a button without it. `variant:` is
583
600
  `:primary` `:secondary` `:danger` `:text`, `size:` is `:sm` `:md` `:lg`,
584
601
  and `type:`, `name:` and `value:` apply only to the button.
@@ -596,6 +613,12 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
596
613
  - `full_width: true` makes the button as wide as its row at every screen
597
614
  width, and for a button sending a request its form too. Use it for large
598
615
  choices stacked down a phone screen.
616
+ - `confirm:` is a question Turbo asks before the button goes ahead. On a
617
+ button sending a request it is set on the button's own form, and on a
618
+ link or a plain button it is set on the element, merged into `data:`.
619
+ With no `confirm:` nothing is asked, whatever the method, unlike
620
+ `ui_action_menu_item`. Ask the developer which buttons need a question
621
+ and how it is worded.
599
622
  - `ui_picture(src:, alt:, full_size_url: nil)` shows an image centred and
600
623
  never taller than half the visible screen, so what follows it stays in
601
624
  view. `alt:` is the text read out in place of the image. With
@@ -102,14 +102,21 @@ One decision: is keystone_ui set up in the app?
102
102
  submits an empty id and shows the new record's fields the page placed inside
103
103
  the dropdown, and picking a match afterwards hides them again. The arrow keys
104
104
  move through the list, Enter picks, and Escape closes it.
105
+ - **Confirmation.** A button can carry a confirmation question, and the
106
+ browser asks it before following the link or sending the form. Nothing is
107
+ sent when the user cancels.
105
108
  - **A page's main action on a phone.** A page header can name one main action:
106
- a label, an address and an optional HTTP method. The header shows it as a
107
- button and also publishes it to the layout. Below `sm:` the layout can show it
108
- again as a plus icon in the top bar, or as a full-width button in a bar fixed
109
- to the bottom of the screen, above the bottom tab bar when the page has one. The page leaves room under
110
- its last content so the bar does not cover it. Both forms show nothing when
111
- the header names no action, and both are hidden inside a Hotwire Native web
112
- view and in print.
109
+ a label, an address, an optional HTTP method and an optional confirmation
110
+ question. The header shows it as a button and also publishes it to the
111
+ layout. Below `sm:` the layout can show it again as a plus icon in the top
112
+ bar, or as a full-width button in a bar fixed to the bottom of the screen,
113
+ above the bottom tab bar when the page has one. The bottom bar asks the
114
+ header's confirmation question before sending, and the plus icon does not.
115
+ The page leaves room under its last content so the bar does not cover it.
116
+ A header can mark its action as belonging at the bottom, and then hides its
117
+ own button below `sm:` so the action is not shown twice. Both forms show
118
+ nothing when the header names no action, and both are hidden inside a
119
+ Hotwire Native web view and in print.
113
120
  - **Action menus.** Each action is a label, an address and an HTTP method. A
114
121
  plain visit is a link, and any other method is a button, so an action that
115
122
  changes data is never a bare link. A delete asks for confirmation by default.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keystone_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.80.0
4
+ version: 0.81.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider