keystone_ui 0.27.0 → 0.29.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: 77e17961bce047a1477df49c98660ff0484456e5add3eb78a59b67735296d91b
4
- data.tar.gz: 75fa1c3b979a7490ea47423b5b5d64a075648ff203a9554af170a1dfc06865bf
3
+ metadata.gz: e0ee212f207d337a71bacb75feeb208b87dce778bae0d0f1f3eae6c579edffc7
4
+ data.tar.gz: feddfc10e16f72a89a15b0c5c9b35caf22e5c579205d954d483e21093fb3ab59
5
5
  SHA512:
6
- metadata.gz: 5f1b133c91668e4fa4886c15f664588d2cec6fd972dae84caf199fd891aeb22c6e48c534f8274b5a64669fb02cd6adb09890f51b75656bd9943a2252b30555a8
7
- data.tar.gz: 34db47e65e4304e7224557d1b29dc1abe8803edd920e851f4a9f8f456d680b71b78710fe2966fd2253b756373a6ca01197dbd1881ec53fa28b730c72e7516a0b
6
+ metadata.gz: 8444ed3845fb6be74e214ffad7c57f19221a48b7ccf023bd252506eafbc53588bbd76378a2cb19174ea293c3304ae66edc90509b9209ba9ba9d8565282e021fa
7
+ data.tar.gz: ebef332e0bdda943a4a95bb72e7f6b0e2b5d2f0de6bca13b623bc3d8fe7432d9d605b58810a208fa73eea0523a3b795f4a2ba657df036b790fd0a9837d0314a9
@@ -0,0 +1,10 @@
1
+ <span class="block">
2
+ <% lines.each do |line| %>
3
+ <span class="<%= Keystone::Ui::BreakdownComponent::LINE_CLASSES %>">
4
+ <span class="<%= Keystone::Ui::BreakdownComponent::AMOUNT_CLASSES %>"><%= line[:amount] %></span>&nbsp;<span class="<%= Keystone::Ui::BreakdownComponent::LABEL_CLASSES %>"><%= line[:label] %></span>
5
+ </span>
6
+ <% end %>
7
+ <span class="<%= Keystone::Ui::BreakdownComponent::TOTAL_CLASSES %>">
8
+ <span class="<%= Keystone::Ui::BreakdownComponent::AMOUNT_CLASSES %>"><%= total[:amount] %></span>&nbsp;<span class="<%= Keystone::Ui::BreakdownComponent::LABEL_CLASSES %>"><%= total[:label] %></span>
9
+ </span>
10
+ </span>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class BreakdownComponent < ViewComponent::Base
6
+ LINE_CLASSES = "ks-breakdown-line block"
7
+ AMOUNT_CLASSES = "ks-breakdown-amount inline-block w-24 text-right tabular-nums"
8
+ LABEL_CLASSES = "ks-breakdown-label"
9
+ TOTAL_CLASSES = "ks-breakdown-total ks-stat-card-emphasis block"
10
+
11
+ attr_reader :lines, :total
12
+
13
+ def initialize(lines:, total:)
14
+ @lines = lines
15
+ @total = total
16
+ end
17
+ end
18
+ end
19
+ end
@@ -22,6 +22,13 @@
22
22
  </select>
23
23
  <% else %>
24
24
  <%= tag.input(**input_options) %>
25
+ <% if suggestions? %>
26
+ <datalist id="<%= suggestions_id %>">
27
+ <% suggestions.each do |suggestion| %>
28
+ <option value="<%= suggestion %>"></option>
29
+ <% end %>
30
+ </datalist>
31
+ <% end %>
25
32
  <% end %>
26
33
 
27
34
  <% if hint? %>
@@ -11,7 +11,7 @@ module Keystone
11
11
  CHECKBOX_CLASSES = "ks-checkbox"
12
12
  CHECKBOX_WRAPPER_CLASSES = "ks-form-field-checkbox flex items-center"
13
13
 
14
- def initialize(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [], include_blank: nil, disabled: false)
14
+ def initialize(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [], include_blank: nil, disabled: false, suggestions: [])
15
15
  @attribute = attribute
16
16
  @label = label
17
17
  @type = type
@@ -26,6 +26,7 @@ module Keystone
26
26
  @errors = Array(errors)
27
27
  @include_blank = include_blank.to_s
28
28
  @disabled = disabled
29
+ @suggestions = Array(suggestions)
29
30
  end
30
31
 
31
32
  def label_text
