keystone_ui 0.25.0 → 0.27.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: 0dcc3e30b080deb833cdbbcde6a946930de28fa541cb24fbdef2d83087eaca7c
4
- data.tar.gz: 4c623f38dcbdd1f43371359af97b06fafd59796570267479db951236ac7daf85
3
+ metadata.gz: 77e17961bce047a1477df49c98660ff0484456e5add3eb78a59b67735296d91b
4
+ data.tar.gz: 75fa1c3b979a7490ea47423b5b5d64a075648ff203a9554af170a1dfc06865bf
5
5
  SHA512:
6
- metadata.gz: 38add779239157bad1402f79b96cd66bf3219c39dac2e90b8d3b27eda892137bc1b2b5a0f5ed5df175e92b3963a90996fbc861d81b82667f87f711b37e08b7af
7
- data.tar.gz: 4838c78a35737a4bdea3d3d847ddc7b60276a9630543990510a9ef0c2e67a52a34d0b87cf939b4d14a8213a9a0d52fe528615e232626c8b84de24d89158bc79f
6
+ metadata.gz: 5f1b133c91668e4fa4886c15f664588d2cec6fd972dae84caf199fd891aeb22c6e48c534f8274b5a64669fb02cd6adb09890f51b75656bd9943a2252b30555a8
7
+ data.tar.gz: 34db47e65e4304e7224557d1b29dc1abe8803edd920e851f4a9f8f456d680b71b78710fe2966fd2253b756373a6ca01197dbd1881ec53fa28b730c72e7516a0b
@@ -5,16 +5,23 @@ module Keystone
5
5
  class ActionMenuItemComponent < ViewComponent::Base
6
6
  ITEM_CLASSES = "ks-menu-option block w-full text-left"
7
7
 
8
- def initialize(label:, href:, method: :get)
8
+ def initialize(label:, href:, method: :get, confirm: nil)
9
9
  @label = label
10
10
  @href = href
11
11
  @method = method.to_sym
12
+ @confirm = confirm
13
+ end
14
+
15
+ def question
16
+ return @confirm if @confirm
17
+
18
+ "#{@label} this? This cannot be undone." if @method == :delete
12
19
  end
13
20
 
14
21
  def call
15
22
  return link_to(@label, @href, class: ITEM_CLASSES) if @method == :get
16
23
 
17
- button_to(@href, method: @method, class: ITEM_CLASSES) { @label }
24
+ button_to(@href, method: @method, class: ITEM_CLASSES, form: { data: { turbo_confirm: question }.compact }) { @label }
18
25
  end
19
26
  end
20
27
  end
@@ -1,7 +1,13 @@
1
- <label class="<%= row_classes %>">
1
+ <label class="<%= row_classes %> relative"<%= ' data-controller="stat-card-info"'.html_safe if info? %>>
2
2
  <input type="checkbox" name="<%= name %>" value="<%= value %>" <%= "checked" if checked? %> class="<%= input_classes %>">
3
3
  <span>
4
- <span class="<%= label_classes %>"><%= label %></span>
4
+ <span class="<%= header_classes %>">
5
+ <span class="<%= label_classes %>"><%= label %></span>
6
+ <% if info? %>
7
+ <button type="button" class="<%= info_button_classes %>" data-action="click->stat-card-info#toggle" aria-label="About <%= label %>"><%= info_icon.html_safe %></button>
8
+ <span class="<%= disclosure_classes %>" data-stat-card-info-target="panel"><%= info %></span>
9
+ <% end %>
10
+ </span>
5
11
  <% if hint? %>
6
12
  <span class="<%= hint_classes %>"><%= hint %></span>
7
13
  <% end %>
@@ -7,14 +7,18 @@ module Keystone
7
7
  INPUT_CLASSES = "ks-checkbox-row-input size-4 shrink-0"
8
8
  LABEL_CLASSES = "ks-checkbox-row-label block text-sm"
9
9
  HINT_CLASSES = "ks-checkbox-row-hint block text-sm"
10
+ HEADER_CLASSES = RadioCardComponent::HEADER_CLASSES
11
+ INFO_BUTTON_CLASSES = RadioCardComponent::INFO_BUTTON_CLASSES
12
+ DISCLOSURE_CLASSES = RadioCardComponent::DISCLOSURE_CLASSES
10
13
 
11
- attr_reader :name, :value, :label, :hint
14
+ attr_reader :name, :value, :label, :hint, :info
12
15
 
