keystone_ui 0.37.0 → 0.39.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.
@@ -1,250 +1,106 @@
1
1
  ---
2
2
  name: keystone_ui-info
3
- description: Use to learn what Keystone UI offers — what the component system covers, its layout, color and theme model, and the vocabulary the install and develop locals assume.
3
+ description: Use to learn what keystone_ui offers — its UI components for pages, forms, tables, navigation and dashboards, its look, colour and theme model, and the vocabulary the install and develop locals assume.
4
4
  tools: Read
5
5
  scope: UI — pages, forms, tables, navigation, dashboards
6
6
  ---
7
7
 
8
- This local explains Keystone UI and sends you to the local that does the work.
9
- It changes nothing and gives no steps.
8
+ This local explains keystone_ui and says which local does the work. It changes
9
+ nothing and gives no steps.
10
10
 
11
- ## What Keystone UI is
11
+ ## What keystone_ui is
12
12
 
13
- Keystone UI is a Rails engine gem that supplies a host app's visual layer as a
14
- library of view helpers built on ViewComponent. Screens are built from named
15
- pieces — page shells, sections, panels, grids, form fields, data tables,
16
- navigation bars, breadcrumbs, cards, stat tiles, charts, funnels, goal buckets, pipelines,
17
- banners, the calculation behind a figure, a breakdown of amounts and their
18
- total, a figure coloured as a gain or a loss, an info button — instead of hand-written ERB and
19
- Tailwind. Every class the UI renders lives inside the gem, in frozen constants,
20
- so the look is defined in one place.
13
+ keystone_ui is a Rails engine gem that supplies a host app's UI as view helpers
14
+ built on ViewComponent. Screens are built from named pieces — page shells,
15
+ sections, panels, grids, forms and fields, data tables, navigation, cards, stat
16
+ tiles, charts, funnels, goal buckets, pipelines, breakdowns and info buttons —
17
+ instead of hand-written ERB and Tailwind. Every class the UI renders lives in the
18
+ gem, so two pages built from the same helpers cannot disagree about spacing,
19
+ colour or dark-mode treatment, and a change to a component reaches every page
20
+ that uses it.
21
21
 
22
22
  Reach for it whenever you build or change a screen in an app that has it
23
- installed. It exists to stop UI drift: two pages built from the same helpers
24
- cannot disagree about spacing, color or dark-mode treatment, and a change to a
25
- component updates every page that uses it. It is mobile-first — components ship
26
- separate mobile and desktop treatments (a bottom tab bar and mobile header on
27
- small screens, a full navigation bar from the `lg:` breakpoint up), because
28
- these apps are often viewed in a native webview. The form and show page shells
29
- follow the same split for going back: the mobile header carries the back arrow on
30
- small screens, and from `lg:` up the shell shows a "Back" link. Given a trail of
31
- earlier pages, the shell shows breadcrumbs under that "Back" link, ending with
32
- the page's own title. A page that passes no trail gets the one the app or
33
- another gem supplies for the request, if any, and a page that passes no back
34
- link goes back to that trail's last link. A trail or back link the page passes
35
- itself always wins. Supplying a trail is set up through the install local.
36
-
37
- A nav tab's own page has nowhere to go back to, and it says so by passing an
38
- empty trail. Such a page shows no "Back" link and no breadcrumbs, and its mobile
39
- header shows no back arrow. Every other form and show page must end up with a
40
- Back link. A page with no back link, no trail and no supplied trail raises
41
- `KeystoneUi::MissingBackLink` when it renders, and the message names the page's
42
- title and says an empty trail marks a nav tab's page. A trail with a link
43
- missing its label or its address raises `KeystoneUi::IncompleteTrail`, also
44
- naming the page. The "Back" link, the breadcrumbs and the form page's title
45
- appear at the top of the page container, inside its width and padding, so a
46
- page using either shell also uses the page container.
23
+ installed. It is mobile-first, because these apps are often shown in a native
24
+ web view: small screens get a mobile header and a bottom tab bar, and from the
25
+ `lg:` breakpoint up the app gets a full navigation bar or sidebar.
47
26
 