@@ -83,8 +84,21 @@ module Keystone
83
84
  options[:max] = @max unless @max.nil?
84
85
  options[:step] = @step unless @step.nil?
85
86
  options[:disabled] = true if @disabled
87
+ options[:list] = suggestions_id if suggestions?
86
88
  options
87
89
  end
90
+
91
+ def suggestions?
92
+ @suggestions.any?
93
+ end
94
+
95
+ def suggestions
96
+ @suggestions
97
+ end
98
+
99
+ def suggestions_id
100
+ "#{@attribute.to_s.parameterize}-suggestions"
101
+ end
88
102
  end
89
103
  end
90
104
  end
@@ -0,0 +1,7 @@
1
+ <span class="relative inline-flex" data-controller="stat-card-info">
2
+ <button type="button" class="<%= button_classes %>" data-action="click->stat-card-info#toggle" aria-label="More about this"><%= icon.html_safe %></button>
3
+ <span class="<%= summary_classes %>"<%= ' data-stat-card-info-target="panel"'.html_safe unless content? %>><%= summary %></span>
4
+ <% if content? %>
5
+ <span class="<%= detail_classes %>" data-stat-card-info-target="panel"><%= content %></span>
6
+ <% end %>
7
+ </span>
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class InfoComponent < ViewComponent::Base
6
+ BUTTON_CLASSES = RadioCardComponent::INFO_BUTTON_CLASSES
7
+ ICON = RadioCardComponent::INFO_ICON
8
+ SUMMARY_CLASSES = "ks-info-summary #{RadioCardComponent::DISCLOSURE_CLASSES} w-64"
9
+ DETAIL_CLASSES = "ks-info-detail ks-radio-card-disclosure hidden absolute top-full right-0 z-20 w-72 text-sm"
10
+
11
+ def initialize(summary:)
12
+ @summary = summary
13
+ end
14
+
15
+ attr_reader :summary
16
+
17
+ def summary_classes
18
+ SUMMARY_CLASSES
19
+ end
20
+
21
+ def detail_classes
22
+ DETAIL_CLASSES
23
+ end
24
+
25
+ def button_classes
26
+ BUTTON_CLASSES
27
+ end
28
+
29
+ def icon
30
+ ICON
31
+ end
32
+ end
33
+ end
34
+ end
@@ -229,6 +229,14 @@ module KeystoneUiHelper
229
229
  render Keystone::Ui::CalculationComponent.new(**args)
230
230
  end
231
231
 
232
+ def ui_info(**args, &block)
233
+ render(Keystone::Ui::InfoComponent.new(**args), &block)
234
+ end
235
+
236
+ def ui_breakdown(**args)
237
+ render Keystone::Ui::BreakdownComponent.new(**args)
238
+ end
239
+
232
240
  private
233
241
 
234
242
  def keystone_theme_choice
@@ -61,7 +61,9 @@ module Keystone
61
61
  Keystone::Ui::ThemeToggleComponent,
62
62
  Keystone::Ui::CalculationComponent,
63
63
  Keystone::Ui::DesktopBackLinkComponent,
64
- Keystone::Ui::BreadcrumbsComponent
64
+ Keystone::Ui::BreadcrumbsComponent,
65
+ Keystone::Ui::InfoComponent,
66
+ Keystone::Ui::BreakdownComponent
65
67
  ].freeze
66
68
 
67
69
  # Constants that hold non-CSS values (e.g. HTML input type maps)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeystoneUi
4
- VERSION = "0.27.0"
4
+ VERSION = "0.29.0"
5
5
  end
@@ -146,7 +146,7 @@ outer element. See Conventions before using it.
146
146
  The `<form>` wrapper. `method:` may be `:patch`/`:put`/`:delete` and is
147
147
  translated for Rails. Set `multipart: true` when the form contains a file
148
148
  upload.
149
- - `ui_form_field(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [], include_blank: nil, disabled: false)`
149
+ - `ui_form_field(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [], include_blank: nil, disabled: false, suggestions: [])`
150
150
  — a labeled field with hint and error text. This is the default way to render
151
151
  an input. `type:` `:text` `:number` `:email` `:password` `:date` `:textarea`
152
152
  `:checkbox` `:select`. `attribute:` is used verbatim as the input's `name`, so
@@ -159,7 +159,12 @@ outer element. See Conventions before using it.
159
159
  it. Never add your own empty choice to `options:` as well. A
