keystone_ui 0.6.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 +7 -0
- data/app/assets/javascripts/keystone_ui/accordion_controller.js +21 -0
- data/app/assets/javascripts/keystone_ui/auto_submit_controller.js +7 -0
- data/app/assets/javascripts/keystone_ui/chartjs.js +24 -0
- data/app/assets/javascripts/keystone_ui/clipboard_controller.js +25 -0
- data/app/assets/javascripts/keystone_ui/color_picker_controller.js +158 -0
- data/app/assets/javascripts/keystone_ui/column_picker_controller.js +47 -0
- data/app/assets/javascripts/keystone_ui/dismiss_controller.js +7 -0
- data/app/assets/javascripts/keystone_ui/dropdown_controller.js +16 -0
- data/app/assets/javascripts/keystone_ui/file_upload_controller.js +61 -0
- data/app/assets/javascripts/keystone_ui/index.js +33 -0
- data/app/assets/javascripts/keystone_ui/line_chart_controller.js +38 -0
- data/app/assets/javascripts/keystone_ui/modal_controller.js +19 -0
- data/app/assets/javascripts/keystone_ui/multi_select_controller.js +35 -0
- data/app/assets/javascripts/keystone_ui/stat_card_info_controller.js +9 -0
- data/app/assets/javascripts/keystone_ui/swipe_deck_controller.js +123 -0
- data/app/assets/javascripts/keystone_ui/tab_switcher_controller.js +19 -0
- data/app/assets/tailwind/keystone_ui_engine/color_picker.css +31 -0
- data/app/assets/tailwind/keystone_ui_engine/engine.css +7 -0
- data/app/assets/tailwind/keystone_ui_engine/nav.css +175 -0
- data/app/assets/tailwind/keystone_ui_engine/theme.css +32 -0
- data/app/components/keystone/ui/accordion_component.html.erb +19 -0
- data/app/components/keystone/ui/accordion_component.rb +51 -0
- data/app/components/keystone/ui/alert_component.html.erb +15 -0
- data/app/components/keystone/ui/alert_component.rb +58 -0
- data/app/components/keystone/ui/badge_component.html.erb +1 -0
- data/app/components/keystone/ui/badge_component.rb +28 -0
- data/app/components/keystone/ui/bottom_nav_component.html.erb +3 -0
- data/app/components/keystone/ui/bottom_nav_component.rb +13 -0
- data/app/components/keystone/ui/bottom_nav_item_component.html.erb +4 -0
- data/app/components/keystone/ui/bottom_nav_item_component.rb +24 -0
- data/app/components/keystone/ui/button_component.html.erb +1 -0
- data/app/components/keystone/ui/button_component.rb +57 -0
- data/app/components/keystone/ui/card_component.html.erb +9 -0
- data/app/components/keystone/ui/card_component.rb +33 -0
- data/app/components/keystone/ui/card_link_component.html.erb +3 -0
- data/app/components/keystone/ui/card_link_component.rb +25 -0
- data/app/components/keystone/ui/chart_card_component.html.erb +6 -0
- data/app/components/keystone/ui/chart_card_component.rb +35 -0
- data/app/components/keystone/ui/code_component.html.erb +6 -0
- data/app/components/keystone/ui/code_component.rb +40 -0
- data/app/components/keystone/ui/color_picker_component.html.erb +37 -0
- data/app/components/keystone/ui/color_picker_component.rb +31 -0
- data/app/components/keystone/ui/column.rb +21 -0
- data/app/components/keystone/ui/column_picker_component.html.erb +20 -0
- data/app/components/keystone/ui/column_picker_component.rb +35 -0
- data/app/components/keystone/ui/copy_button_component.html.erb +11 -0
- data/app/components/keystone/ui/copy_button_component.rb +39 -0
- data/app/components/keystone/ui/cta_banner_component.html.erb +9 -0
- data/app/components/keystone/ui/cta_banner_component.rb +39 -0
- data/app/components/keystone/ui/data_table_component.html.erb +54 -0
- data/app/components/keystone/ui/data_table_component.rb +173 -0
- data/app/components/keystone/ui/disclosure_component.html.erb +9 -0
- data/app/components/keystone/ui/disclosure_component.rb +46 -0
- data/app/components/keystone/ui/feature_grid_component.html.erb +17 -0
- data/app/components/keystone/ui/feature_grid_component.rb +55 -0
- data/app/components/keystone/ui/file_upload_component.html.erb +18 -0
- data/app/components/keystone/ui/file_upload_component.rb +85 -0
- data/app/components/keystone/ui/form_component.html.erb +3 -0
- data/app/components/keystone/ui/form_component.rb +27 -0
- data/app/components/keystone/ui/form_field_component.html.erb +36 -0
- data/app/components/keystone/ui/form_field_component.rb +83 -0
- data/app/components/keystone/ui/form_page_component.html.erb +10 -0
- data/app/components/keystone/ui/form_page_component.rb +21 -0
- data/app/components/keystone/ui/funnel_component.html.erb +14 -0
- data/app/components/keystone/ui/funnel_component.rb +85 -0
- data/app/components/keystone/ui/grid_component.html.erb +3 -0
- data/app/components/keystone/ui/grid_component.rb +38 -0
- data/app/components/keystone/ui/hero_component.html.erb +27 -0
- data/app/components/keystone/ui/hero_component.rb +74 -0
- data/app/components/keystone/ui/input_component.html.erb +1 -0
- data/app/components/keystone/ui/input_component.rb +57 -0
- data/app/components/keystone/ui/line_chart_component.html.erb +3 -0
- data/app/components/keystone/ui/line_chart_component.rb +48 -0
- data/app/components/keystone/ui/mobile_actions_component.html.erb +11 -0
- data/app/components/keystone/ui/mobile_actions_component.rb +21 -0
- data/app/components/keystone/ui/mobile_header_component.html.erb +11 -0
- data/app/components/keystone/ui/mobile_header_component.rb +38 -0
- data/app/components/keystone/ui/modal_component.html.erb +13 -0
- data/app/components/keystone/ui/modal_component.rb +68 -0
- data/app/components/keystone/ui/multi_select_component.html.erb +21 -0
- data/app/components/keystone/ui/multi_select_component.rb +40 -0
- data/app/components/keystone/ui/nav_dropdown_component.html.erb +11 -0
- data/app/components/keystone/ui/nav_dropdown_component.rb +33 -0
- data/app/components/keystone/ui/nav_item_component.html.erb +1 -0
- data/app/components/keystone/ui/nav_item_component.rb +21 -0
- data/app/components/keystone/ui/navbar_component.html.erb +34 -0
- data/app/components/keystone/ui/navbar_component.rb +32 -0
- data/app/components/keystone/ui/option_card_component.html.erb +9 -0
- data/app/components/keystone/ui/option_card_component.rb +28 -0
- data/app/components/keystone/ui/page_component.html.erb +3 -0
- data/app/components/keystone/ui/page_component.rb +40 -0
- data/app/components/keystone/ui/page_header_component.html.erb +17 -0
- data/app/components/keystone/ui/page_header_component.rb +42 -0
- data/app/components/keystone/ui/panel_component.html.erb +3 -0
- data/app/components/keystone/ui/panel_component.rb +25 -0
- data/app/components/keystone/ui/pipeline_component.html.erb +34 -0
- data/app/components/keystone/ui/pipeline_component.rb +48 -0
- data/app/components/keystone/ui/progress_component.html.erb +8 -0
- data/app/components/keystone/ui/progress_component.rb +35 -0
- data/app/components/keystone/ui/radio_card_component.html.erb +9 -0
- data/app/components/keystone/ui/radio_card_component.rb +42 -0
- data/app/components/keystone/ui/section_component.html.erb +18 -0
- data/app/components/keystone/ui/section_component.rb +32 -0
- data/app/components/keystone/ui/select_component.html.erb +8 -0
- data/app/components/keystone/ui/select_component.rb +38 -0
- data/app/components/keystone/ui/settings_link_component.html.erb +4 -0
- data/app/components/keystone/ui/settings_link_component.rb +26 -0
- data/app/components/keystone/ui/show_page_component.html.erb +4 -0
- data/app/components/keystone/ui/show_page_component.rb +21 -0
- data/app/components/keystone/ui/stat_card_component.html.erb +22 -0
- data/app/components/keystone/ui/stat_card_component.rb +92 -0
- data/app/components/keystone/ui/swipe_deck_component.html.erb +39 -0
- data/app/components/keystone/ui/swipe_deck_component.rb +52 -0
- data/app/components/keystone/ui/tab_switcher_component.html.erb +14 -0
- data/app/components/keystone/ui/tab_switcher_component.rb +33 -0
- data/app/components/keystone/ui/textarea_component.html.erb +1 -0
- data/app/components/keystone/ui/textarea_component.rb +38 -0
- data/app/helpers/keystone_ui_helper.rb +195 -0
- data/config/importmap.rb +19 -0
- data/lib/generators/keystone/install_generator.rb +90 -0
- data/lib/keystone_ui/configuration.rb +24 -0
- data/lib/keystone_ui/engine.rb +47 -0
- data/lib/keystone_ui/safelist.rb +106 -0
- data/lib/keystone_ui/version.rb +5 -0
- data/lib/keystone_ui.rb +9 -0
- data/the_local/agents/keystone_ui-develop.md +297 -0
- data/the_local/agents/keystone_ui-info.md +81 -0
- data/the_local/agents/keystone_ui-install.md +137 -0
- data/the_local/interface.yml +110 -0
- metadata +203 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystone_ui-develop
|
|
3
|
+
description: Use PROACTIVELY for building or editing screens in a Rails app that has Keystone UI — pages, forms, tables, navigation, dashboards, charts, marketing sections — MUST BE USED instead of hand-writing ERB and Tailwind for UI.
|
|
4
|
+
tools: Read, Write, Edit, Grep
|
|
5
|
+
scope: UI — pages, forms, tables, navigation, dashboards
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
This local builds screens by composing Keystone UI's `ui_*` view helpers in ERB.
|
|
9
|
+
It always works the same way: pick the page shell, fill it with the helpers that
|
|
10
|
+
match the content, and write no Tailwind classes of its own.
|
|
11
|
+
|
|
12
|
+
## What Keystone UI is
|
|
13
|
+
|
|
14
|
+
Keystone UI is a Rails engine that supplies an app's visual layer as a library of
|
|
15
|
+
view helpers. Each helper renders one named piece — a page shell, a section, a
|
|
16
|
+
form field, a data table, a navigation bar, a stat card, a chart — with all
|
|
17
|
+
styling and dark-mode treatment owned inside the gem. Two screens built from the
|
|
18
|
+
same helpers cannot drift apart, and a change to a piece updates every screen at
|
|
19
|
+
once. It is mobile-first: several helpers ship distinct mobile and desktop
|
|
20
|
+
treatments, which matters because these apps are often viewed in a native
|
|
21
|
+
webview.
|
|
22
|
+
|
|
23
|
+
Fire on any request to build or change a screen, view, form, table, navigation,
|
|
24
|
+
or dashboard in an app that has Keystone UI installed. If the `ui_*` helpers are
|
|
25
|
+
not available in the app yet, that is the `keystone_ui-install` local's job, not
|
|
26
|
+
this one's.
|
|
27
|
+
|
|
28
|
+
## Interface
|
|
29
|
+
|
|
30
|
+
Every entry point is a view helper called from ERB. Keywords with defaults are
|
|
31
|
+
optional; the rest are required. Symbol options are validated — an unrecognized
|
|
32
|
+
one raises at render time.
|
|
33
|
+
|
|
34
|
+
### Page shells and layout
|
|
35
|
+
|
|
36
|
+
- `ui_page(max_width: :full, padding: :standard, top_offset: nil)` — takes a
|
|
37
|
+
block. The outer wrapper for a screen. `max_width:` `:sm` `:md` `:lg` `:xl`
|
|
38
|
+
`:full` (anything but `:full` also centers); `padding:` `:standard` or `:none`;
|
|
39
|
+
`top_offset:` `:sm` `:md` `:lg` `:xl` to clear a fixed navbar.
|
|
40
|
+
- `ui_section(title: nil, subtitle: nil, action: nil, spacing: :md)` — takes a
|
|
41
|
+
block. A titled block of content with an optional right-aligned link.
|
|
42
|
+
`action:` is `{ label:, href: }`; `spacing:` `:sm` `:md` `:lg`.
|
|
43
|
+
- `ui_panel(padding: :md, radius: :lg, shadow: true)` — takes a block. A bordered
|
|
44
|
+
card surface. `padding:` `:sm` `:md` `:lg`; `radius:` `:md` `:lg` `:xl`.
|
|
45
|
+
- `ui_grid(cols: { default: 1 }, gap: :md, gap_x: nil, gap_y: nil)` — takes a
|
|
46
|
+
block. A responsive grid. `cols:` maps breakpoints `:default` `:sm` `:md` `:lg`
|
|
47
|
+
to a column count 1–12, e.g. `{ default: 1, md: 3 }`. `gap:` `:sm` `:md` `:lg`
|
|
48
|
+
`:xl`; passing `gap_x:`/`gap_y:` replaces `gap:` entirely.
|
|
49
|
+
- `ui_card_link(href:, padding: :md, shadow: true)` — takes a block. A whole
|
|
50
|
+
panel that is one link. `padding:` `:sm` `:md` `:lg`.
|
|
51
|
+
- `ui_card(title:, summary:, link:, cta: "Read more", edge_to_edge: false)` — a
|
|
52
|
+
fixed title/summary/CTA card. `edge_to_edge: true` drops the side border and
|
|
53
|
+
corner rounding below `sm:` so it spans the full width on mobile.
|
|
54
|
+
- `ui_page_header(title:, subtitle: nil, action_url: nil, action_label: "Add new")`
|
|
55
|
+
— takes a block yielding the header. Desktop-only page title (hidden below
|
|
56
|
+
`sm:`). Call `header.action { ... }` in the block to place a custom control on
|
|
57
|
+
the right; only what `action` receives is rendered. Passing `action_url:`
|
|
58
|
+
publishes that URL and label for a mobile navbar to pick up.
|
|
59
|
+
- `ui_form_page(title:, back_url:, subtitle: nil)` — the shell marker for a form
|
|
60
|
+
screen. Renders the desktop title block and publishes the title and back URL so
|
|
61
|
+
the navbar can render mobile header context.
|
|
62
|
+
- `ui_show_page(title:, back_url:, subtitle: nil)` — the shell marker for a
|
|
63
|
+
detail screen. Renders nothing itself; only publishes the title, subtitle, and
|
|
64
|
+
back URL for the navbar.
|
|
65
|
+
|
|
66
|
+
### Navigation
|
|
67
|
+
|
|
68
|
+
- `ui_navbar(sticky: true)` — takes a block yielding the navbar. Fill named slots
|
|
69
|
+
on it: `logo`, `desktop_links`, `desktop_right`, `mobile_left`,
|
|
70
|
+
`mobile_center`, `mobile_right`. Desktop slots are hidden below `lg:` and the
|
|
71
|
+
mobile slots above it. `desktop_right` renders only when `desktop_links` is
|
|
72
|
+
also filled.
|
|
73
|
+
- `ui_nav_item(label:, href:, active: false)` — one desktop navigation link.
|
|
74
|
+
- `ui_nav_dropdown(title:, area:, active: false)` — takes a block. A navbar
|
|
75
|
+
dropdown; the block holds the menu links.
|
|
76
|
+
- `ui_bottom_nav` — no keywords, takes a block. The mobile bottom tab bar; hidden
|
|
77
|
+
above `lg:` and inside a Hotwire Native webview.
|
|
78
|
+
- `ui_bottom_nav_item(label:, href:, icon:, active: false)` — one bottom tab.
|
|
79
|
+
`icon:` is a raw SVG string.
|
|
80
|
+
- `ui_mobile_header(title:, back_url:, subtitle: nil)` — a back chevron plus
|
|
81
|
+
centered title for mobile; hidden above `lg:`. Place it in the navbar's
|
|
82
|
+
`mobile_left` slot.
|
|
83
|
+
- `ui_mobile_actions` — no keywords, takes a block. An ellipsis dropdown for
|
|
84
|
+
mobile actions; the block holds the menu items. Hidden above `lg:`.
|
|
85
|
+
- `ui_settings_link(label:, href:)` — a full-width settings row with a chevron.
|
|
86
|
+
|
|
87
|
+
### Forms
|
|
88
|
+
|
|
89
|
+
- `ui_form(action:, method: :post, multipart: false, data: nil)` — takes a block.
|
|
90
|
+
The `<form>` wrapper. `method:` may be `:patch`/`:put`/`:delete` and is
|
|
91
|
+
translated for Rails. Set `multipart: true` when the form contains a file
|
|
92
|
+
upload.
|
|
93
|
+
- `ui_form_field(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [])`
|
|
94
|
+
— a labeled field with hint and error text. This is the default way to render
|
|
95
|
+
an input. `type:` `:text` `:number` `:email` `:password` `:date` `:textarea`
|
|
96
|
+
`:checkbox` `:select`. `label:` defaults to the humanized attribute name.
|
|
97
|
+
`options:` is for `:select` and takes `[[label, value], ...]`; a non-required
|
|
98
|
+
select gets a leading blank option. A `:checkbox` submits `"0"` when unchecked
|
|
99
|
+
and `"1"` when checked, and pre-checks when `value:` is `"1"`. `errors:` is an
|
|
100
|
+
array of message strings.
|
|
101
|
+
- `ui_input(name:, type: :text, value: nil, placeholder: nil, disabled: false, min: nil, max: nil, step: nil)`
|
|
102
|
+
— a bare styled input with no label. `type:` `:text` `:number` `:email`
|
|
103
|
+
`:password` `:date`.
|
|
104
|
+
- `ui_textarea(name:, value: nil, rows: 3, placeholder: nil, disabled: false)` —
|
|
105
|
+
a bare styled textarea.
|
|
106
|
+
- `ui_select(name:, options: [], selected: nil, include_blank: nil, disabled: false)`
|
|
107
|
+
— a bare styled select. `options:` is `[[label, value], ...]`;
|
|
108
|
+
`include_blank:` is the text of a leading empty option.
|
|
109
|
+
- `ui_multi_select(name:, label:, options:, selected: [])` — a dropdown of
|
|
110
|
+
checkboxes all posting under `name`. `options:` is `[[label, value], ...]`;
|
|
111
|
+
the trigger reads "All <label>" when nothing is checked and "N selected"
|
|
112
|
+
otherwise.
|
|
113
|
+
- `ui_file_upload(name:, label: nil, accept: nil, multiple: false, hint: nil)` —
|
|
114
|
+
a drop zone with drag-and-drop and selected-file feedback. Requires the
|
|
115
|
+
enclosing form to be multipart.
|
|
116
|
+
- `ui_color_picker(name:, value: "#000000", label: nil)` — a swatch that opens a
|
|
117
|
+
hue/saturation panel and writes the hex into a hidden input named `name`.
|
|
118
|
+
- `ui_radio_card(name:, value:, label:, hint: nil, checked: false)` — a
|
|
119
|
+
selectable card backed by a real radio input; selection styling is pure CSS.
|
|
120
|
+
- `ui_option_card(name:, value:, selected: false, input_data: {}, label_data: {})`
|
|
121
|
+
— takes a block. A radio whose visible body is whatever the block renders.
|
|
122
|
+
`input_data:`/`label_data:` become `data-*` attributes on the input and label.
|
|
123
|
+
|
|
124
|
+
### Tables
|
|
125
|
+
|
|
126
|
+
- `ui_data_table(items:, columns:, empty_message: nil, sort: nil, sort_direction: nil, sort_url: nil, hidden_columns: [])`
|
|
127
|
+
— takes a block yielding the table. `items:` are records or hashes; each cell
|
|
128
|
+
value is read by calling the column key on the item, falling back to `item[key]`.
|
|
129
|
+
`columns:` accepts plain `{ key: "Label" }` hashes or `Keystone::Ui::Column`
|
|
130
|
+
objects. In the block, `table.link(:column_key) { |item| url }` turns that
|
|
131
|
+
column's cells into links and `table.actions { |item| ... }` appends a
|
|
132
|
+
right-aligned actions column. Sorting requires all three of `sort:` (the
|
|
133
|
+
current column key), `sort_direction:` (`:asc`/`:desc`), and `sort_url:` (a
|
|
134
|
+
lambda taking `(column_key, direction)` and returning a URL); headers then
|
|
135
|
+
render as links that flip direction. `hidden_columns:` drops columns
|
|
136
|
+
server-side and only affects columns declared `hideable: true`.
|
|
137
|
+
- `Keystone::Ui::Column.new(key, header_text, mobile_hidden: false, sortable: false, hideable: false)`
|
|
138
|
+
— a column with per-column options, for when a `{ key: "Label" }` hash is not
|
|
139
|
+
enough. `mobile_hidden:` hides the column below `sm:`; `sortable:` opts it into
|
|
140
|
+
sort headers; `hideable:` lets the column picker hide it.
|
|
141
|
+
- `ui_column_picker(columns:, hidden_columns: [], save_url: nil)` — a "Columns"
|
|
142
|
+
dropdown of checkboxes for every `hideable` column. Pass it the same columns
|
|
143
|
+
and hidden keys as the table. On toggle it sends `PATCH save_url` with JSON
|
|
144
|
+
`{ "hidden_columns": ["key", ...] }` and a `X-CSRF-Token` header, then reloads
|
|
145
|
+
the page. The app must provide that endpoint and persist the list.
|
|
146
|
+
|
|
147
|
+
### Content and status
|
|
148
|
+
|
|
149
|
+
- `ui_button(label:, href: nil, variant: :primary, size: :md, type: :submit, data: nil)`
|
|
150
|
+
— renders an `<a>` when `href:` is given and a `<button>` otherwise. `variant:`
|
|
151
|
+
`:primary` `:secondary` `:danger`; `size:` `:sm` `:md` `:lg`; `type:` applies
|
|
152
|
+
only to the button form.
|
|
153
|
+
- `ui_badge(label:, variant: :neutral)` — a pill. `variant:` `:neutral`
|
|
154
|
+
`:success` `:danger` `:warning` `:info`.
|
|
155
|
+
- `ui_alert(message:, type: :info, title: nil, dismissible: false)` — a banner.
|
|
156
|
+
`type:` `:info` `:success` `:warning` `:error`. `dismissible: true` adds a
|
|
157
|
+
close control.
|
|
158
|
+
- `ui_progress(value:, max:, label: nil)` — a labeled progress bar. The percent
|
|
159
|
+
is `value / max`, rounded and clamped at 100.
|
|
160
|
+
- `ui_stat_card(label:, value:, variant: :neutral, suffix: nil, definition: nil, calculation: nil, change: nil)`
|
|
161
|
+
— a single metric tile. `variant:` `:neutral` `:success` `:danger` `:warning`
|
|
162
|
+
`:info` colors the value. `change:` is a signed number rendered as `▲ 4.2%` in
|
|
163
|
+
green when positive, `▼` in red when negative, plain when zero. Passing
|
|
164
|
+
`definition:` and/or `calculation:` adds an info button that reveals them.
|
|
165
|
+
- `ui_copy_button(text:, label: "Copy", success_message: "Copied!", error_message: "Failed!")`
|
|
166
|
+
— copies `text:` to the clipboard.
|
|
167
|
+
- `ui_code(language: nil, caption: nil)` — takes a block holding the code. The
|
|
168
|
+
caption is a header strip above the block; `language:` sets the `language-*`
|
|
169
|
+
class for a highlighter.
|
|
170
|
+
- `ui_disclosure(open: false)` — takes a block yielding the component. Fill its
|
|
171
|
+
`summary` slot with the clickable header; the rest of the block is the body.
|
|
172
|
+
Native `<details>` — no JavaScript.
|
|
173
|
+
- `ui_accordion(items: [])` — a stack of independently expandable rows. `items:`
|
|
174
|
+
is `[{ question:, answer: }, ...]`.
|
|
175
|
+
- `ui_tab_switcher(tabs:)` — takes a block. `tabs:` is an array of label strings;
|
|
176
|
+
the first is active on load. The block renders below the tab bar. Selecting a
|
|
177
|
+
tab dispatches a `tab-switcher:change` event carrying the clicked index —
|
|
178
|
+
showing and hiding the matching panels is the app's job.
|
|
179
|
+
- `ui_modal(title:, size: :md)` — takes a block holding the body. `size:` `:sm`
|
|
180
|
+
`:md` `:lg` `:xl`. Renders hidden; it closes on its own close button and on a
|
|
181
|
+
backdrop click, but the app must supply the control that opens it by targeting
|
|
182
|
+
the modal controller's `open` action.
|
|
183
|
+
- `ui_swipe_deck(items:, empty_title: "All done!", empty_subtitle: nil)` — takes
|
|
184
|
+
a block yielding the deck. Call `deck.item { |item| ... }` in the block to
|
|
185
|
+
render one card's face. Accepting a card dispatches `swipe-deck:complete` and
|
|
186
|
+
rejecting dispatches `swipe-deck:skip`, both carrying the item's id — the app
|
|
187
|
+
must listen and persist the outcome.
|
|
188
|
+
|
|
189
|
+
### Charts and analytics
|
|
190
|
+
|
|
191
|
+
- `ui_chart_card(title:, height: :md)` — takes a block holding a chart. `height:`
|
|
192
|
+
`:sm` `:md` `:lg`.
|
|
193
|
+
- `ui_line_chart(series:, labels:, height: :md)` — a line chart. `labels:` is the
|
|
194
|
+
x-axis labels; `series:` is `[{ name:, data:, color:, dashed: }, ...]` where
|
|
195
|
+
`color:` and `dashed:` are optional. `height:` `:sm` `:md` `:lg`.
|
|
196
|
+
- `ui_funnel(steps:)` — a conversion funnel. `steps:` is `[{ label:, value: }, ...]`
|
|
197
|
+
in order. Bar widths are relative to the first step; the caption between two
|
|
198
|
+
layers is the step-to-step conversion. Divide-by-zero safe, no JavaScript.
|
|
199
|
+
- `ui_pipeline(title:, boxes:, links:, subtitle: nil)` — a staged flow diagram
|
|
200
|
+
for event flows, approval chains, or state machines. `boxes:` is
|
|
201
|
+
`[{ label:, count:, accent:, action: }, ...]` where `count:` and `accent:`
|
|
202
|
+
(`:amber` `:emerald` `:danger` `:muted`) are optional, and `action:` is
|
|
203
|
+
`{ url:, label:, params:, variant: }` rendering a button that POSTs `params`
|
|
204
|
+
to `url`. `links:` has exactly one fewer entry than `boxes:`, each
|
|
205
|
+
`{ url:, params:, broken: }`, rendering a ✓/✗ toggle between two boxes that
|
|
206
|
+
POSTs to flip its state. The app owns every endpoint these post to.
|
|
207
|
+
|
|
208
|
+
### Marketing sections
|
|
209
|
+
|
|
210
|
+
- `ui_hero(title:, subtitle: nil, badge: nil, layout: :split)` — takes a block
|
|
211
|
+
holding the call-to-action buttons, and yields the component so its `aside`
|
|
212
|
+
slot can hold an image or panel. `layout:` `:split` (content beside the aside)
|
|
213
|
+
or `:centered`.
|
|
214
|
+
- `ui_cta_banner(title:, subtitle: nil)` — takes a block holding the
|
|
215
|
+
call-to-action buttons.
|
|
216
|
+
- `ui_feature_grid(title:, features:, subtitle: nil)` — a responsive grid of
|
|
217
|
+
feature cards. `features:` is `[{ icon:, title:, description: }, ...]` where
|
|
218
|
+
`icon:` is a raw SVG or HTML string.
|
|
219
|
+
|
|
220
|
+
## How to use it
|
|
221
|
+
|
|
222
|
+
1. Confirm the helpers are available in the app. If they are not, stop and hand
|
|
223
|
+
off to `keystone_ui-install` — do not hand-roll the markup in the meantime.
|
|
224
|
+
|
|
225
|
+
2. Find the closest existing screen in `app/views/` and read it. Match its
|
|
226
|
+
composition before inventing one; that screen is the house style.
|
|
227
|
+
|
|
228
|
+
3. Pick the page shell for what you are building:
|
|
229
|
+
- a form screen → `ui_form_page`
|
|
230
|
+
- a detail screen → `ui_show_page`
|
|
231
|
+
- anything else → `ui_page`, with `ui_page_header` for the desktop title.
|
|
232
|
+
|
|
233
|
+
`ui_form_page` and `ui_show_page` publish their title and back URL for the
|
|
234
|
+
navbar to render as a mobile header. Check the app's layout: if it does not
|
|
235
|
+
already render `ui_mobile_header` from that published context, ask the
|
|
236
|
+
developer whether to wire it before adding more screens that depend on it.
|
|
237
|
+
|
|
238
|
+
4. Lay out the body with `ui_section` for each titled group, `ui_grid` for
|
|
239
|
+
multi-column arrangements, and `ui_panel` or `ui_card_link` for card
|
|
240
|
+
surfaces. Nest them; each takes a block.
|
|
241
|
+
|
|
242
|
+
5. Fill the body with the leaf helpers from the Interface above. Reach for the
|
|
243
|
+
most specific one that fits — `ui_form_field` over `ui_input`,
|
|
244
|
+
`ui_data_table` over a hand-built `<table>`, `ui_stat_card` over a panel with
|
|
245
|
+
text in it.
|
|
246
|
+
|
|
247
|
+
6. For a table, decide how columns are declared. Use `{ key: "Label" }` hashes
|
|
248
|
+
when every column is plain. Switch the whole set to `Keystone::Ui::Column`
|
|
249
|
+
objects as soon as one column needs `mobile_hidden:`, `sortable:`, or
|
|
250
|
+
`hideable:`.
|
|
251
|
+
|
|
252
|
+
7. Wire up anything that posts back. Several helpers render controls whose
|
|
253
|
+
endpoints the app must own — the column picker's save URL, the pipeline's box
|
|
254
|
+
and link URLs, the swipe deck's outcome events, the modal's open trigger, the
|
|
255
|
+
tab switcher's panel visibility. Each is named in the Interface. These are
|
|
256
|
+
real decisions about the app's domain: where a preference is persisted, what
|
|
257
|
+
a stage's action does, what happens when a card is accepted. Do not invent
|
|
258
|
+
routes or a persistence strategy — put the choice to the developer, then
|
|
259
|
+
implement what they pick.
|
|
260
|
+
|
|
261
|
+
8. Read back what you wrote and delete every Tailwind class and inline `style`
|
|
262
|
+
you added. If the result still needs one, that is a signal the wrong helper
|
|
263
|
+
was chosen — go back to step 5. Bring it to the developer only if no helper
|
|
264
|
+
fits.
|
|
265
|
+
|
|
266
|
+
## Conventions
|
|
267
|
+
|
|
268
|
+
- **Helpers only.** Call `ui_*` helpers from ERB. Never name a component class
|
|
269
|
+
directly in an app. `Keystone::Ui::Column` is the one exception — it is a value
|
|
270
|
+
object passed as an argument, not a thing that renders.
|
|
271
|
+
- **Never hand-write Tailwind for something a helper covers.** Utility classes
|
|
272
|
+
layered onto a helper's output fight the component and drift the moment the gem
|
|
273
|
+
updates. The gem owns spacing, color, borders, radius, shadow, and dark mode.
|
|
274
|
+
- **Never restyle a helper from the outside** — no wrapper div that overrides its
|
|
275
|
+
padding or width, no `class:` smuggled through, no CSS targeting its markup.
|
|
276
|
+
Choose a different option symbol instead, or say the helper does not fit.
|
|
277
|
+
- **Semantic color only.** Themed color is `accent-*` (the brand hue) and
|
|
278
|
+
`surface-*` (the neutral family). Never write a literal color into a view;
|
|
279
|
+
changing the palette is install-local territory.
|
|
280
|
+
- **Options are per-helper.** `variant:`, `size:`, `padding:`, and `spacing:` do
|
|
281
|
+
not share one vocabulary — a button's `variant:` and a badge's `variant:`
|
|
282
|
+
accept different symbols. Use the values listed above; a wrong symbol raises at
|
|
283
|
+
render time rather than degrading quietly.
|
|
284
|
+
- **Containers take blocks, leaves take keywords.** Helpers that wrap content
|
|
285
|
+
yield; helpers that render one thing are configured entirely by keywords.
|
|
286
|
+
Composite helpers (the navbar, the hero, the disclosure) yield the component so
|
|
287
|
+
named slots can be filled. Helpers that yield a receiver for registration —
|
|
288
|
+
the data table, page header, and swipe deck — only render what was registered
|
|
289
|
+
through it, so anything else emitted inside their block is discarded.
|
|
290
|
+
- **Mobile is not an afterthought.** Several helpers render only on one side of
|
|
291
|
+
the `lg:` (or `sm:`) breakpoint — page headers, mobile headers, mobile actions,
|
|
292
|
+
bottom navigation. A screen needs both treatments; check the small viewport
|
|
293
|
+
before calling it done.
|
|
294
|
+
- Out of scope for this local: installing or upgrading the gem, changing the
|
|
295
|
+
palette or theme defaults, and editing the components themselves. Building a
|
|
296
|
+
new UI primitive belongs in the gem, not in a host app's views — raise it with
|
|
297
|
+
the developer rather than approximating it locally.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystone_ui-info
|
|
3
|
+
description: Use to learn what Keystone UI offers — what the component system covers, its layout and color model, and the vocabulary the install and develop locals assume.
|
|
4
|
+
tools: Read
|
|
5
|
+
scope: UI — pages, forms, tables, navigation, dashboards
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
This local explains Keystone UI and hands you off to the local that does the
|
|
9
|
+
work. It changes nothing and gives no steps.
|
|
10
|
+
|
|
11
|
+
## What Keystone UI is
|
|
12
|
+
|
|
13
|
+
Keystone UI is a Rails engine gem that supplies a host app's entire visual
|
|
14
|
+
layer as a library of view helpers built on ViewComponent. Screens are composed
|
|
15
|
+
out of named pieces — page shells, sections, panels, grids, form fields, data
|
|
16
|
+
tables, navigation bars, cards, charts, banners — instead of hand-written ERB
|
|
17
|
+
and Tailwind. Every class the UI renders lives inside the gem, in frozen
|
|
18
|
+
constants, so the look is owned in one place.
|
|
19
|
+
|
|
20
|
+
Reach for it whenever you are building or changing a screen in an app that has
|
|
21
|
+
it installed. The point is to stop UI drift: two pages built from the same
|
|
22
|
+
helpers cannot disagree about spacing, color, or dark-mode treatment, and a
|
|
23
|
+
change to a component updates every page at once. It is mobile-first by
|
|
24
|
+
construction — components ship distinct mobile and desktop treatments (a bottom
|
|
25
|
+
tab bar and mobile header on small screens, a full navigation bar above the
|
|
26
|
+
`lg:` breakpoint), which matters because these apps are often viewed in a native
|
|
27
|
+
webview.
|
|
28
|
+
|
|
29
|
+
## Interface
|
|
30
|
+
|
|
31
|
+
This local declares no commands. The two working surfaces belong elsewhere:
|
|
32
|
+
|
|
33
|
+
- **Getting the gem into a host app** — adding it, wiring Tailwind and Stimulus,
|
|
34
|
+
setting the palette, refreshing the generated reference → **`keystone_ui-install`**.
|
|
35
|
+
- **Building UI with it** — which helper renders what, what keywords it takes,
|
|
36
|
+
how helpers nest → **`keystone_ui-develop`**.
|
|
37
|
+
|
|
38
|
+
## How to use it
|
|
39
|
+
|
|
40
|
+
One decision: is the app already wearing Keystone UI?
|
|
41
|
+
|
|
42
|
+
- No, or it is out of date → **`keystone_ui-install`**.
|
|
43
|
+
- Yes, and you have a screen to build or edit → **`keystone_ui-develop`**. Do not
|
|
44
|
+
hand-write ERB or Tailwind for UI it covers.
|
|
45
|
+
|
|
46
|
+
Questions about which piece fits a scenario are also answered by the develop
|
|
47
|
+
local — it owns the catalog.
|
|
48
|
+
|
|
49
|
+
## Conventions
|
|
50
|
+
|
|
51
|
+
- **Helpers, not classes.** Every entry point is a view helper prefixed `ui_`,
|
|
52
|
+
called from ERB. Components live under the `Keystone::Ui` namespace, but a
|
|
53
|
+
host app should not name a component class directly.
|
|
54
|
+
- **Containers take blocks; leaves take keywords.** Helpers that wrap content
|
|
55
|
+
(page shells, sections, panels, grids, forms, tables) yield a block; helpers
|
|
56
|
+
that render one thing (a button, a badge, a field, a stat) are configured
|
|
57
|
+
entirely by keyword arguments. Composite pieces such as the navigation bar
|
|
58
|
+
expose named slots rather than a single block.
|
|
59
|
+
- **Options are symbols, and the accepted set is per-component.** Appearance is
|
|
60
|
+
chosen by name — `variant:`, `size:`, `type:`, `padding:`, `spacing:`,
|
|
61
|
+
`max_width:`, `radius:` — on a small t-shirt scale (`:sm` … `:xl`) or a short
|
|
62
|
+
semantic list. Do not assume one global vocabulary: a button's `variant:` and
|
|
63
|
+
a badge's `variant:` accept different symbols, and `padding:` means different
|
|
64
|
+
things on a page shell than on a panel. The develop local carries the real
|
|
65
|
+
values. An unrecognized symbol raises rather than degrading silently, so a
|
|
66
|
+
wrong guess fails loudly at render time.
|
|
67
|
+
- **Semantic color, not literal color.** The themed hue is `accent-*` and the
|
|
68
|
+
themed neutral family is `surface-*` — Tailwind v4 CSS custom properties
|
|
69
|
+
defaulting to blue and zinc, so retheming an app is a token change, not a
|
|
70
|
+
component change. Older components still reach for Tailwind's stock
|
|
71
|
+
`gray-*`/`zinc-*` neutrals directly; the accent hue is themed throughout.
|
|
72
|
+
Dark-mode variants are already built into every component. Changing the
|
|
73
|
+
defaults is install-local territory.
|
|
74
|
+
- **Tailwind classes are static strings.** Class names are never interpolated,
|
|
75
|
+
so Tailwind's scanner can see them; the host needs tailwindcss-rails v4+ and
|
|
76
|
+
the engine tells Tailwind where to look.
|
|
77
|
+
- **Interactivity is Stimulus.** Dropdowns, modals, dismissible alerts, file
|
|
78
|
+
uploads, tab switchers, column pickers, clipboard copy and similar behavior
|
|
79
|
+
ship with the gem as Stimulus controllers registered in one call at install —
|
|
80
|
+
a host writes no JavaScript to use them.
|
|
81
|
+
- Ruby >= 3.2. ViewComponent >= 2.0.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
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 and Stimulus, and setting the accent/surface palette.
|
|
4
|
+
tools: Bash, Read, Edit
|
|
5
|
+
scope: UI — pages, forms, tables, navigation, dashboards
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
This local carries the steps for wiring Keystone UI into a host app. Follow them
|
|
9
|
+
in order, exactly as written, and invent none.
|
|
10
|
+
|
|
11
|
+
## What Keystone UI is
|
|
12
|
+
|
|
13
|
+
A Rails engine gem that supplies an app's visual layer as `ui_*` view helpers
|
|
14
|
+
built on ViewComponent; hook it in before building any screen with those helpers.
|
|
15
|
+
|
|
16
|
+
## Interface
|
|
17
|
+
|
|
18
|
+
- `bin/rails generate keystone:install` — sets up the host's Tailwind entry point
|
|
19
|
+
and registers the gem's Stimulus controllers. Idempotent, and safe to re-run.
|
|
20
|
+
- `KeystoneUi.configure` — a block yielding `accent` and `surface`, the names of
|
|
21
|
+
the palettes the app wants (`:blue` and `:zinc` unless set).
|
|
22
|
+
|
|
23
|
+
## How to use it
|
|
24
|
+
|
|
25
|
+
1. Confirm the prerequisites: Ruby >= 3.2 and **tailwindcss-rails v4+** in the
|
|
26
|
+
host app. The gem brings ViewComponent with it. Tailwind does not have to be
|
|
27
|
+
initialized first — the generator creates the stylesheet if it is missing.
|
|
28
|
+
|
|
29
|
+
2. Add the gem to the Gemfile and install it:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
gem "keystone_ui"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If the project sources its own gems from somewhere other than RubyGems (a git
|
|
40
|
+
or path reference), ask the developer which to use rather than choosing.
|
|
41
|
+
|
|
42
|
+
3. Run the generator:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bin/rails generate keystone:install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
It touches two host files:
|
|
49
|
+
|
|
50
|
+
- `app/assets/tailwind/application.css` — creates it with
|
|
51
|
+
`@import "tailwindcss";` and `@import "./keystone_source.css";` if absent;
|
|
52
|
+
otherwise injects the Keystone import after the Tailwind one and strips
|
|
53
|
+
superseded Keystone lines from older installs.
|
|
54
|
+
- `app/javascript/controllers/index.js` — appends
|
|
55
|
+
`import { registerControllers } from "keystone_ui/index"` and
|
|
56
|
+
`registerControllers(application)`.
|
|
57
|
+
|
|
58
|
+
Read its output. If it reports that `app/javascript/controllers/index.js` was
|
|
59
|
+
not found, ask the developer where the Stimulus application is set up and add
|
|
60
|
+
those two lines there — without them, dropdowns, modals, file uploads, the
|
|
61
|
+
column picker and the other interactive components do nothing.
|
|
62
|
+
|
|
63
|
+
4. Restart the app (or rebuild assets). On boot the engine writes
|
|
64
|
+
`app/assets/tailwind/keystone_source.css`, which points Tailwind at the
|
|
65
|
+
component files and pulls in the gem's theme and component CSS. It is written
|
|
66
|
+
only when `application.css` exists **and** contains
|
|
67
|
+
`@import "./keystone_source.css";` — if the file never appears, that import is
|
|
68
|
+
missing.
|
|
69
|
+
|
|
70
|
+
5. Keep the generated file out of git. It holds absolute paths to the gem on the
|
|
71
|
+
machine that booted the app, and is rewritten on every boot — dev server, CI,
|
|
72
|
+
and `assets:precompile` alike. Add to `.gitignore`:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
app/assets/tailwind/keystone_source.css
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Only the `@import` line in `application.css` belongs in the repo.
|
|
79
|
+
|
|
80
|
+
6. Settle the palette. Out of the box the accent scale is blue and the surface
|
|
81
|
+
scale is zinc, with no configuration required. To change them statically, add
|
|
82
|
+
an `@theme` block to `application.css` **after** the two imports and override
|
|
83
|
+
only the shades the app uses:
|
|
84
|
+
|
|
85
|
+
```css
|
|
86
|
+
@import "tailwindcss";
|
|
87
|
+
@import "./keystone_source.css";
|
|
88
|
+
|
|
89
|
+
@theme {
|
|
90
|
+
--color-accent-500: #6366f1;
|
|
91
|
+
--color-accent-600: #4f46e5;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Both scales run 50 through 950. Every component picks the values up with no
|
|
96
|
+
component changes.
|
|
97
|
+
|
|
98
|
+
This is a decision, not a default to assume: fixed app-wide colors set in CSS,
|
|
99
|
+
or per-user colors generated at runtime by a companion theming gem. Ask the
|
|
100
|
+
developer which the app wants before wiring either.
|
|
101
|
+
|
|
102
|
+
7. Only if a companion gem or engine reads the palette choice, declare it in
|
|
103
|
+
`config/initializers/keystone_ui.rb`:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
require "keystone_ui"
|
|
107
|
+
|
|
108
|
+
KeystoneUi.configure do |config|
|
|
109
|
+
config.accent = :emerald
|
|
110
|
+
config.surface = :slate
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Keystone UI itself stores these names and renders from the CSS custom
|
|
115
|
+
properties regardless — setting them changes no color on its own. Colors move
|
|
116
|
+
when the custom properties in step 6 move. The engine reads host configuration
|
|
117
|
+
after initializers have run, so the initializer is the correct home for the
|
|
118
|
+
block.
|
|
119
|
+
|
|
120
|
+
## Conventions
|
|
121
|
+
|
|
122
|
+
- **Verify the install before building anything on it.** `application.css` holds
|
|
123
|
+
both imports; `app/assets/tailwind/keystone_source.css` exists after a boot;
|
|
124
|
+
the Stimulus setup calls `registerControllers(application)`. Then load one page
|
|
125
|
+
that renders a `ui_*` helper and confirm it is styled and that an interactive
|
|
126
|
+
piece (a dropdown, a dismissible alert) responds.
|
|
127
|
+
- **Importmap is the supported JS path.** For apps configured with importmap the
|
|
128
|
+
gem pins its own controllers — and the charting library they need — on boot, so
|
|
129
|
+
the host pins nothing. If the app bundles JavaScript instead (esbuild, bun,
|
|
130
|
+
webpack), the appended `keystone_ui/index` import has no pin behind it — surface
|
|
131
|
+
this to the developer rather than guessing at a bundler configuration.
|
|
132
|
+
- **Re-run the generator after upgrading the gem.** It clears out superseded
|
|
133
|
+
install lines and reports "already up to date" when there is nothing to do.
|
|
134
|
+
- **New components need no re-run.** Tailwind rescans the gem on each build, so
|
|
135
|
+
components added by a later version are styled on the next boot.
|
|
136
|
+
- Building UI with the helpers — which helper to use, what keywords it takes — is
|
|
137
|
+
out of scope here and belongs to `keystone_ui-develop`.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
scope: UI — pages, forms, tables, navigation, dashboards
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
- bin/rails generate keystone:install
|
|
5
|
+
- KeystoneUi.configure
|
|
6
|
+
|
|
7
|
+
develop:
|
|
8
|
+
- ui_accordion
|
|
9
|
+
- ui_tab_switcher
|
|
10
|
+
- ui_stat_card
|
|
11
|
+
- ui_chart_card
|
|
12
|
+
- ui_line_chart
|
|
13
|
+
- ui_cta_banner
|
|
14
|
+
- ui_feature_grid
|
|
15
|
+
- ui_hero
|
|
16
|
+
- ui_modal
|
|
17
|
+
- ui_badge
|
|
18
|
+
- ui_copy_button
|
|
19
|
+
- ui_card
|
|
20
|
+
- ui_button
|
|
21
|
+
- ui_data_table
|
|
22
|
+
- ui_page
|
|
23
|
+
- ui_section
|
|
24
|
+
- ui_grid
|
|
25
|
+
- ui_panel
|
|
26
|
+
- ui_card_link
|
|
27
|
+
- ui_input
|
|
28
|
+
- ui_textarea
|
|
29
|
+
- ui_select
|
|
30
|
+
- ui_form_field
|
|
31
|
+
- ui_page_header
|
|
32
|
+
- ui_alert
|
|
33
|
+
- ui_form_page
|
|
34
|
+
- ui_show_page
|
|
35
|
+
- ui_mobile_header
|
|
36
|
+
- ui_mobile_actions
|
|
37
|
+
- ui_navbar
|
|
38
|
+
- ui_nav_item
|
|
39
|
+
- ui_nav_dropdown
|
|
40
|
+
- ui_bottom_nav
|
|
41
|
+
- ui_bottom_nav_item
|
|
42
|
+
- ui_option_card
|
|
43
|
+
- ui_radio_card
|
|
44
|
+
- ui_progress
|
|
45
|
+
- ui_settings_link
|
|
46
|
+
- ui_color_picker
|
|
47
|
+
- ui_multi_select
|
|
48
|
+
- ui_swipe_deck
|
|
49
|
+
- ui_column_picker
|
|
50
|
+
- ui_form
|
|
51
|
+
- ui_file_upload
|
|
52
|
+
- ui_funnel
|
|
53
|
+
- ui_pipeline
|
|
54
|
+
- ui_code
|
|
55
|
+
- ui_disclosure
|
|
56
|
+
- Keystone::Ui::Column
|
|
57
|
+
|
|
58
|
+
sources:
|
|
59
|
+
- lib/generators/keystone/install_generator.rb
|
|
60
|
+
- lib/keystone_ui/configuration.rb
|
|
61
|
+
- app/helpers/keystone_ui_helper.rb
|
|
62
|
+
- app/components/keystone/ui/accordion_component.rb
|
|
63
|
+
- app/components/keystone/ui/alert_component.rb
|
|
64
|
+
- app/components/keystone/ui/badge_component.rb
|
|
65
|
+
- app/components/keystone/ui/bottom_nav_component.rb
|
|
66
|
+
- app/components/keystone/ui/bottom_nav_item_component.rb
|
|
67
|
+
- app/components/keystone/ui/button_component.rb
|
|
68
|
+
- app/components/keystone/ui/card_component.rb
|
|
69
|
+
- app/components/keystone/ui/card_link_component.rb
|
|
70
|
+
- app/components/keystone/ui/chart_card_component.rb
|
|
71
|
+
- app/components/keystone/ui/code_component.rb
|
|
72
|
+
- app/components/keystone/ui/color_picker_component.rb
|
|
73
|
+
- app/components/keystone/ui/column_picker_component.rb
|
|
74
|
+
- app/components/keystone/ui/column.rb
|
|
75
|
+
- app/components/keystone/ui/copy_button_component.rb
|
|
76
|
+
- app/components/keystone/ui/cta_banner_component.rb
|
|
77
|
+
- app/components/keystone/ui/data_table_component.rb
|
|
78
|
+
- app/components/keystone/ui/disclosure_component.rb
|
|
79
|
+
- app/components/keystone/ui/feature_grid_component.rb
|
|
80
|
+
- app/components/keystone/ui/file_upload_component.rb
|
|
81
|
+
- app/components/keystone/ui/form_component.rb
|
|
82
|
+
- app/components/keystone/ui/form_field_component.rb
|
|
83
|
+
- app/components/keystone/ui/form_page_component.rb
|
|
84
|
+
- app/components/keystone/ui/funnel_component.rb
|
|
85
|
+
- app/components/keystone/ui/grid_component.rb
|
|
86
|
+
- app/components/keystone/ui/hero_component.rb
|
|
87
|
+
- app/components/keystone/ui/input_component.rb
|
|
88
|
+
- app/components/keystone/ui/line_chart_component.rb
|
|
89
|
+
- app/components/keystone/ui/mobile_actions_component.rb
|
|
90
|
+
- app/components/keystone/ui/mobile_header_component.rb
|
|
91
|
+
- app/components/keystone/ui/modal_component.rb
|
|
92
|
+
- app/components/keystone/ui/multi_select_component.rb
|
|
93
|
+
- app/components/keystone/ui/nav_dropdown_component.rb
|
|
94
|
+
- app/components/keystone/ui/nav_item_component.rb
|
|
95
|
+
- app/components/keystone/ui/navbar_component.rb
|
|
96
|
+
- app/components/keystone/ui/option_card_component.rb
|
|
97
|
+
- app/components/keystone/ui/page_component.rb
|
|
98
|
+
- app/components/keystone/ui/page_header_component.rb
|
|
99
|
+
- app/components/keystone/ui/panel_component.rb
|
|
100
|
+
- app/components/keystone/ui/pipeline_component.rb
|
|
101
|
+
- app/components/keystone/ui/progress_component.rb
|
|
102
|
+
- app/components/keystone/ui/radio_card_component.rb
|
|
103
|
+
- app/components/keystone/ui/section_component.rb
|
|
104
|
+
- app/components/keystone/ui/select_component.rb
|
|
105
|
+
- app/components/keystone/ui/settings_link_component.rb
|
|
106
|
+
- app/components/keystone/ui/show_page_component.rb
|
|
107
|
+
- app/components/keystone/ui/stat_card_component.rb
|
|
108
|
+
- app/components/keystone/ui/swipe_deck_component.rb
|
|
109
|
+
- app/components/keystone/ui/tab_switcher_component.rb
|
|
110
|
+
- app/components/keystone/ui/textarea_component.rb
|