48
27
  ## Interface
49
28
 
50
- This local declares no commands. The two working surfaces belong elsewhere:
29
+ This local declares no commands. The working surfaces belong to the other two:
51
30
 
52
- - **Getting the gem into a host app** — adding it, wiring Tailwind, Stimulus and
53
- the theme attributes on the layout, setting the palette, and the suppliers
54
- for trails, themes, looks and saved table layouts → **`keystone_ui-install`**.
55
- - **Building UI with it** — which helper renders what, what keywords it takes,
56
- how helpers nest → **`keystone_ui-develop`**.
31
+ - **Getting the gem into a host app** — the install generator, the
32
+ configuration block, the theme and look attributes on the layout, the palette,
33
+ registered looks, the navigation groups, and the suppliers for trails, themes,
34
+ looks, the current tab and saved preferences → **`keystone_ui-install`**.
35
+ - **Building screens with it** — which helper renders what, the keywords each
36
+ takes, and how they nest → **`keystone_ui-develop`**.
57
37
 
58
38
  ## How to use it
59
39
 
60
- One decision: is Keystone UI already set up in the app?
40
+ One decision: is keystone_ui set up in the app?
61
41
 
62
42
  - No, or the setup is out of date → **`keystone_ui-install`**.
63
- - Yes, and you have a screen to build or edit → **`keystone_ui-develop`**. Do not
64
- hand-write ERB or Tailwind for UI it covers.
65
-
66
- Questions about which piece fits a scenario also go to the develop local, which
67
- holds the catalog.
43
+ - Yes, and there is a screen to build or edit, or a question about which piece
44
+ fits → **`keystone_ui-develop`**. Do not hand-write ERB or Tailwind for UI it
45
+ covers.
68
46
 
69
47
  ## Conventions
70
48
 
