keystone_ui 0.20.0 → 0.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2831a7bc53b87bd905d8b270f78c26d1d47a8fe32bff2bf53c60093b8c84cf03
4
- data.tar.gz: 36cf740c39fbeb33f4cc27d9ea4942e3ddfac9712856c195de49d3cde4989f66
3
+ metadata.gz: 2c400b566f0760e79b2808012f91e7b4d978ec0ad7e7caa8b83dc0e18b1007ec
4
+ data.tar.gz: 594c632edd55b0ae49c62226ae0e8ebdcc38469827abf0f508c87f9812f79aa2
5
5
  SHA512:
6
- metadata.gz: ddf44be2fc4062b9ab5655f72d613dad4d6e367cfd138c95513adfd1cb749df42c7b1309972d688b629247b792cceba97f568c55850609209e6e1ae9426972c9
7
- data.tar.gz: 70507b228b07861f2ca48449bfa3dbeb0b28019bd5f3a5a22eaf2cfa4ba852d160a7401ec0058c7a8e832afea1a06e1c548b312bab74a8f5efa93d5f05f903b1
6
+ metadata.gz: ed9434328fd13e5c1a48060beba4174ebb78d72b36fbec129389571c40661a58ef5adc6e1245e19186a92a31738a80e4ba4896390b883b909d45d33ce1de93d9
7
+ data.tar.gz: 9ae4ec9ec70310c666b8c3bd810517099e5d03566f04763c706df9193b705d512c585f0b64427db6999acd0318f88047f24af0dcc6cea65971abbf6a8c558dd6
@@ -0,0 +1,14 @@
1
+ <nav aria-label="Breadcrumb" class="<%= CLASSES %>">
2
+ <% @trail.each_with_index do |(label, href), index| %>
3
+ <% if index.positive? %>
4
+ <span aria-hidden="true"><%= SEPARATOR %></span>
5
+ <% end %>
6
+ <%= link_to label, href %>
7
+ <% end %>
8
+ <% if @current %>
9
+ <% if @trail.any? %>
10
+ <span aria-hidden="true"><%= SEPARATOR %></span>
11
+ <% end %>
12
+ <span aria-current="page"><%= @current %></span>
13
+ <% end %>
14
+ </nav>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class BreadcrumbsComponent < ViewComponent::Base
6
+ CLASSES = "ks-mobile-header-back hidden lg:block text-sm"
7
+ SEPARATOR = "›"
8
+
9
+ def initialize(trail:, current: nil)
10
+ @trail = trail
11
+ @current = current
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ <%= link_to @url, class: CLASSES do %>
2
+ <%= Keystone::Ui::MobileHeaderComponent::BACK_ICON.html_safe %>
3
+ <%= LABEL %>
4
+ <% end %>
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class DesktopBackLinkComponent < ViewComponent::Base
6
+ CLASSES = "ks-mobile-header-back hidden lg:inline-flex items-center"
7
+ LABEL = "Back"
8
+
9
+ def initialize(url:)
10
+ @url = url
11
+ end
12
+ end
13
+ end
14
+ end
@@ -2,9 +2,17 @@
2
2
  <% content_for :form_page_title, @title %>
3
3
  <% content_for :form_page_back_url, @back_url %>
4
4
 
5
- <div class="<%= DESKTOP_WRAPPER_CLASSES %>">
6
- <h1 class="<%= TITLE_CLASSES %>"><%= @title %></h1>
7
- <% if subtitle? %>
8
- <p class="<%= SUBTITLE_CLASSES %>"><%= @subtitle %></p>
9
- <% end %>
10
- </div>
5
+ <% if @trail %>
6
+ <% content_for :keystone_page_header, render(Keystone::Ui::BreadcrumbsComponent.new(trail: @trail, current: @title)) %>
7
+ <% else %>
8
+ <% content_for :keystone_page_header, render(Keystone::Ui::DesktopBackLinkComponent.new(url: @back_url)) %>
9
+ <% end %>
10
+
11
+ <% content_for :keystone_page_header do %>
12
+ <div class="<%= DESKTOP_WRAPPER_CLASSES %>">
13
+ <h1 class="<%= TITLE_CLASSES %>"><%= @title %></h1>
14
+ <% if subtitle? %>
15
+ <p class="<%= SUBTITLE_CLASSES %>"><%= @subtitle %></p>
16
+ <% end %>
17
+ </div>
18
+ <% end %>
@@ -7,10 +7,11 @@ module Keystone
7
7
  TITLE_CLASSES = "ks-page-title text-2xl"