13
- def initialize(name:, value:, label:, hint: nil, checked: false)
16
+ def initialize(name:, value:, label:, hint: nil, info: nil, checked: false)
14
17
  @name = name
15
18
  @value = value
16
19
  @label = label
17
20
  @hint = hint
21
+ @info = info
18
22
  @checked = checked
19
23
  end
20
24
 
@@ -26,6 +30,22 @@ module Keystone
26
30
  !@hint.nil?
27
31
  end
28
32
 
33
+ def info?
34
+ !@info.nil?
35
+ end
36
+
37
+ def info_button_classes
38
+ INFO_BUTTON_CLASSES
39
+ end
40
+
41
+ def disclosure_classes
42
+ DISCLOSURE_CLASSES
43
+ end
44
+
45
+ def info_icon
46
+ RadioCardComponent::INFO_ICON
47
+ end
48
+
29
49
  def row_classes
30
50
  ROW_CLASSES
31
51
  end
@@ -34,6 +54,10 @@ module Keystone
34
54
  INPUT_CLASSES
35
55
  end
36
56
 
57
+ def header_classes
58
+ HEADER_CLASSES
59
+ end
60
+
37
61
  def label_classes
38
62
  LABEL_CLASSES
39
63
  end
@@ -1,7 +1,13 @@
1
- <label class="inline-block cursor-pointer">
1
+ <label class="inline-block cursor-pointer relative"<%= ' data-controller="stat-card-info"'.html_safe if info? %>>
2
2
  <input type="radio" name="<%= name %>" value="<%= value %>" <%= "checked" if checked? %> class="peer sr-only">
3
3
  <span class="<%= classes %>">
4
- <span class="<%= label_classes %>"><%= label %></span>
4
+ <span class="<%= header_classes %>">
5
+ <span class="<%= label_classes %>"><%= label %></span>
6
+ <% if info? %>
7
+ <button type="button" class="<%= info_button_classes %>" data-action="click->stat-card-info#toggle" aria-label="About <%= label %>"><%= info_icon.html_safe %></button>
8
+ <span class="<%= disclosure_classes %>" data-stat-card-info-target="panel"><%= info %></span>
9
+ <% end %>
10
+ </span>
5
11
  <% if hint? %>
6
12
  <span class="<%= hint_classes %>"><%= hint %></span>
7
13
  <% end %>
@@ -5,16 +5,21 @@ module Keystone
5
5
  class RadioCardComponent < ViewComponent::Base
6
6
  BASE_CLASSES = "ks-radio-card inline-flex flex-col cursor-pointer transition"
7
7
  HIGHLIGHT_CLASSES = "ks-radio-card-highlight"
8
+ HEADER_CLASSES = "ks-radio-card-header flex items-start justify-between"
8
9
  LABEL_CLASSES = "ks-radio-card-label block"
9
10
  HINT_CLASSES = "ks-radio-card-hint block text-sm"
11
+ INFO_BUTTON_CLASSES = "ks-radio-card-info peer shrink-0"
12
+ DISCLOSURE_CLASSES = "ks-radio-card-disclosure hidden peer-hover:block absolute inset-x-0 top-full z-10 text-sm"
13
+ INFO_ICON = Keystone::Ui::StatCardComponent::INFO_ICON
10
14
 
11
- attr_reader :name, :value, :label, :hint
15
+ attr_reader :name, :value, :label, :hint, :info
12
16
 
13
- def initialize(name:, value:, label:, hint: nil, checked: false)
17
+ def initialize(name:, value:, label:, hint: nil, info: nil, checked: false)
14
18
  @name = name
15
19
  @value = value
16
20
  @label = label
17
21
  @hint = hint
22
+ @info = info
18
23
  @checked = checked
19
24
  end
20
25
 
@@ -26,10 +31,30 @@ module Keystone
26
31
  !@hint.nil?
27
32
  end
28
33
 
34
+ def info?
35
+ !@info.nil?
36
+ end
37
+
38
+ def info_button_classes
39
+ INFO_BUTTON_CLASSES
40
+ end
41
+
42
+ def info_icon
43
+ INFO_ICON
44
+ end
45
+
46
+ def disclosure_classes
47
+ DISCLOSURE_CLASSES
48
+ end
49
+
29
50
  def classes
30
51
  "#{BASE_CLASSES} #{HIGHLIGHT_CLASSES}"
31
52
  end
32
53
 
54
+ def header_classes
55
+ HEADER_CLASSES
56
+ end
57
+
33
58
  def label_classes
34
59
  LABEL_CLASSES