71
- - **Helpers, not classes.** Every piece of UI is a view helper prefixed `ui_`,
72
- called from ERB. The one layout helper that is not prefixed `ui_` writes the
73
- theme onto the page's `html` tag, and it belongs to the install local.
74
- Components live under the `Keystone::Ui` namespace, but a host app does not
75
- name a component class directly. The one exception is the table column value
76
- object, which is passed as an argument and renders nothing.
77
- - **Containers take blocks, leaves take keywords.** Helpers that wrap content
78
- (page shells, sections, panels, grids, forms, tables) yield a block. Helpers
79
- that render one thing (a button, a badge, a field, a stat, a bucket) are
80
- configured entirely by keyword arguments. The navigation bar exposes named
81
- slots instead of a single block.
82
- - **Action menus.** An action menu is an ellipsis button that opens a dropdown
83
- of actions, shown at every screen size. The mobile action menu is the one
84
- hidden from `lg:` up. Each action is a label, an address and an HTTP method.
85
- A plain visit renders as a link. Any other method renders as a button that
86
- sends that method, so an action that changes data is never a bare link. An
87
- action can carry a question that Turbo asks before it runs. A delete asks
88
- "<label> this? This cannot be undone." unless it is given its own question,
89
- 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,
90
- and a section can carry an action menu in its header beside its title.
91
- - **Info panels.** A stat card, a radio card and a checkbox row can each carry
92
- extra text about themselves. Given that text, the piece shows an info button,
93
- and the text stays hidden until the button is hovered or tapped. A piece
94
- given no such text shows no button. On a radio card and a checkbox row the
95
- button sits on the line with the label. Wherever it is placed, the button
96
- sits level with the text beside it.
97
- - **Standalone info button.** The same info button can be placed on its own,
98
- beside anything. It takes a short summary, which is required, and shows it
99
- when the button is hovered. Given nothing more, a tap shows the summary too.
100
- Given a block of further detail, a tap opens that detail instead, in a panel
101
- under the button, and the summary stays the hover text. Both popups are
102
- placed against the screen, not against the element the button sits in, so a
103
- table or any other container that clips its contents does not cut them off.
104
- Each opens under the button with its right edge on the button's, the first
105
- time as well as every later time, and is kept on the screen when the button
106
- is near the left edge. The detail panel is wider than the summary. Both carry
107
- one shared popup class from keystone_ui-styles, which decides how their text
108
- wraps and how they fit a narrow screen. A second tap closes
109
- what the first opened, a click anywhere else closes the detail, and
110
- scrolling the page closes both.
111
- - **A breakdown is text.** A breakdown lists amounts, each beside a label
112
- saying what it is, and ends with a total set apart from the lines above it.
113
- Every line and the total are an amount and a label, passed as
114
- already-formatted text and shown as written. The component adds nothing up,
115
- so the total is whatever it is given. Its whole layout comes from
116
- keystone_ui-styles' `ks-breakdown` classes: how the amounts and labels line
117
- up, and how the total is set apart. The component sets no widths or spacing
118
- of its own, so a breakdown looks right only with a keystone_ui-styles version
119
- that defines those classes. It has no button and no hidden state of
120
- its own, and it can be placed inside an info button's detail.
121
- - **A toned figure is text.** A figure shows one amount inline, in plain text
122
- or in the success or danger colour, so it reads as a gain or a loss without a
123
- badge around it. The amount is passed as already-formatted text and shown as
124
- written, and the tone is chosen by name. An unknown tone raises at render
125
- time. The colours come from keystone_ui-styles, so a figure shows its tone
126
- only with a keystone_ui-styles version that defines those classes.
127
- - **The last table column is right-aligned.** In a data table with no actions
128
- column, the last column's header is right-aligned like the cells beneath it,
129
- so a figure or total placed in that header lines up with the figures below.
130
- - **A locked first column.** A table column can be marked locked. When it is
131
- the table's first column, its header and cells stay in view while the rest
132
- of the table scrolls sideways, styled by keystone_ui-styles' locked header
133
- and cell classes. A locked column anywhere else renders as an ordinary
134
- column. A locked column cannot be hidden or moved, so it never appears in the
135
- "Columns" menu and a saved layout leaves it where it is, even when it is also
136
- marked hideable.
137
- - **Saved table layouts.** A data table can be given a key naming it. A table
138
- with a key asks the app, or another gem such as keystone_ui-preferences, for
139
- the layout saved under that key for the request. A saved layout lists the
140
- columns to hide, and only columns marked hideable can be hidden. The columns
141
- the table's own call hides are its default layout. A saved layout that lists
142
- columns to hide replaces that default, and a saved empty list shows every
143
- column. When nothing is saved, or the saved layout lists no columns to hide,
144
- the table keeps its default. A saved layout can also list an order for the
145
- columns. Only hideable columns move: they fill the places hideable columns
146
- held in the table's own call, in the saved order, and any the order leaves
147
- out follow the ones it names. Every other column keeps its place, and a
148
- layout with no order keeps the order the columns were declared in. When the
149
- supplier gives an address to save to, a "Columns" menu appears above the
150
- table, at its right, and saves the user's choice there, even before anything
151
- has been saved. The menu lists the hideable columns in the order the table
152
- shows them, each with a box to show or hide it and buttons to move it up or
153
- down. The menu and the table read the same saved layout, so the menu's
154
- ticked boxes and order always match the columns the table shows. An unticked column's name is greyed at once. Ticking boxes and moving
155
- columns sends nothing while the menu is open. Closing the menu, with its
156
- Columns button or by clicking anywhere outside it, saves the hidden columns
157
- and the order together once, then reloads the page. Closing it with nothing
158
- changed saves nothing. When the save is refused or cannot reach the server,
159
- the page does not reload. The Columns menu shows the message "Your
160
- column changes were not saved." and puts its boxes and order back to what
161
- the table shows.
162
- A table with no key, or an app with no supplier, renders from its own call
163
- alone. Setting up the supplier belongs to the install local.
164
- - **Suggestions.** A form field can carry a list of suggested values. The
165
- browser offers them while the field's text is typed, and the user can still
166
- enter a value that is not on the list. This applies to a field with a typed
167
- input, such as text, number, email, password or date. A textarea, select or
168
- checkbox field ignores the list, and a field given no list offers nothing.
169
- It needs no JavaScript.
170
- - **Options are symbols, and each component accepts its own set.** Appearance is
171
- chosen by name — `variant:`, `size:`, `type:`, `padding:`, `spacing:`,
172
- `max_width:`, `radius:` — on a size scale (`:sm` … `:xl`) or a short list of
173
- named choices. A button's `variant:` and a badge's `variant:` accept
174
- different symbols, and the develop local carries the real values. Most
175
- components raise on a symbol they do not know, so a wrong guess fails at
176
- render time.
177
- - **Measured values are numbers.** Components that measure or compare values, such as
178
- progress bars, funnels and buckets, do arithmetic on them. Pass a number, not
179
- a formatted string such as `"9,000"`. A bucket raises on one.
180
- - **A funnel has two shapes.** Both size each step against the first step and
181
- show the percent carried from one step to the next. The default draws one bar
182
- per step, each with its label and value on a row above it. The joined shape
183
- draws the steps as one figure: each step's value and label sit in a column on
184
- the left, each step is a block centred at its width, and a neutral band
185
- between two blocks narrows from one width to the next and holds the percent.
186
- Any other shape raises `ArgumentError`. Neither shape needs JavaScript.
187
- - **A line chart is labelled or dated.** A line chart draws one line per
188
- series, and each series is a name and its values, with an optional colour
189
- and an optional dashed line. Its horizontal axis is given one of two ways,
190
- never both and never neither, and the wrong combination raises
191
- `ArgumentError`. Labels are text, one per value, spaced evenly and shown as
192
- written. Dates are one day per value, and a dated chart places each point by
193
- its day, so a gap of a week is seven times as wide as a gap of a day. A dated
194
- axis runs from the first day to the last, marks only whole days, and reads
195
- each day as a date such as "Oct 2, 2026", which is also what a hovered point
196
- shows. A time of day is dropped, so a dated chart has one position per day.
197
- - **A calculation is text.** The calculation behind a figure is the opposite
198
- case. It is a list of groups, each with an optional title and its lines, and
199
- each line is a label, the working and the result. They are passed as
200
- already-formatted text and shown as written. The component does no
201
- arithmetic, and it is closed by default under a quiet summary row.
202
- - **Semantic color, not literal color.** The themed hue is `accent-*` and the
203
- themed neutral family is `surface-*`, both CSS custom properties whose
204
- defaults (blue and zinc) come from the keystone_ui-styles gem. Retheming an
205
- app changes those values, not the components. Some components still use
206
- Tailwind's stock `gray-*` and `zinc-*` neutrals, or fixed status colors such
207
- as green and amber, directly. Changing the defaults belongs to the install
208
- local.
209
- - **Looks.** A look is one CSS file that sets the `--ks-` variables from
210
- keystone_ui-styles 0.6.0 or later: corner radius, font, label weight, border
211
- width, padding and colours. Each colour variable has a `-dark` partner read
212
- on a dark page. Buttons, panels, cards, alerts, badges, form fields, the
213
- modal, the action menu, the mobile action menu, the column picker, multi select, copy button,
214
- theme toggle, checkbox row, radio card, option card, file upload and colour
215
- picker read these variables. So do the data display components: stat card,
216
- chart card, card link, CTA banner, feature grid, hero, data table, code,
217
- accordion, disclosure, calculation, tab switcher, progress, funnel, bucket,
218
- pipeline and swipe deck. So do the navigation components: navbar, nav item,
219
- nav dropdown, bottom nav, mobile header, breadcrumbs and settings link. A host imports its
220
- look after `keystone_source.css`, and a gem ships one through
221
- `tailwind_imports`, both set up through the install local.
222
- - **Registered looks.** Looks can also be registered by name, and each page
223
- gets one: the name another gem supplies for the request, then the configured
224
- default, each used only when registered. The layout's `<html>` tag carries
225
- `data-look="<name>"`, and a registered look file scopes its variables to
226
- `:root[data-look="<name>"]`. A host with no registered looks gets no
227
- `data-look`. Registering and choosing looks is set up through the install
228
- local.
229
- - **Light, dark, system and custom themes.** Every component has dark-mode
230
- styling. The theme is chosen in this order: the user's choice stored in a
49
+ - **Helpers, not classes.** Every piece of UI is a view helper prefixed `ui_`.
50
+ Components sit under the `Keystone::Ui` namespace, but a host app does not
51
+ name a component class. The one class a host does name is the table column
52
+ value object, which is passed as an argument and renders nothing.
53
+ - **Containers take blocks, leaves take keywords.** Page shells, sections,
54
+ panels, grids, forms and tables wrap a block. A button, badge, field, stat or
55
+ bucket is set entirely by keyword arguments. The navigation bar takes named
56
+ slots.
57
+ - **Options are symbols.** Appearance is chosen by name, such as `variant:`,
58
+ `size:` or `padding:`, and each component accepts its own set. Most raise on a
59
+ symbol they do not know, so a wrong guess fails at render time.
60
+ - **Numbers and text are different inputs.** Progress bars, funnels and buckets
61
+ do arithmetic, so they take numbers, and a bucket raises on `"9,000"`. A
62
+ breakdown, a calculation and a toned figure take already-formatted text, show
63
+ it as written and add nothing up.
64
+ - **Going back.** A form or show page shell needs a way back: a back link, a
65
+ trail of earlier pages shown as breadcrumbs, or a trail the app supplies for
66
+ the request. A nav tab's own page passes an empty trail and shows no back
67
+ link. A page with none of these raises `KeystoneUi::MissingBackLink`, and a
68
+ trail link missing a label or an address raises `KeystoneUi::IncompleteTrail`.
69
+ - **Navigation.** The app declares navigation groups, each a label and its tabs,
70
+ and each tab a key, label, address and permission check. Tabs the user may
71
+ not see are left out, and a group left with no tabs is left out. A saved
72
+ preference can place the navigation as a top bar or a left or right sidebar
73
+ and set the order of groups and tabs. A supplier names the current tab, which
74
+ is marked active with its group. A declaration with an empty group, a tab with
75
+ no label or address, or two tabs sharing a key stops the app at boot with
76
+ `KeystoneUi::NavigationCheck::Error`.
77
+ - **Saved table layouts.** A data table given a key reads the layout saved under
78
+ it: which hideable columns to hide and their order. With a save address, a
79
+ "Columns" menu lets the user change it. A locked first column stays in view
80
+ while the table scrolls sideways and is never hidden or moved.
81
+ - **Action menus.** Each action is a label, an address and an HTTP method. A
82
+ plain visit is a link, and any other method is a button, so an action that
83
+ changes data is never a bare link. A delete asks for confirmation by default.
84
+ - **Semantic colour.** The themed hue is `accent-*` and the themed neutrals are
85
+ `surface-*`, CSS custom properties whose defaults (blue and zinc) come from
86
+ the keystone_ui-styles gem. Retheming changes those values, not the
87
+ components.
88
+ - **Looks.** A look is one CSS file setting keystone_ui-styles' `--ks-`
89
+ variables for radius, font, weight, border, spacing and colour, each colour
90
+ with a `-dark` partner. Components render `ks-` classes that read them, and
91
+ their own Tailwind constants hold only layout, size and behaviour. Looks can
92
+ be registered by name, and the page's `html` tag carries `data-look` for the
93
+ supplied look or the default, when that name is registered.
94
+ - **Themes.** Light, dark, system and custom. The mode comes from the user's
231
95
  cookie, then a mode another gem supplies, then light. System leaves the page