8
8
  SUBTITLE_CLASSES = "ks-page-header-subtitle text-sm"
9
9
 
10
- def initialize(title:, back_url:, subtitle: nil)
10
+ def initialize(title:, back_url:, subtitle: nil, trail: nil)
11
11
  @title = title
12
12
  @back_url = back_url
13
13
  @subtitle = subtitle
14
+ @trail = trail
14
15
  end
15
16
 
16
17
  def subtitle?
@@ -1,3 +1,4 @@
1
1
  <div class="<%= classes %>">
2
+ <%= content_for(:keystone_page_header) %>
2
3
  <%= content %>
3
4
  </div>
@@ -2,3 +2,9 @@
2
2
  <% content_for :show_page_title, @title %>
3
3
  <% content_for :show_page_back_url, @back_url %>
4
4
  <% content_for :show_page_subtitle, @subtitle if @subtitle.present? %>
5
+
6
+ <% if @trail %>
7
+ <% content_for :keystone_page_header, render(Keystone::Ui::BreadcrumbsComponent.new(trail: @trail, current: @title)) %>
8
+ <% else %>
9
+ <% content_for :keystone_page_header, render(Keystone::Ui::DesktopBackLinkComponent.new(url: @back_url)) %>
10
+ <% end %>
@@ -7,10 +7,11 @@ module Keystone
7
7
  TITLE_CLASSES = "ks-page-title text-2xl"
8
8
  SUBTITLE_CLASSES = "ks-page-header-subtitle text-sm"
9
9
 
10
- def initialize(title:, back_url:, subtitle: nil)
10
+ def initialize(title:, back_url:, subtitle: nil, trail: nil)
11
11
  @title = title
12
12
  @back_url = back_url
13
13
  @subtitle = subtitle
14
+ @trail = trail
14
15
  end
15
16
 
16
17
  def subtitle?
@@ -117,6 +117,10 @@ module KeystoneUiHelper
117
117
  render Keystone::Ui::ShowPageComponent.new(**args)
118
118
  end
119
119
 
120
+ def ui_breadcrumbs(**args)
121
+ render Keystone::Ui::BreadcrumbsComponent.new(**args)
122
+ end
123
+
120
124
  def ui_mobile_header(**args)
121
125
  render Keystone::Ui::MobileHeaderComponent.new(**args)
122
126
  end
@@ -57,7 +57,9 @@ module Keystone
57
57
  Keystone::Ui::CodeComponent,
58
58
  Keystone::Ui::DisclosureComponent,
59
59
  Keystone::Ui::ThemeToggleComponent,
60
- Keystone::Ui::CalculationComponent
60
+ Keystone::Ui::CalculationComponent,
61
+ Keystone::Ui::DesktopBackLinkComponent,
62
+ Keystone::Ui::BreadcrumbsComponent
61
63
  ].freeze
62
64
 
63
65
  # 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.20.0"
4
+ VERSION = "0.21.1"
5
5
  end
@@ -42,7 +42,9 @@ outer element. See Conventions before using it.
42
42
  - `ui_page(max_width: :full, padding: :standard, top_offset: nil, class: nil)` —
43
43
  takes a block. The outer wrapper for a screen. `max_width:` `:sm` `:md` `:lg`
44
44
  `:xl` `:full`; `padding:` `:standard` or `:none`; `top_offset:` `:sm` `:md`
45
- `:lg` `:xl` to clear a fixed navbar.
45
+ `:lg` `:xl` to clear a fixed navbar. When `ui_form_page` or `ui_show_page` was
46
+ called earlier on the same screen, `ui_page` renders their back link or
47
+ breadcrumbs, and the form page's title, at its top, above the block.
46
48
  - `ui_section(title: nil, subtitle: nil, action: nil, spacing: :md, class: nil)`