160
160
  `:checkbox` renders its label beside the box, submits `"0"` when unchecked and
161
161
  `"1"` when checked, and pre-checks when `value:` is `"1"`. `errors:` is an
162
- array of message strings.
162
+ array of message strings. `suggestions:` is an array of strings the browser
163
+ offers while the field's text is typed, and the user can still type a value
164
+ that is not in it. It applies to `:text`, `:number`, `:email`, `:password`
165
+ and `:date` fields, and a `:textarea`, `:checkbox` or `:select` ignores it.
166
+ The suggestion list's `id` is built from `attribute:`, so two fields with
167
+ suggestions on one screen need different `attribute:` values.
163
168
  - `ui_input(name:, type: :text, value: nil, placeholder: nil, disabled: false, min: nil, max: nil, step: nil)`
164
169
  — a bare styled input with no label. `type:` `:text` `:number` `:email`
165
170
  `:password` `:date`.
@@ -264,6 +269,23 @@ outer element. See Conventions before using it.
264
269
  — with the result right-aligned. Every value is printed as given, so format
265
270
  numbers and currency before passing them. Place it directly under the figure
266
271
  it explains, such as a `ui_stat_card`.
272
+ - `ui_info(summary:)` — an info button, named "More about this" for screen
273
+ readers, placed inline beside the thing it explains. `summary:` is a short
274
+ line of text shown in a floating panel while the button is hovered. The block
275
+ is optional and holds the full detail. With a block, tapping or clicking the
276
+ button toggles a second floating panel holding the block's content, and the
277
+ summary stays hover-only. With no block, tapping the button toggles the
278
+ summary itself. Both panels render inside a `<span>`, so the block may hold
279
+ text and inline elements only, such as a `ui_breakdown`, and never a `<div>`,
280
+ list or table. Toggling needs the gem's Stimulus controllers registered.
281
+ - `ui_breakdown(lines:, total:)` — no block. A list of amounts ending in their
282
+ total. `lines:` is `[{ amount:, label: }, ...]` and `total:` is one
283
+ `{ amount:, label: }`, both required. Each line renders its amount
284
+ right-aligned in a fixed-width column with its label beside it, and the total
285
+ renders last, emphasized. Every value is printed as given and nothing is
286
+ added up, so compute the total and format numbers and currency before
287
+ passing them. It renders inline elements only, so it can sit inside a
288
+ `ui_info` block.
267
289
  - `ui_accordion(items: [])` — a stack of independently expandable rows. `items:`
268
290
  is `[{ question:, answer: }, ...]`.
269
291
  - `ui_tab_switcher(tabs:)` — takes a block. `tabs:` is an array of label strings;
@@ -392,6 +414,15 @@ outer element. See Conventions before using it.
392
414
  To show the arithmetic behind a figure, put `ui_calculation` under it rather
393
415
  than a hand-built list. Which lines and groups to show is the app's own
394
416
  calculation, so ask the developer which steps a reader needs to see.
417
+ To explain a figure or label that is not a stat card, put `ui_info` beside
418
+ it, with the one-line explanation as `summary:` and, when the figure is a
419
+ sum of parts, a `ui_breakdown` in its block. Which amounts the breakdown
420
+ lists, and the wording of the summary, are the app's own, so ask the
421
+ developer rather than pick.
422
+ A field whose value must be one of a fixed list is a `:select` with
423
+ `options:`, and a field that accepts any text and offers common values is a
424
+ text field with `suggestions:`. Which one a field is, and which values it
425
+ suggests, is the app's own rule, so ask the developer rather than pick.
395
426
  On a radio card or a checkbox row, text every reader needs to choose goes in `hint:` and
396
427
  text only some will want goes in `info:`. Which is which is the app's own
397
428
  wording, so ask the developer rather than pick.
@@ -14,7 +14,8 @@ Keystone UI is a Rails engine gem that supplies a host app's visual layer as a
14
14
  library of view helpers built on ViewComponent. Screens are built from named
15
15
  pieces — page shells, sections, panels, grids, form fields, data tables,
16
16
  navigation bars, breadcrumbs, cards, stat tiles, charts, funnels, goal buckets, pipelines,
17
- banners, the calculation behind a figure — instead of hand-written ERB and
17
+ banners, the calculation behind a figure, a breakdown of amounts and their
18
+ total, an info button — instead of hand-written ERB and
18
19
  Tailwind. Every class the UI renders lives inside the gem, in frozen constants,
19
20
  so the look is defined in one place.