232
- to follow the operating system. Custom marks the page for a palette another
233
- gem defines. The theme toggle does not offer it, so a page reaches it when
234
- another gem supplies it. On a Turbo visit, the page being shown sets the
235
- theme, so a change to the mode on the server shows up without a full reload.
236
- Marking the layout with the theme is set up through the install local, and
237
- placing the toggle on a screen goes through the develop local.
238
- - **Tailwind classes are static strings.** Class names are never interpolated,
239
- so Tailwind's scanner can find them. Widths and heights that depend on data,
240
- such as a progress bar or a bucket's fill, are set with an inline style
241
- instead. The host needs tailwindcss-rails v4+, and the engine tells Tailwind
242
- at boot where the component files are.
243
- - **Interactivity is Stimulus.** Dropdowns, modals, dismissible alerts, file
244
- uploads, the color picker, the multi-select, tab switchers, accordions, the
245
- swipe deck, column pickers, info panels, line charts, clipboard copy
246
- and the theme toggle ship with the gem as Stimulus controllers registered once
247
- at install. A host writes no JavaScript to use them. Components that post
248
- somewhere, such as the column picker and the pipeline, post to endpoints the
249
- host app or another gem owns.
250
- - Ruby >= 3.2. ViewComponent >= 2.0 and < 5.
96
+ to follow the operating system, and custom marks the page for a palette
97
+ another gem defines, which the theme toggle does not offer.
98
+ - **Static Tailwind classes.** Class names are never interpolated, so Tailwind's
99
+ scanner finds them. Sizes that depend on data, such as a progress bar's
100
+ width, use an inline style. The host needs tailwindcss-rails v4+.
101
+ - **Interactivity is Stimulus.** Dropdowns, modals, tab switchers, the column
102
+ picker, info buttons, charts, copy buttons and the theme toggle ship as
103
+ Stimulus controllers, so a host writes no JavaScript. Components that post,
104
+ such as the column picker and the pipeline, post to endpoints the host or
105
+ another gem owns.
106
+ - Ruby >= 3.2, ViewComponent >= 2.0 and < 5, keystone_ui-styles >= 0.14.0.
@@ -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, the saved table layout 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, the navigation groups and current tab supplier, and extra Tailwind imports and sources.
4
4
  tools: Bash, Read, Edit