47
49
  — takes a block. A titled block of content with an optional right-aligned
48
50
  link. `action:` is `{ label:, href: }`; `spacing:` `:sm` `:md` `:lg`.
@@ -62,12 +64,27 @@ outer element. See Conventions before using it.
62
64
  `sm:`). Call `header.action { ... }` in the block to place a custom control on
63
65
  the right; only what `action` receives is rendered. Passing `action_url:`
64
66
  publishes that URL and label for a mobile navbar to pick up.
65
- - `ui_form_page(title:, back_url:, subtitle: nil)` — the shell marker for a form
66
- screen. Renders the desktop title block and publishes the title and back URL so
67
- the navbar can render mobile header context.
68
- - `ui_show_page(title:, back_url:, subtitle: nil)` — the shell marker for a
69
- detail screen. Renders nothing itself; only publishes the title, subtitle, and
70
- back URL for the navbar.
67
+ - `ui_form_page(title:, back_url:, subtitle: nil, trail: nil)` — the shell
68
+ marker for a form screen. It renders nothing where it is called. It publishes
69
+ the title and back URL so the navbar can render mobile header context, and
70
+ hands `ui_page` a "Back" link to `back_url` shown from `lg:` up, then the
71
+ title and subtitle shown from `md:` up. Call it before `ui_page`, outside
72
+ `ui_page`'s block, or none of that appears. Passing `trail:` replaces the
73
+ "Back" link with breadcrumbs: the trail's links followed by `title` unlinked,
74
+ as `ui_breadcrumbs` renders them. `back_url:` is still required with a trail,
75
+ because the mobile header still uses it.
76
+ - `ui_show_page(title:, back_url:, subtitle: nil, trail: nil)` — the shell marker
77
+ for a detail screen. It renders nothing where it is called. It publishes the
78
+ title, subtitle, and back URL for the navbar, and hands `ui_page` a "Back"
79
+ link to `back_url` shown from `lg:` up. Call it before `ui_page`, outside
80
+ `ui_page`'s block. It shows no title, so put `ui_page_header` inside the
81
+ `ui_page` block for the desktop title. `trail:` works as it does on
82
+ `ui_form_page`, ending the breadcrumbs with `title` unlinked.
83
+ - `ui_breadcrumbs(trail:, current: nil)` — a line of links shown only from `lg:`
84
+ up. `trail:` is `[[label, href], ...]`, from the top level down, and each pair
85
+ renders as a link, separated by `›`. `current:` is the page being shown,
86
+ rendered last, unlinked, and marked as the current page. On a form or detail
87
+ screen pass `trail:` to the shell instead of calling this helper.
71
88
 
72
89
  ### Navigation
73
90
 
@@ -289,12 +306,29 @@ outer element. See Conventions before using it.
289
306
  composition before inventing one; that screen is the house style.
290
307
 
291
308
  3. Pick the page shell for what you are building:
292
- - a form screen → `ui_form_page`
293
- - a detail screen → `ui_show_page`
309
+ - a form screen → `ui_form_page`, then `ui_page` holding the form
310
+ - a detail screen → `ui_show_page`, then `ui_page` holding `ui_page_header`
311
+ and the details
294
312
  - anything else → `ui_page`, with `ui_page_header` for the desktop title.
295
313
 
296
- `ui_form_page` and `ui_show_page` publish their title and back URL for the
297
- navbar to render as a mobile header. Check the app's layout: if it does not
314
+ ```erb
315
+ <%= ui_form_page(title: "New quote", back_url: quotes_path) %>
316
+ <%= ui_page(max_width: :md) do %>
317
+ <%= ui_form(action: quotes_path) do %>
318
+ ...
319
+ <% end %>
320
+ <% end %>
321
+ ```
322
+
323
+ The shell call always comes first and sits outside `ui_page`'s block,
324
+ because `ui_page` renders what the shell handed it at its own top. A form or
325
+ detail screen without `ui_page` shows no desktop "Back" link or form title.
326
+ Those shells supply the desktop "Back" link themselves, so never add a
327
+ second back link or button to those screens. Whether a screen
328
+ shows that "Back" link or breadcrumbs, and which parent screens the trail
329
+ names, is the developer's choice, so ask before passing `trail:`. Below `lg:` the
330
+ back link comes from `ui_mobile_header`, which the navbar renders from the
331
+ title and back URL these shells publish. Check the app's layout: if it does not
298
332
  already render `ui_mobile_header` from that published context, ask the