35
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeystoneUi
4
- VERSION = "0.25.0"
4
+ VERSION = "0.27.0"
5
5
  end
@@ -120,12 +120,15 @@ outer element. See Conventions before using it.
120
120
  - `ui_action_menu` — no keywords, takes a block. An ellipsis (⋯) button that
121
121
  opens a dropdown of actions, shown at every screen size. Fill the block with
122
122
  `ui_action_menu_item` calls.
123
- - `ui_action_menu_item(label:, href:, method: :get)` — one entry in an action
124
- menu. With `method: :get` it is a link to `href`. Any other method, such as
125
- `:post`, `:patch` or `:delete`, renders a button inside its own small form
126
- that sends that method to `href`, so never place such an item inside a
127
- `ui_form` block, since a form cannot contain another form. It asks for no
128
- confirmation before sending.
123
+ - `ui_action_menu_item(label:, href:, method: :get, confirm: nil)` — one entry
124
+ in an action menu. With `method: :get` it is a link to `href`. Any other
125
+ method, such as `:post`, `:patch` or `:delete`, renders a button inside its
126
+ own small form that sends that method to `href`, so never place such an item
127
+ inside a `ui_form` block, since a form cannot contain another form.
128
+ `confirm:` is a question Turbo asks before the item sends. With no
129
+ `confirm:`, a `method: :delete` item asks "<label> this? This cannot be
130
+ undone.", and every other method sends without asking. A `method: :get` item
131
+ is a plain link and ignores `confirm:`.
129
132
  - `ui_mobile_actions` — no keywords, takes a block. An ellipsis dropdown for
130
133
  mobile actions; the block holds the menu items. Hidden above `lg:`. Use
131
134
  `ui_action_menu` instead when the actions must also be reachable on desktop.
@@ -175,13 +178,24 @@ outer element. See Conventions before using it.
175
178
  enclosing form to be multipart.
176
179
  - `ui_color_picker(name:, value: "#000000", label: nil)` — a swatch that opens a
177
180
  hue/saturation panel and writes the hex into a hidden input named `name`.
178
- - `ui_radio_card(name:, value:, label:, hint: nil, checked: false)` — a
179
- selectable card backed by a real radio input; selection styling is pure CSS.
180
- - `ui_checkbox_row(name:, value:, label:, hint: nil, checked: false)` — a real
181
- checkbox with its label and optional hint inside one `<label>`, so a tap
182
- anywhere on the row toggles the box. A checked row submits `value` under
181
+ - `ui_radio_card(name:, value:, label:, hint: nil, info: nil, checked: false)`
182
+ — a selectable card backed by a real radio input; selection styling is pure
183
+ CSS. `hint:` is a line of text always shown under the label. `info:` is
184
+ longer text about the option, kept hidden: passing it adds an info button on
185
+ the row with the label, named "About <label>" for screen readers, and the
186
+ text shows in a panel floating below the card while the button is hovered,
187
+ and tapping the button toggles it. With no `info:` the card has no info
188
+ button.
189
+ - `ui_checkbox_row(name:, value:, label:, hint: nil, info: nil, checked: false)`
190
+ — a real checkbox with its label and optional hint inside one `<label>`, so a
191
+ tap anywhere on the row toggles the box. A checked row submits `value` under
183
192
  `name`; give several rows the same array name, e.g. `"shown[]"`, to submit the
184
- checked values as a list. An unchecked row submits nothing. No JavaScript.
193
+ checked values as a list. An unchecked row submits nothing. `hint:` is a line
194
+ of text always shown under the label. `info:` works as it does on
195
+ `ui_radio_card`: passing it adds an info button on the line with the label,
196
+ named "About <label>" for screen readers, and the text shows in a panel
197
+ floating below the row while the button is hovered, and tapping the button
198
+ toggles it. With no `info:` the row has no info button.
185
199
  - `ui_option_card(name:, value:, selected: false, input_data: {}, label_data: {})`
186
200
  — takes a block. A radio whose visible body is whatever the block renders.
187
201
  `input_data:`/`label_data:` become `data-*` attributes on the input and label.
@@ -378,12 +392,16 @@ outer element. See Conventions before using it.
378
392
  To show the arithmetic behind a figure, put `ui_calculation` under it rather
379
393
  than a hand-built list. Which lines and groups to show is the app's own
380
394
  calculation, so ask the developer which steps a reader needs to see.