5
5
  scope: UI — pages, forms, tables, navigation, dashboards
6
6
  ---
@@ -28,7 +28,12 @@ built on ViewComponent; hook it in before building any screen with those helpers
28
28
  show pages that pass none, and their Back link when they pass none either),
29
29
  `preference_supplier` (a callable that supplies the saved hidden columns and
30
30
  column order of a data table given a `key:`, and the address its Columns menu
31
- saves to),
31
+ saves to, and the navigation's placement under the key `:navigation`),
32
+ `navigation_group` (a labelled group of desktop tabs that the navigation
33
+ helper draws, each tab declared with `tab key, label:, href:, permitted:`),
34
+ `current_tab_supplier` (a callable that supplies the key of the declared tab
35
+ the current page belongs to, so the navigation marks it and its group
36
+ active),
32
37
  and the `tailwind_imports` and `tailwind_sources` lists (extra CSS files and
33
38
  scan paths added to the Tailwind build).
34
39
  - `keystone_theme_attributes` — a layout helper placed inside the `<html>` tag.
@@ -43,7 +48,7 @@ built on ViewComponent; hook it in before building any screen with those helpers
43
48
  ## How to use it
44
49
 
45
50
  1. Confirm the prerequisites: Ruby >= 3.2 and **tailwindcss-rails v4+** in the