299
333
  developer whether to wire it before adding more screens that depend on it.
300
334
 
@@ -369,7 +403,7 @@ outer element. See Conventions before using it.
369
403
  through it, so anything else emitted inside their block is discarded.
370
404
  - **Mobile is not an afterthought.** Several helpers render only on one side of
371
405
  the `lg:` (or `sm:`) breakpoint — page headers, mobile headers, mobile actions,
372
- bottom navigation. A screen needs both treatments; check the small viewport
406
+ bottom navigation, breadcrumbs. A screen needs both treatments; check the small viewport
373
407
  before calling it done.
374
408
  - Out of scope for this local: installing or upgrading the gem, changing the
375
409
  palette or theme defaults, and editing the components themselves. Building a
@@ -13,7 +13,7 @@ It changes nothing and gives no steps.
13
13
  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
- navigation bars, cards, stat tiles, charts, funnels, goal buckets, pipelines,
16
+ navigation bars, breadcrumbs, cards, stat tiles, charts, funnels, goal buckets, pipelines,
17
17
  banners, the calculation behind a figure — instead of hand-written ERB and
18
18
  Tailwind. Every class the UI renders lives inside the gem, in frozen constants,
19
19
  so the look is defined in one place.
@@ -24,7 +24,13 @@ cannot disagree about spacing, color or dark-mode treatment, and a change to a
24
24
  component updates every page that uses it. It is mobile-first — components ship
25
25
  separate mobile and desktop treatments (a bottom tab bar and mobile header on
26
26
  small screens, a full navigation bar from the `lg:` breakpoint up), because
27
- these apps are often viewed in a native webview.
27
+ these apps are often viewed in a native webview. The form and show page shells
28
+ follow the same split for going back: the mobile header carries the back link on
29
+ small screens, and from `lg:` up the shell shows a "Back" link. Given a trail of
30
+ earlier pages instead, the shell shows breadcrumbs in place of that link, ending
31
+ with the page's own title. That link or those breadcrumbs, and the form page's
32
+ title, appear at the top of the page container, inside its width and padding, so
33
+ a page using either shell also uses the page container.
28
34
 
29
35
  ## Interface
30
36
 
@@ -91,7 +97,7 @@ holds the catalog.
91
97
  chart card, card link, CTA banner, feature grid, hero, data table, code,
92
98
  accordion, disclosure, calculation, tab switcher, progress, funnel, bucket,
93
99
  pipeline and swipe deck. So do the navigation components: navbar, nav item,
94
- nav dropdown, bottom nav, mobile header and settings link. A host imports its
100
+ nav dropdown, bottom nav, mobile header, breadcrumbs and settings link. A host imports its
95
101
  look after `keystone_source.css`, and a gem ships one through
96
102
  `tailwind_imports`, both set up through the install local.
97
103
  - **Registered looks.** Looks can also be registered by name, and each page
@@ -154,8 +154,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
154
154
  disclosure, calculation, tab switcher, progress, funnel, bucket, pipeline and
155
155
  swipe deck.
156
156
  So do the navigation components: navbar, nav item, nav dropdown, bottom nav,
157
- mobile header and settings link. The keystone_ui-styles README lists every
158
- variable and its default.
157
+ mobile header and settings link. The desktop "Back" link and the breadcrumbs
158
+ on form and show pages use the mobile header's back link variables. The
159
+ keystone_ui-styles README lists every variable and its default.
159
160
 
160
161
  To offer several looks and choose one per page, register them by name in
161
162
  step 7 rather than importing them here.
