keystone_ui 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2d9cae890778efeef3333b64e2e04fa14955d60171b99d1f0fced2d64d0d8aa
4
- data.tar.gz: 45bb649fa4211a143e3e5c987332de6b2849423f52bcb04765120f49aff9ba87
3
+ metadata.gz: 0ebdae8cec6c4c1ac5a5d9e0ba2c2f81af46ac2bcd2d1537dce3ac039287a895
4
+ data.tar.gz: 3729bc3f9486e0adca2a21482bf493c832506d36c0e5bb9e98a50bba7a70e49a
5
5
  SHA512:
6
- metadata.gz: 193b52fdb0006bb35f2d58e50934755a3917b4f34348051a3d64a7b5b1eda768005c9dd74c7638ba585cd2fcc46f923730816f08a04f078d46e0132405cd310d
7
- data.tar.gz: d2aa6186b582a92554c1c58139351e2ebb67c8682908bbbb849d72d2fa117cb6e92f0124cb024e0d1a9ea1cc85d138eb1f4f9da08f1cb5bcbf81cd72ae32c6c0
6
+ metadata.gz: 1091cd7bb197a64a0ceb327906932e191e11e19bbcdc2559283cfde82573a7ad36adc4a6b1c2e430fef41eeb421642e6d3779ca1879a172d009098e98a4cfbba
7
+ data.tar.gz: 2d532adcf2a254df72de1e82383f4f373a520c03503adbf31c9c45b881c2a0f8296348bb0f581b3360e8b7ff505fd22431a41f7fef6042fc15681cbf97242833
@@ -0,0 +1,11 @@
1
+ <div class="<%= WRAPPER_CLASSES %>" data-controller="dropdown">
2
+ <button type="button"
3
+ data-action="click->dropdown#toggle click@window->dropdown#hide"
4
+ class="<%= BUTTON_CLASSES %>"
5
+ aria-label="Actions">
6
+ <%= ELLIPSIS_ICON.html_safe %>
7
+ </button>
8
+ <div data-dropdown-target="menu" class="<%= DROPDOWN_CLASSES %>">
9
+ <%= content %>
10
+ </div>
11
+ </div>
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class ActionMenuComponent < ViewComponent::Base
6
+ WRAPPER_CLASSES = "relative inline-block"
7
+ BUTTON_CLASSES = MobileActionsComponent::BUTTON_CLASSES
8
+ DROPDOWN_CLASSES = MobileActionsComponent::DROPDOWN_CLASSES
9
+ ELLIPSIS_ICON = MobileActionsComponent::ELLIPSIS_ICON
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keystone
4
+ module Ui
5
+ class ActionMenuItemComponent < ViewComponent::Base
6
+ ITEM_CLASSES = "ks-menu-option block w-full text-left"
7
+
8
+ def initialize(label:, href:, method: :get)
9
+ @label = label
10
+ @href = href
11
+ @method = method.to_sym
12
+ end
13
+
14
+ def call
15
+ return link_to(@label, @href, class: ITEM_CLASSES) if @method == :get
16
+
17
+ button_to(@href, method: @method, class: ITEM_CLASSES) { @label }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -43,7 +43,9 @@
43
43
  <% end %>
44
44
  <% if actions? %>
45
45
  <td class="<%= Keystone::Ui::DataTableComponent::ROW_CLASSES_LAST %>">
46
- <%= capture(item, &@actions_block) %>
46
+ <%= render Keystone::Ui::ActionMenuComponent.new do %>
47
+ <%= capture(item, &@actions_block) %>
48
+ <% end %>
47
49
  </td>
48
50
  <% end %>
49
51
  </tr>
@@ -12,6 +12,13 @@
12
12
  <%= @action[:label] %>
13
13
  </a>
14
14
  <% end %>
15
+ <% if @menu.any? %>
16
+ <%= render Keystone::Ui::ActionMenuComponent.new do %>
17
+ <% @menu.each do |item| %>
18
+ <%= render Keystone::Ui::ActionMenuItemComponent.new(**item) %>
19
+ <% end %>
20
+ <% end %>
21
+ <% end %>
15
22
  </div>
16
23
  <% end %>
17
24
  <%= content %>
@@ -9,10 +9,11 @@ module Keystone
9
9
  SUBTITLE_CLASSES = "ks-section-subtitle"
10
10
  ACTION_CLASSES = "ks-section-action"
11
11
 