20
21
 
@@ -88,6 +89,23 @@ holds the catalog.
88
89
  and the text stays hidden until the button is hovered or tapped. A piece
89
90
  given no such text shows no button. On a radio card and a checkbox row the
90
91
  button sits on the line with the label.
92
+ - **Standalone info button.** The same info button can be placed on its own,
93
+ beside anything. It takes a short summary, which is required, and shows it
94
+ when the button is hovered. Given nothing more, a tap shows the summary too.
95
+ Given a block of further detail, a tap opens that detail instead, in a panel
96
+ under the button, and the summary stays the hover text.
97
+ - **A breakdown is text.** A breakdown lists amounts, each beside a label
98
+ saying what it is, and ends with a total set apart from the lines above it.
99
+ Every line and the total are an amount and a label, passed as
100
+ already-formatted text and shown as written. The component adds nothing up,
101
+ so the total is whatever it is given. It has no button and no hidden state of
102
+ its own, and it can be placed inside an info button's detail.
103
+ - **Suggestions.** A form field can carry a list of suggested values. The
104
+ browser offers them while the field's text is typed, and the user can still
105
+ enter a value that is not on the list. This applies to a field with a typed
106
+ input, such as text, number, email, password or date. A textarea, select or
107
+ checkbox field ignores the list, and a field given no list offers nothing.
108
+ It needs no JavaScript.
91
109
  - **Options are symbols, and each component accepts its own set.** Appearance is
92
110
  chosen by name — `variant:`, `size:`, `type:`, `padding:`, `spacing:`,
93
111
  `max_width:`, `radius:` — on a size scale (`:sm` … `:xl`) or a short list of
@@ -40,7 +40,7 @@ built on ViewComponent; hook it in before building any screen with those helpers
40
40
  ## How to use it
41
41
 
42
42
  1. Confirm the prerequisites: Ruby >= 3.2 and **tailwindcss-rails v4+** in the
43
- host app. The gem brings ViewComponent and keystone_ui-styles 0.6.0 or later with it.
43
+ host app. The gem brings ViewComponent and keystone_ui-styles 0.8.0 or later with it.
44
44
  Tailwind does not have to be initialized first, because the generator creates
45
45
  the stylesheet if it is missing.
46
46
 
@@ -61,6 +61,8 @@ develop:
61
61
  - ui_code
62
62
  - ui_disclosure
63
63
  - ui_calculation
64
+ - ui_info
65
+ - ui_breakdown
64
66
  - ui_theme_toggle
65
67
  - Keystone::Ui::Column
66
68
 
@@ -93,6 +95,10 @@ sources:
93
95
  - app/components/keystone/ui/data_table_component.html.erb
94
96
  - app/components/keystone/ui/disclosure_component.rb
95
97
  - app/components/keystone/ui/calculation_component.rb
98
+ - app/components/keystone/ui/info_component.rb
99
+ - app/components/keystone/ui/info_component.html.erb
100
+ - app/components/keystone/ui/breakdown_component.rb
101
+ - app/components/keystone/ui/breakdown_component.html.erb
96
102
  - app/components/keystone/ui/feature_grid_component.rb
97
103
  - app/components/keystone/ui/file_upload_component.rb
98
104
  - app/components/keystone/ui/form_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.27.0
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -87,6 +87,8 @@ files:
87
87
  - app/components/keystone/ui/bottom_nav_item_component.rb
88
88
  - app/components/keystone/ui/breadcrumbs_component.html.erb
89
89
  - app/components/keystone/ui/breadcrumbs_component.rb
90
+ - app/components/keystone/ui/breakdown_component.html.erb
91
+ - app/components/keystone/ui/breakdown_component.rb
90
92
  - app/components/keystone/ui/bucket_component.html.erb
91
93
  - app/components/keystone/ui/bucket_component.rb
92
94
  - app/components/keystone/ui/bucket_series_component.html.erb
@@ -136,6 +138,8 @@ files:
136
138
  - app/components/keystone/ui/grid_component.rb
137
139
  - app/components/keystone/ui/hero_component.html.erb
138
140
  - app/components/keystone/ui/hero_component.rb
141
+ - app/components/keystone/ui/info_component.html.erb
142
+ - app/components/keystone/ui/info_component.rb
139
143
  - app/components/keystone/ui/input_component.html.erb
140
144
  - app/components/keystone/ui/input_component.rb
141
145
  - app/components/keystone/ui/line_chart_component.html.erb