@@ -197,6 +198,10 @@ built on ViewComponent; hook it in before building any screen with those helpers
197
198
  this way scopes its variables to `:root[data-look="<name>"]` instead of
198
199
  `:root`, so several can be imported at once.
199
200
  - `default_look = :name` — the look a page gets when nothing else chooses one.
201
+ - The app refuses to boot when `default_look` names a look that is not
202
+ registered, when a registered look's file does not exist, or when the file
203
+ sets no `--ks-` variable inside a `:root[data-look="<name>"]` rule. Use an
204
+ absolute path for each look file.
200
205
  - `look_supplier` — a callable that receives the view and returns a look
201
206
  name for the request. A name that is not registered, or `nil`, leaves the
202
207
  page on the default look.
@@ -33,6 +33,7 @@ develop:
33
33
  - ui_alert
34
34
  - ui_form_page
35
35
  - ui_show_page
36
+ - ui_breadcrumbs
36
37
  - ui_mobile_header
37
38
  - ui_mobile_actions
38
39
  - ui_navbar
@@ -92,6 +93,11 @@ sources:
92
93
  - app/components/keystone/ui/form_component.rb
93
94
  - app/components/keystone/ui/form_field_component.rb
94
95
  - app/components/keystone/ui/form_page_component.rb
96
+ - app/components/keystone/ui/form_page_component.html.erb
97
+ - app/components/keystone/ui/desktop_back_link_component.rb
98
+ - app/components/keystone/ui/desktop_back_link_component.html.erb
99
+ - app/components/keystone/ui/breadcrumbs_component.rb
100
+ - app/components/keystone/ui/breadcrumbs_component.html.erb
95
101
  - app/components/keystone/ui/funnel_component.rb
96
102
  - app/components/keystone/ui/bucket_component.rb
97
103
  - app/components/keystone/ui/bucket_series_component.rb
@@ -108,6 +114,7 @@ sources:
108
114
  - app/components/keystone/ui/navbar_component.rb
109
115
  - app/components/keystone/ui/option_card_component.rb
110
116
  - app/components/keystone/ui/page_component.rb
117
+ - app/components/keystone/ui/page_component.html.erb
111
118
  - app/components/keystone/ui/page_header_component.rb
112
119
  - app/components/keystone/ui/panel_component.rb
113
120
  - app/components/keystone/ui/pipeline_component.rb
@@ -118,6 +125,7 @@ sources:
118
125
  - app/components/keystone/ui/select_component.rb
119
126
  - app/components/keystone/ui/settings_link_component.rb
120
127
  - app/components/keystone/ui/show_page_component.rb
128
+ - app/components/keystone/ui/show_page_component.html.erb
121
129
  - app/components/keystone/ui/stat_card_component.rb
122
130
  - app/components/keystone/ui/stat_card_component.html.erb
123
131
  - app/components/keystone/ui/swipe_deck_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.20.0
4
+ version: 0.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -81,6 +81,8 @@ files:
81
81
  - app/components/keystone/ui/bottom_nav_component.rb
82
82
  - app/components/keystone/ui/bottom_nav_item_component.html.erb
83
83
  - app/components/keystone/ui/bottom_nav_item_component.rb
84
+ - app/components/keystone/ui/breadcrumbs_component.html.erb
85
+ - app/components/keystone/ui/breadcrumbs_component.rb
84
86
  - app/components/keystone/ui/bucket_component.html.erb
85
87
  - app/components/keystone/ui/bucket_component.rb
86
88
  - app/components/keystone/ui/bucket_series_component.html.erb
@@ -110,6 +112,8 @@ files:
110
112
  - app/components/keystone/ui/cta_banner_component.rb
111
113
  - app/components/keystone/ui/data_table_component.html.erb
112
114
  - app/components/keystone/ui/data_table_component.rb
115
+ - app/components/keystone/ui/desktop_back_link_component.html.erb
116
+ - app/components/keystone/ui/desktop_back_link_component.rb
113
117
  - app/components/keystone/ui/disclosure_component.html.erb
114
118
  - app/components/keystone/ui/disclosure_component.rb
115
119
  - app/components/keystone/ui/feature_grid_component.html.erb