46
- host app. The gem brings ViewComponent and keystone_ui-styles 0.12.0 or later with it.
51
+ host app. The gem brings ViewComponent and keystone_ui-styles 0.14.0 or later with it.
47
52
  Tailwind does not have to be initialized first, because the generator creates
48
53
  the stylesheet if it is missing.
49
54
 
@@ -183,6 +188,10 @@ built on ViewComponent; hook it in before building any screen with those helpers
183
188
  config.look_supplier = ->(view) { view.current_user&.look }
184
189
  config.trail_supplier = ->(view) { view.breadcrumb_trail }
185
190
  config.preference_supplier = ->(view, key) { TablePreferences.for(view.current_user, key) }
191
+ config.navigation_group "Sales" do |group|
192
+ group.tab :quotes, label: "Quotes", href: ->(view) { view.quotes_path }, permitted: ->(view) { view.policy(Quote).index? }
193
+ end
194
+ config.current_tab_supplier = ->(view) { view.controller.try(:navigation_tab) }
186
195
  config.tailwind_imports << "/absolute/path/to/extra.css"
187
196
  config.tailwind_sources << "/absolute/path/to/components/**/*.{erb,rb}"
188
197
  end
@@ -226,9 +235,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
226
235
  pages, and `nil` for a page whose Back link comes from its own