12
- def initialize(title: nil, subtitle: nil, action: nil, spacing: :md, class: nil)
12
+ def initialize(title: nil, subtitle: nil, action: nil, menu: [], spacing: :md, class: nil)
13
13
  @title = title
14
14
  @subtitle = subtitle
15
15
  @action = action
16
+ @menu = menu
16
17
  @spacing = spacing
17
18
  @extra_classes = binding.local_variable_get(:class)
18
19
  end
@@ -125,6 +125,14 @@ module KeystoneUiHelper
125
125
  render Keystone::Ui::MobileHeaderComponent.new(**args)
126
126
  end
127
127
 
128
+ def ui_action_menu(**args, &block)
129
+ render Keystone::Ui::ActionMenuComponent.new(**args), &block
130
+ end
131
+
132
+ def ui_action_menu_item(**args)
133
+ render Keystone::Ui::ActionMenuItemComponent.new(**args)
134
+ end
135
+
128
136
  def ui_mobile_actions(**args, &block)
129
137
  render Keystone::Ui::MobileActionsComponent.new(**args), &block
130
138
  end
@@ -6,6 +6,8 @@ module Keystone
6
6
  # The safelist_spec verifies this list matches Keystone::Ui components.
7
7
  COMPONENTS = [
8
8
  Keystone::Ui::AccordionComponent,
9
+ Keystone::Ui::ActionMenuComponent,
10
+ Keystone::Ui::ActionMenuItemComponent,
9
11
  Keystone::Ui::TabSwitcherComponent,
10
12
  Keystone::Ui::StatCardComponent,
11
13
  Keystone::Ui::ChartCardComponent,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeystoneUi
4
- VERSION = "0.23.0"
4
+ VERSION = "0.24.0"
5
5
  end
@@ -1,6 +1,6 @@
1
1
  ---
2
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, a light/dark theme switch — MUST BE USED instead of hand-writing ERB and Tailwind for UI.
3
+ description: Use PROACTIVELY for building or editing screens in a Rails app that has Keystone UI — pages, forms, tables, navigation, action menus for a record's Edit and Delete, dashboards, charts, marketing sections, a light/dark theme switch — MUST BE USED instead of hand-writing ERB and Tailwind for UI.
4
4
  tools: Read, Write, Edit, Grep
5
5
  scope: UI — pages, forms, tables, navigation, dashboards
6
6
  ---
@@ -45,9 +45,13 @@ outer element. See Conventions before using it.
45
45
  `:lg` `:xl` to clear a fixed navbar. When `ui_form_page` or `ui_show_page` was
46
46
  called earlier on the same screen, `ui_page` renders their back link or
47
47
  breadcrumbs, and the form page's title, at its top, above the block.
48
- - `ui_section(title: nil, subtitle: nil, action: nil, spacing: :md, class: nil)`
48
+ - `ui_section(title: nil, subtitle: nil, action: nil, menu: [], spacing: :md, class: nil)`
49
49
  — takes a block. A titled block of content with an optional right-aligned
50
50
  link. `action:` is `{ label:, href: }`; `spacing:` `:sm` `:md` `:lg`.
51
+ `menu:` is `[{ label:, href:, method: }, ...]`, each hash taking the keywords
52
+ of `ui_action_menu_item`, and renders an action menu at the right of the
53
+ header. The header, and with it `action:` and `menu:`, renders only when
54
+ `title:` is given.
51
55
  - `ui_panel(padding: :md, radius: :lg, shadow: true)` — takes a block. A bordered
52
56
  card surface. `padding:` `:sm` `:md` `:lg`; `radius:` `:md` `:lg` `:xl`.
53
57
  - `ui_grid(cols: { default: 1 }, gap: :md, gap_x: nil, gap_y: nil)` — takes a
@@ -113,8 +117,18 @@ outer element. See Conventions before using it.
113
117
  - `ui_mobile_header(title:, back_url:, subtitle: nil)` — a back chevron plus
114
118
  centered title for mobile; hidden above `lg:`. Place it in the navbar's
115
119
  `mobile_left` slot.
120
+ - `ui_action_menu` — no keywords, takes a block. An ellipsis (⋯) button that
121
+ opens a dropdown of actions, shown at every screen size. Fill the block with
122
+ `ui_action_menu_item` calls.
123
+ - `ui_action_menu_item(label:, href:, method: :get)` — one entry in an action
124
+ menu. With `method: :get` it is a link to `href`. Any other method, such as
125
+ `:post`, `:patch` or `:delete`, renders a button inside its own small form
126
+ that sends that method to `href`, so never place such an item inside a
127
+ `ui_form` block, since a form cannot contain another form. It asks for no
128
+ confirmation before sending.
116
129
  - `ui_mobile_actions` — no keywords, takes a block. An ellipsis dropdown for
117
- mobile actions; the block holds the menu items. Hidden above `lg:`.
130
+ mobile actions; the block holds the menu items. Hidden above `lg:`. Use
131
+ `ui_action_menu` instead when the actions must also be reachable on desktop.
118
132
  - `ui_settings_link(label:, href:)` — a full-width settings row with a chevron.
119
133
  - `ui_theme_toggle` — no keywords, no block. A row of three buttons, Light, Dark
120
134
  and System, that switches the page's theme at once and remembers the choice in
@@ -180,7 +194,9 @@ outer element. See Conventions before using it.
180
194
  `columns:` accepts plain `{ key: "Label" }` hashes or `Keystone::Ui::Column`
181
195
  objects. In the block, `table.link(:column_key) { |item| url }` turns that
182
196
  column's cells into links and `table.actions { |item| ... }` appends a
183
- right-aligned actions column. Sorting requires all three of `sort:` (the
197
+ right-aligned actions column. Each row's actions render inside an action
198
+ menu, so the `actions` block holds `ui_action_menu_item` calls and nothing
199
+ else, never buttons or bare links. Sorting requires all three of `sort:` (the
184
200
  current column key), `sort_direction:` (`:asc`/`:desc`), and `sort_url:` (a
185
201
  lambda taking `(column_key, direction)` and returning a URL); headers then
186
202
  render as links that flip direction. `hidden_columns:` drops columns
@@ -362,6 +378,12 @@ outer element. See Conventions before using it.
362
378
  To show the arithmetic behind a figure, put `ui_calculation` under it rather
363
379
  than a hand-built list. Which lines and groups to show is the app's own
364
380
  calculation, so ask the developer which steps a reader needs to see.
381
+ Put the actions on a record, such as Edit and Delete, in an action menu
382
+ rather than in a row of buttons: `menu:` on the `ui_section` that shows the
383
+ record, `table.actions` for a table row, or `ui_action_menu` anywhere else.
384
+ An item that deletes or changes data sends without asking for
385
+ confirmation, so ask the developer whether that action needs a confirmation
386
+ step before adding it.
365
387
 
366
388
  6. For a table, decide how columns are declared. Use `{ key: "Label" }` hashes
367
389
  when every column is plain. Switch the whole set to `Keystone::Ui::Column`
@@ -421,7 +443,8 @@ outer element. See Conventions before using it.
421
443
  through it, so anything else emitted inside their block is discarded.
422
444
  - **Mobile is not an afterthought.** Several helpers render only on one side of
423
445
  the `lg:` (or `sm:`) breakpoint — page headers, mobile headers, mobile actions,
424
- bottom navigation, breadcrumbs. A screen needs both treatments; check the small viewport
446
+ bottom navigation, breadcrumbs. The action menu is the exception and shows at
447
+ every size. A screen needs both treatments; check the small viewport
425
448
  before calling it done.
426
449
  - Out of scope for this local: installing or upgrading the gem, changing the
427
450
  palette or theme defaults, and editing the components themselves. Building a
@@ -74,6 +74,13 @@ holds the catalog.
74
74
  that render one thing (a button, a badge, a field, a stat, a bucket) are
75
75
  configured entirely by keyword arguments. The navigation bar exposes named
76
76
  slots instead of a single block.
77
+ - **Action menus.** An action menu is an ellipsis button that opens a dropdown
78
+ of actions, shown at every screen size. The mobile action menu is the one
79
+ hidden from `lg:` up. Each action is a label, an address and an HTTP method.
80
+ A plain visit renders as a link. Any other method renders as a button that
81
+ sends that method, so an action that changes data is never a bare link. A
82
+ data table puts each row's actions in one action menu at the end of the row,
83
+ and a section can carry an action menu in its header beside its title.
77
84
  - **Options are symbols, and each component accepts its own set.** Appearance is
78
85
  chosen by name — `variant:`, `size:`, `type:`, `padding:`, `spacing:`,
79
86
  `max_width:`, `radius:` — on a size scale (`:sm` … `:xl`) or a short list of
@@ -100,7 +107,7 @@ holds the catalog.
100
107
  keystone_ui-styles 0.6.0 or later: corner radius, font, label weight, border
101
108
  width, padding and colours. Each colour variable has a `-dark` partner read
102
109
  on a dark page. Buttons, panels, cards, alerts, badges, form fields, the
103
- modal, the mobile action menu, the column picker, multi select, copy button,
110
+ modal, the action menu, the mobile action menu, the column picker, multi select, copy button,
104
111
  theme toggle, checkbox row, radio card, option card, file upload and colour
105
112
  picker read these variables. So do the data display components: stat card,
106
113
  chart card, card link, CTA banner, feature grid, hero, data table, code,
@@ -83,9 +83,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
83
83
  the Stimulus application is set up and add those two lines there. Without
84
84
  them, dropdowns, modals, file uploads, the column picker, the theme toggle
85
85
  and the other interactive components do nothing.
86
- `registerControllers(application)` also copies `data-theme` from each page
87
- Turbo renders onto the `<html>` tag. Without it, a Turbo visit keeps the
88
- theme of the first page loaded.
86
+ `registerControllers(application)` also copies `data-theme` and `data-look`
87
+ from each page Turbo renders onto the `<html>` tag. Without it, a Turbo
88
+ visit keeps the theme and look of the first page loaded.
89
89
  - `app/views/layouts/application.html.erb not found` — ask the developer which
90
90
  layout the app renders and add `<%= keystone_theme_attributes %>` to its
91
91
  `<html>` tag. Without it, a saved light or dark choice is not applied when
@@ -149,7 +149,7 @@ built on ViewComponent; hook it in before building any screen with those helpers
149
149
  ```
150
150
 
151
151
  Buttons, panels, cards, alerts, badges, form fields, the modal, the mobile
152
- action menu, the column picker, multi select, copy button, theme toggle,
152
+ action menu, the action menu, the column picker, multi select, copy button, theme toggle,
153
153
  checkbox row, radio card, option card, file upload and colour picker read
154
154
  these variables. So do the data display components: stat card, chart card,
155
155
  card link, CTA banner, feature grid, hero, data table, code, accordion,
@@ -36,6 +36,8 @@ develop:
36
36
  - ui_breadcrumbs
37
37
  - ui_mobile_header
38
38
  - ui_mobile_actions
39
+ - ui_action_menu
40
+ - ui_action_menu_item
39
41
  - ui_navbar
40
42
  - ui_nav_item
41
43
  - ui_nav_dropdown
@@ -88,6 +90,7 @@ sources:
88
90
  - app/components/keystone/ui/copy_button_component.rb
89
91
  - app/components/keystone/ui/cta_banner_component.rb
90
92
  - app/components/keystone/ui/data_table_component.rb
93
+ - app/components/keystone/ui/data_table_component.html.erb
91
94
  - app/components/keystone/ui/disclosure_component.rb
92
95
  - app/components/keystone/ui/calculation_component.rb
93
96
  - app/components/keystone/ui/feature_grid_component.rb
@@ -108,6 +111,8 @@ sources:
108
111
  - app/components/keystone/ui/input_component.rb
109
112
  - app/components/keystone/ui/line_chart_component.rb
110
113
  - app/components/keystone/ui/mobile_actions_component.rb
114
+ - app/components/keystone/ui/action_menu_component.rb
115
+ - app/components/keystone/ui/action_menu_item_component.rb
111
116
  - app/components/keystone/ui/mobile_header_component.rb
112
117
  - app/components/keystone/ui/modal_component.rb
113
118
  - app/components/keystone/ui/multi_select_component.rb
@@ -124,6 +129,7 @@ sources:
124
129
  - app/components/keystone/ui/progress_component.rb
125
130
  - app/components/keystone/ui/radio_card_component.rb
126
131
  - app/components/keystone/ui/section_component.rb
132
+ - app/components/keystone/ui/section_component.html.erb
127
133
  - app/components/keystone/ui/select_component.rb
128
134
  - app/components/keystone/ui/settings_link_component.rb
129
135
  - app/components/keystone/ui/show_page_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.23.0
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -73,6 +73,9 @@ files:
73
73
  - app/assets/tailwind/keystone_ui_engine/grid_safelist.css
74
74
  - app/components/keystone/ui/accordion_component.html.erb
75
75
  - app/components/keystone/ui/accordion_component.rb
76
+ - app/components/keystone/ui/action_menu_component.html.erb
77
+ - app/components/keystone/ui/action_menu_component.rb
78
+ - app/components/keystone/ui/action_menu_item_component.rb
76
79
  - app/components/keystone/ui/alert_component.html.erb
77
80
  - app/components/keystone/ui/alert_component.rb
78
81
  - app/components/keystone/ui/badge_component.html.erb