395
+ On a radio card or a checkbox row, text every reader needs to choose goes in `hint:` and
396
+ text only some will want goes in `info:`. Which is which is the app's own
397
+ wording, so ask the developer rather than pick.
381
398
  Put the actions on a record, such as Edit and Delete, in an action menu
382
399
  rather than in a row of buttons: `menu:` on the `ui_section` that shows the
383
400
  record, `table.actions` for a table row, or `ui_action_menu` anywhere else.
384
- An item that deletes or changes data sends without asking for
385
- confirmation, so ask the developer whether that action needs a confirmation
386
- step before adding it.
401
+ A delete item asks for confirmation on its own, so pass `confirm:` only
402
+ when the developer wants different wording. A `:post`, `:patch` or `:put`
403
+ item sends without asking, so ask the developer whether that action needs
404
+ a question, and pass it as `confirm:` if it does.
387
405
 
388
406
  6. For a table, decide how columns are declared. Use `{ key: "Label" }` hashes
389
407
  when every column is plain. Switch the whole set to `Keystone::Ui::Column`
@@ -78,9 +78,16 @@ holds the catalog.
78
78
  of actions, shown at every screen size. The mobile action menu is the one
79
79
  hidden from `lg:` up. Each action is a label, an address and an HTTP method.
80
80
  A plain visit renders as a link. Any other method renders as a button that
81
- sends that method, so an action that changes data is never a bare link. A
82
- data table puts each row's actions in one action menu at the end of the row,
81
+ sends that method, so an action that changes data is never a bare link. An
82
+ action can carry a question that Turbo asks before it runs. A delete asks
83
+ "<label> this? This cannot be undone." unless it is given its own question,
84
+ and other actions ask nothing unless given one. A data table puts each row's actions in one action menu at the end of the row,
83
85
  and a section can carry an action menu in its header beside its title.
86
+ - **Info panels.** A stat card, a radio card and a checkbox row can each carry
87
+ extra text about themselves. Given that text, the piece shows an info button,
88
+ and the text stays hidden until the button is hovered or tapped. A piece
89
+ given no such text shows no button. On a radio card and a checkbox row the
90
+ button sits on the line with the label.
84
91
  - **Options are symbols, and each component accepts its own set.** Appearance is
85
92
  chosen by name — `variant:`, `size:`, `type:`, `padding:`, `spacing:`,
86
93
  `max_width:`, `radius:` — on a size scale (`:sm` … `:xl`) or a short list of
@@ -139,7 +146,7 @@ holds the catalog.
139
146
  at boot where the component files are.
140
147
  - **Interactivity is Stimulus.** Dropdowns, modals, dismissible alerts, file
141
148
  uploads, the color picker, the multi-select, tab switchers, accordions, the
142
- swipe deck, column pickers, stat card info panels, line charts, clipboard copy
149
+ swipe deck, column pickers, info panels, line charts, clipboard copy
143
150
  and the theme toggle ship with the gem as Stimulus controllers registered once
144
151
  at install. A host writes no JavaScript to use them. Components that post
145
152
  somewhere, such as the column picker and the pipeline, post to endpoints the
@@ -215,8 +215,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
215
215
  A page that passes no Back link goes back to the trail's last link. A page
216
216
  that passes its own trail or Back link keeps it. With no supplier, or a
217
217
  `nil` return, the page shows its Back link. Return `nil`, not an empty
218
- array, for a page with no trail, because an empty array replaces the
219
- page's Back link with breadcrumbs holding only its title.
218
+ array, for a page with no trail. An empty array replaces the page's Back
219
+ link with breadcrumbs holding only its title, and on a page that passes
220
+ no Back link it raises `KeystoneUi::MissingBackLink`.
220
221
  - Every form and show page must end up with a Back link. A page that passes
221
222
  no Back link and no trail, and gets no trail from the supplier, raises
222
223
  `KeystoneUi::MissingBackLink` naming the page's title when it renders.
@@ -128,6 +128,7 @@ sources:
128
128
  - app/components/keystone/ui/theme_toggle_component.rb
129
129
  - app/components/keystone/ui/progress_component.rb
130
130
  - app/components/keystone/ui/radio_card_component.rb
131
+ - app/components/keystone/ui/radio_card_component.html.erb
131
132
  - app/components/keystone/ui/section_component.rb
132
133
  - app/components/keystone/ui/section_component.html.erb
133
134
  - app/components/keystone/ui/select_component.rb
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.25.0
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.6.0
18
+ version: 0.8.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.6.0
25
+ version: 0.8.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: view_component
28
28
  requirement: !ruby/object:Gem::Requirement