227
236
  `back_url:`.
228
237
  - Every other form and show page must end up with a Back link. A page that
229
- passes no `back_url:`, and has no trail or a trail whose last link has no
230
- address, raises `KeystoneUi::MissingBackLink` naming the page's title when
231
- it renders. The message tells the developer to pass `back_url:` or
238
+ passes no `back_url:`, and has no trail or a trail whose last link's
239
+ address is `nil`, raises `KeystoneUi::MissingBackLink` naming the page's
240
+ title when it renders. The message tells the developer to pass `back_url:` or
232
241
  `trail:`, to supply a trail through `trail_supplier`, or to pass
233
242
  `trail: []` on a nav tab's own page.
234
243
  - Every link in a trail needs both a label and an address. A trail with a
@@ -287,9 +296,44 @@ built on ViewComponent; hook it in before building any screen with those helpers
287
296
  page unreloaded. The table then shows "Your column changes were not
288
297
  saved." beside its Columns button, and the menu's boxes and order go back
289
298
  to what the table shows.
299
+ - For the key `:navigation` the supplier returns the person's navigation
300
+ placement as `{ value: { "placement" => "left" } }`, with `"left"`,
301
+ `"right"` or `"top"`. The navigation draws a sidebar on the left or the
302
+ right of the page content on desktop screens for the first two, and the
303
+ top bar for `"top"`, `nil`, a value with no `"placement"` string key, or
304
+ any other placement.
305
+ - The same `:navigation` value may hold the person's order of groups and
306
+ tabs under `"order"`, as
307
+ `[ { "group" => "Admin" }, { "group" => "Sales", "tabs" => [ "orders", "quotes" ] } ]`.
308
+ A group is named by its declared label and a tab by its key as a string.
309
+ The navigation draws the named groups and tabs first in that order, then
310
+ the rest in declared order, and ignores names no longer declared.
290
311
  - A companion preferences gem may set this supplier for the app. Ask the
291
312
  developer whether the app uses one, or which code stores each user's table
292
313
  layouts, before writing the callable.
314
+ - `navigation_group "Label" do |group| ... end` — declares one menu of
315
+ desktop tabs, in the order the groups are declared. Inside the block,
316
+ `group.tab key, label:, href:, permitted:` adds a tab in order. `href:` is
317
+ an address or a callable that receives the view and returns one.
318
+ `permitted:` is a callable that receives the view and returns whether the
319
+ current person may see the tab. A tab the person may not see is left
320
+ out, and a group with no tab left is left out. Ask the developer which
321
+ tabs and groups the app has, and which permission check guards each,
322
+ before declaring them. Boot stops with
323
+ `KeystoneUi::NavigationCheck::Error` when a tab has no label or no
324
+ `href:`, when two tabs share a key, or when a group has no tabs. Drawing
325
+ the declared groups in the layout is done with the navigation helper,
326
+ which belongs to `keystone_ui-develop`.
327
+ - `current_tab_supplier` — a callable that receives the view and returns the
328
+ key of the declared tab the current page belongs to, or `nil`.
329
+ The navigation shows that tab and its group as active in the top bar and
330
+ gives that tab and its group's label the `active` class in the sidebar.
331
+ The key must equal the declared key exactly, so a tab declared as
332
+ `:quotes` is not matched by the string `"quotes"`.
333
+ `nil`, a key no declared tab has, or no supplier marks nothing. The
334
+ callable must answer for every page the layout draws, so a page with no
335
+ tab returns `nil` rather than raising. Ask the developer how a page names
336
+ its tab, such as a method on each controller, before writing the callable.
293
337
  - `tailwind_imports` and `tailwind_sources` — lists to append to, never
294
338
  assign. Each import becomes an `@import` line and each source becomes an
295
339
  `@source` line in `keystone_source.css` on the next boot. They are for
@@ -39,6 +39,7 @@ develop:
39
39
  - ui_action_menu
40
40
  - ui_action_menu_item
41
41
  - ui_navbar
42
+ - ui_navigation
42
43
  - ui_nav_item
43
44
  - ui_nav_dropdown
44
45
  - ui_bottom_nav
@@ -71,6 +72,9 @@ sources:
71
72
  - app/components/keystone/ui/figure_component.rb
72
73
  - lib/generators/keystone/install_generator.rb
73
74
  - lib/keystone_ui/configuration.rb
75
+ - lib/keystone_ui/navigation_group.rb
76
+ - lib/keystone_ui/navigation_order.rb
77
+ - lib/keystone_ui/visible_navigation.rb
74
78
  - lib/keystone_ui/missing_back_link.rb
75
79
  - lib/keystone_ui/incomplete_trail.rb
76
80
  - lib/keystone_ui/theme_choice.rb
@@ -131,6 +135,8 @@ sources:
131
135
  - app/components/keystone/ui/nav_dropdown_component.rb
132
136
  - app/components/keystone/ui/nav_item_component.rb
133
137
  - app/components/keystone/ui/navbar_component.rb
138
+ - app/components/keystone/ui/navigation_component.rb
139
+ - app/components/keystone/ui/navigation_component.html.erb
134
140
  - app/components/keystone/ui/option_card_component.rb
135
141
  - app/components/keystone/ui/page_component.rb
136
142
  - app/components/keystone/ui/page_component.html.erb
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.37.0
4
+ version: 0.39.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.12.0
18
+ version: 0.14.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.12.0
25
+ version: 0.14.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: view_component
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -161,6 +161,8 @@ files:
161
161
  - app/components/keystone/ui/nav_item_component.rb
162
162
  - app/components/keystone/ui/navbar_component.html.erb
163
163
  - app/components/keystone/ui/navbar_component.rb
164
+ - app/components/keystone/ui/navigation_component.html.erb
165
+ - app/components/keystone/ui/navigation_component.rb
164
166
  - app/components/keystone/ui/option_card_component.html.erb
165
167
  - app/components/keystone/ui/option_card_component.rb
166
168
  - app/components/keystone/ui/page_component.html.erb
@@ -206,10 +208,14 @@ files:
206
208
  - lib/keystone_ui/look_check.rb
207
209
  - lib/keystone_ui/look_choice.rb
208
210
  - lib/keystone_ui/missing_back_link.rb
211
+ - lib/keystone_ui/navigation_check.rb
212
+ - lib/keystone_ui/navigation_group.rb
213
+ - lib/keystone_ui/navigation_order.rb
209
214
  - lib/keystone_ui/safelist.rb
210
215
  - lib/keystone_ui/source_css.rb
211
216
  - lib/keystone_ui/theme_choice.rb
212
217
  - lib/keystone_ui/version.rb
218
+ - lib/keystone_ui/visible_navigation.rb
213
219
  - the_local/agents/keystone_ui-develop.md
214
220
  - the_local/agents/keystone_ui-info.md
215
221
  - the_local/agents/keystone_ui-install.md