keystone_ui 0.16.0 → 0.18.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 +4 -4
- data/app/assets/javascripts/keystone_ui/file_upload_controller.js +2 -4
- data/app/components/keystone/ui/accordion_component.rb +9 -9
- data/app/components/keystone/ui/bottom_nav_component.rb +1 -1
- data/app/components/keystone/ui/bottom_nav_item_component.rb +2 -2
- data/app/components/keystone/ui/bucket_component.rb +9 -9
- data/app/components/keystone/ui/bucket_series_component.rb +1 -1
- data/app/components/keystone/ui/card_link_component.rb +4 -4
- data/app/components/keystone/ui/chart_card_component.rb +2 -2
- data/app/components/keystone/ui/checkbox_row_component.rb +4 -4
- data/app/components/keystone/ui/code_component.rb +3 -3
- data/app/components/keystone/ui/color_picker_component.html.erb +1 -1
- data/app/components/keystone/ui/color_picker_component.rb +4 -2
- data/app/components/keystone/ui/column_picker_component.rb +4 -4
- data/app/components/keystone/ui/copy_button_component.rb +1 -1
- data/app/components/keystone/ui/cta_banner_component.rb +7 -7
- data/app/components/keystone/ui/data_table_component.html.erb +4 -4
- data/app/components/keystone/ui/data_table_component.rb +14 -10
- data/app/components/keystone/ui/disclosure_component.rb +4 -4
- data/app/components/keystone/ui/feature_grid_component.rb +13 -13
- data/app/components/keystone/ui/file_upload_component.rb +10 -10
- data/app/components/keystone/ui/form_component.rb +1 -1
- data/app/components/keystone/ui/form_field_component.rb +2 -2
- data/app/components/keystone/ui/form_page_component.rb +2 -2
- data/app/components/keystone/ui/funnel_component.rb +12 -12
- data/app/components/keystone/ui/grid_component.rb +3 -3
- data/app/components/keystone/ui/hero_component.rb +11 -11
- data/app/components/keystone/ui/mobile_actions_component.rb +2 -2
- data/app/components/keystone/ui/mobile_header_component.rb +4 -4
- data/app/components/keystone/ui/modal_component.rb +5 -5
- data/app/components/keystone/ui/multi_select_component.rb +4 -4
- data/app/components/keystone/ui/nav_dropdown_component.rb +4 -4
- data/app/components/keystone/ui/nav_item_component.rb +1 -1
- data/app/components/keystone/ui/navbar_component.rb +1 -1
- data/app/components/keystone/ui/option_card_component.rb +2 -2
- data/app/components/keystone/ui/panel_component.rb +3 -3
- data/app/components/keystone/ui/pipeline_component.rb +14 -14
- data/app/components/keystone/ui/progress_component.rb +3 -3
- data/app/components/keystone/ui/radio_card_component.rb +4 -4
- data/app/components/keystone/ui/settings_link_component.html.erb +1 -1
- data/app/components/keystone/ui/settings_link_component.rb +3 -2
- data/app/components/keystone/ui/show_page_component.rb +2 -2
- data/app/components/keystone/ui/stat_card_component.html.erb +3 -3
- data/app/components/keystone/ui/stat_card_component.rb +17 -15
- data/app/components/keystone/ui/swipe_deck_component.html.erb +6 -6
- data/app/components/keystone/ui/swipe_deck_component.rb +7 -1
- data/app/components/keystone/ui/tab_switcher_component.rb +3 -3
- data/app/components/keystone/ui/theme_toggle_component.rb +2 -2
- data/app/helpers/keystone_ui_helper.rb +6 -1
- data/lib/keystone_ui/configuration.rb +16 -2
- data/lib/keystone_ui/look_choice.rb +19 -0
- data/lib/keystone_ui/source_css.rb +0 -1
- data/lib/keystone_ui/version.rb +1 -1
- data/lib/keystone_ui.rb +1 -0
- data/the_local/agents/keystone_ui-info.md +19 -0
- data/the_local/agents/keystone_ui-install.md +48 -4
- data/the_local/interface.yml +1 -0
- metadata +4 -4
- data/app/assets/tailwind/keystone_ui_engine/nav.css +0 -175
|
@@ -16,10 +16,10 @@ module Keystone
|
|
|
16
16
|
SVG
|
|
17
17
|
|
|
18
18
|
WRAPPER_CLASSES = "lg:hidden"
|
|
19
|
-
BACK_LINK_CLASSES = "
|
|
20
|
-
TITLE_CLASSES = "absolute left-1/2 -translate-x-1/2
|
|
21
|
-
SUBTITLE_CLASSES = "
|
|
22
|
-
DROPDOWN_CLASSES = "hidden absolute right-0 z-50
|
|
19
|
+
BACK_LINK_CLASSES = "ks-mobile-header-back"
|
|
20
|
+
TITLE_CLASSES = "ks-mobile-header-title absolute left-1/2 -translate-x-1/2 lg:hidden truncate max-w-[60%]"
|
|
21
|
+
SUBTITLE_CLASSES = "ks-mobile-header-subtitle block text-xs truncate text-center"
|
|
22
|
+
DROPDOWN_CLASSES = "ks-action-menu hidden absolute right-0 z-50 w-48 origin-top-right"
|
|
23
23
|
|
|
24
24
|
# Renders the left-side and title of the mobile header.
|
|
25
25
|
# Call from the left side of your navbar.
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class ModalComponent < ViewComponent::Base
|
|
6
|
-
BACKDROP_CLASSES = "hidden fixed inset-0 z-50 flex items-center justify-center
|
|
7
|
-
PANEL_CLASSES = "
|
|
8
|
-
HEADER_CLASSES = "flex items-center justify-between
|
|
9
|
-
TITLE_CLASSES = "
|
|
10
|
-
CLOSE_BUTTON_CLASSES = "
|
|
6
|
+
BACKDROP_CLASSES = "ks-modal-backdrop hidden fixed inset-0 z-50 flex items-center justify-center"
|
|
7
|
+
PANEL_CLASSES = "ks-modal-panel w-full max-h-[80vh] flex flex-col"
|
|
8
|
+
HEADER_CLASSES = "ks-modal-header flex items-center justify-between"
|
|
9
|
+
TITLE_CLASSES = "ks-modal-title text-lg"
|
|
10
|
+
CLOSE_BUTTON_CLASSES = "ks-modal-close"
|
|
11
11
|
BODY_CLASSES = "overflow-auto flex-1"
|
|
12
12
|
|
|
13
13
|
SIZE_CLASSES = {
|
|
@@ -4,10 +4,10 @@ module Keystone
|
|
|
4
4
|
module Ui
|
|
5
5
|
class MultiSelectComponent < ViewComponent::Base
|
|
6
6
|
WRAPPER_CLASSES = "relative inline-block"
|
|
7
|
-
TRIGGER_CLASSES = "inline-flex items-center
|
|
8
|
-
MENU_CLASSES = "absolute z-10
|
|
9
|
-
OPTION_CLASSES = "flex items-center
|
|
10
|
-
CHECKBOX_CLASSES = "
|
|
7
|
+
TRIGGER_CLASSES = "ks-menu-trigger inline-flex items-center text-sm"
|
|
8
|
+
MENU_CLASSES = "ks-menu absolute z-10 w-56 hidden"
|
|
9
|
+
OPTION_CLASSES = "ks-menu-option flex items-center text-sm cursor-pointer"
|
|
10
|
+
CHECKBOX_CLASSES = "ks-menu-checkbox"
|
|
11
11
|
CARET_ICON = <<~SVG.freeze
|
|
12
12
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4">
|
|
13
13
|
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class NavDropdownComponent < ViewComponent::Base
|
|
6
|
-
WRAPPER_CLASSES = "nav-dropdown"
|
|
7
|
-
MENU_CLASSES = "nav-dropdown-menu hidden"
|
|
8
|
-
TRIGGER_BASE = "nav-dropdown-trigger"
|
|
6
|
+
WRAPPER_CLASSES = "ks-nav-dropdown"
|
|
7
|
+
MENU_CLASSES = "ks-nav-dropdown-menu hidden"
|
|
8
|
+
TRIGGER_BASE = "ks-nav-dropdown-trigger"
|
|
9
9
|
ACTIVE_CLASS = "active"
|
|
10
10
|
CARET_ICON = <<~SVG.freeze
|
|
11
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="nav-dropdown-caret">
|
|
11
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="ks-nav-dropdown-caret">
|
|
12
12
|
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
|
13
13
|
</svg>
|
|
14
14
|
SVG
|
|
@@ -7,7 +7,7 @@ module Keystone
|
|
|
7
7
|
NAV_STICKY = "sticky top-0 z-40"
|
|
8
8
|
MOBILE_LEFT_CLASSES = "lg:hidden flex items-center"
|
|
9
9
|
LOGO_CLASSES = "logo"
|
|
10
|
-
MOBILE_CENTER_CLASSES = "absolute left-1/2 -translate-x-1/2
|
|
10
|
+
MOBILE_CENTER_CLASSES = "ks-navbar-title absolute left-1/2 -translate-x-1/2 lg:hidden truncate max-w-[60%]"
|
|
11
11
|
DESKTOP_LINKS_CLASSES = "nav-container hidden lg:flex"
|
|
12
12
|
MOBILE_RIGHT_CLASSES = "nav-user-controls ml-auto"
|
|
13
13
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class OptionCardComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "inline-flex items-center
|
|
6
|
+
BASE_CLASSES = "ks-option-card inline-flex items-center transition cursor-pointer"
|
|
7
7
|
|
|
8
8
|
attr_reader :name, :value, :input_data, :label_data
|
|
9
9
|
|
|
@@ -20,7 +20,7 @@ module Keystone
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def classes
|
|
23
|
-
border = selected? ? "
|
|
23
|
+
border = selected? ? "ks-option-card-selected" : "border-transparent"
|
|
24
24
|
"#{BASE_CLASSES} #{border}"
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class PanelComponent < ViewComponent::Base
|
|
6
|
-
PADDING_CLASSES = { sm: "
|
|
7
|
-
RADIUS_CLASSES = { md: "
|
|
6
|
+
PADDING_CLASSES = { sm: "ks-panel-padding-sm", md: "ks-panel-padding-md", lg: "ks-panel-padding-lg" }.freeze
|
|
7
|
+
RADIUS_CLASSES = { md: "ks-panel-radius-md", lg: "ks-panel-radius-lg", xl: "ks-panel-radius-xl" }.freeze
|
|
8
8
|
|
|
9
9
|
def initialize(padding: :md, radius: :lg, shadow: true)
|
|
10
10
|
@padding = padding
|
|
@@ -13,7 +13,7 @@ module Keystone
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
BASE_CLASSES = "ks-panel"
|
|
16
|
-
SHADOW_CLASS = "shadow
|
|
16
|
+
SHADOW_CLASS = "ks-panel-shadow"
|
|
17
17
|
|
|
18
18
|
def classes
|
|
19
19
|
tokens = [ RADIUS_CLASSES.fetch(@radius), BASE_CLASSES, PADDING_CLASSES.fetch(@padding) ]
|
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class PipelineComponent < ViewComponent::Base
|
|
6
|
-
CONTAINER_CLASSES = "
|
|
7
|
-
HEADER_CLASSES = "
|
|
8
|
-
TITLE_CLASSES = "
|
|
9
|
-
SUBTITLE_CLASSES = "
|
|
10
|
-
TRACK_CLASSES = "flex flex-col
|
|
11
|
-
BOX_CLASSES = "flex flex-1 flex-col items-center
|
|
12
|
-
BOX_LABEL_CLASSES = "text-xs uppercase tracking-wide
|
|
6
|
+
CONTAINER_CLASSES = "ks-pipeline"
|
|
7
|
+
HEADER_CLASSES = "ks-pipeline-header"
|
|
8
|
+
TITLE_CLASSES = "ks-pipeline-title text-lg"
|
|
9
|
+
SUBTITLE_CLASSES = "ks-pipeline-subtitle text-sm"
|
|
10
|
+
TRACK_CLASSES = "ks-pipeline-track flex flex-col sm:flex-row sm:items-stretch"
|
|
11
|
+
BOX_CLASSES = "ks-pipeline-box flex flex-1 flex-col items-center text-center"
|
|
12
|
+
BOX_LABEL_CLASSES = "ks-pipeline-box-label text-xs uppercase tracking-wide"
|
|
13
13
|
CONNECTOR_CLASSES = "flex items-center justify-center"
|
|
14
|
-
COUNT_BASE_CLASSES = "text-3xl
|
|
14
|
+
COUNT_BASE_CLASSES = "ks-pipeline-count text-3xl"
|
|
15
15
|
LINK_BASE_CLASSES = "link-toggle text-2xl leading-none"
|
|
16
|
-
LINK_HEALTHY_CLASSES = "
|
|
17
|
-
LINK_BROKEN_CLASSES = "
|
|
16
|
+
LINK_HEALTHY_CLASSES = "ks-pipeline-link-healthy"
|
|
17
|
+
LINK_BROKEN_CLASSES = "ks-pipeline-link-broken"
|
|
18
18
|
|
|
19
19
|
COUNT_CLASSES = {
|
|
20
|
-
amber: "
|
|
21
|
-
emerald: "
|
|
22
|
-
danger: "
|
|
23
|
-
muted: "
|
|
20
|
+
amber: "ks-pipeline-count-amber",
|
|
21
|
+
emerald: "ks-pipeline-count-emerald",
|
|
22
|
+
danger: "ks-pipeline-count-danger",
|
|
23
|
+
muted: "ks-pipeline-count-muted"
|
|
24
24
|
}.freeze
|
|
25
25
|
|
|
26
26
|
attr_reader :title, :boxes, :links, :subtitle
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class ProgressComponent < ViewComponent::Base
|
|
6
|
-
TRACK_CLASSES = "w-full h-2
|
|
7
|
-
BAR_CLASSES = "
|
|
8
|
-
LABEL_CLASSES = "
|
|
6
|
+
TRACK_CLASSES = "ks-progress-track w-full h-2 overflow-hidden"
|
|
7
|
+
BAR_CLASSES = "ks-progress-bar h-full transition-all"
|
|
8
|
+
LABEL_CLASSES = "ks-meter-label text-sm"
|
|
9
9
|
|
|
10
10
|
attr_reader :value, :max, :label
|
|
11
11
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class RadioCardComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "inline-flex flex-col
|
|
7
|
-
HIGHLIGHT_CLASSES = "
|
|
8
|
-
LABEL_CLASSES = "
|
|
9
|
-
HINT_CLASSES = "
|
|
6
|
+
BASE_CLASSES = "ks-radio-card inline-flex flex-col cursor-pointer transition"
|
|
7
|
+
HIGHLIGHT_CLASSES = "ks-radio-card-highlight"
|
|
8
|
+
LABEL_CLASSES = "ks-radio-card-label block"
|
|
9
|
+
HINT_CLASSES = "ks-radio-card-hint block text-sm"
|
|
10
10
|
|
|
11
11
|
attr_reader :name, :value, :label, :hint
|
|
12
12
|
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class SettingsLinkComponent < ViewComponent::Base
|
|
6
|
+
LABEL_CLASSES = "ks-settings-link-label"
|
|
6
7
|
attr_reader :label, :href
|
|
7
8
|
|
|
8
|
-
LINK_CLASSES = "flex items-center justify-between
|
|
9
|
+
LINK_CLASSES = "ks-settings-link flex items-center justify-between no-underline"
|
|
9
10
|
|
|
10
11
|
CHEVRON_ICON = <<~SVG.freeze
|
|
11
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5
|
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 ks-settings-link-chevron">
|
|
12
13
|
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
|
13
14
|
</svg>
|
|
14
15
|
SVG
|
|
@@ -4,8 +4,8 @@ module Keystone
|
|
|
4
4
|
module Ui
|
|
5
5
|
class ShowPageComponent < ViewComponent::Base
|
|
6
6
|
DESKTOP_WRAPPER_CLASSES = "hidden md:block"
|
|
7
|
-
TITLE_CLASSES = "
|
|
8
|
-
SUBTITLE_CLASSES = "
|
|
7
|
+
TITLE_CLASSES = "ks-page-title text-2xl"
|
|
8
|
+
SUBTITLE_CLASSES = "ks-page-header-subtitle text-sm"
|
|
9
9
|
|
|
10
10
|
def initialize(title:, back_url:, subtitle: nil)
|
|
11
11
|
@title = title
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<div class="<%= classes %>"<%= info? ? ' data-controller="stat-card-info"'.html_safe : "" %>>
|
|
2
|
-
<div class="
|
|
2
|
+
<div class="<%= HEADER_CLASSES %>">
|
|
3
3
|
<p class="<%= label_classes %>"><%= label %></p>
|
|
4
4
|
<% if info? %>
|
|
5
5
|
<button type="button" class="<%= info_button_classes %>" data-action="click->stat-card-info#toggle" aria-label="Show metric details">
|
|
6
6
|
<%= info_icon.html_safe %>
|
|
7
7
|
</button>
|
|
8
8
|
<div class="<%= disclosure_classes %>" data-stat-card-info-target="panel">
|
|
9
|
-
<% if definition %><p><span class="
|
|
10
|
-
<% if calculation %><p><span class="
|
|
9
|
+
<% if definition %><p><span class="<%= EMPHASIS_CLASSES %>">Captures:</span> <%= definition %></p><% end %>
|
|
10
|
+
<% if calculation %><p><span class="<%= EMPHASIS_CLASSES %>">Calc:</span> <%= calculation %></p><% end %>
|
|
11
11
|
</div>
|
|
12
12
|
<% end %>
|
|
13
13
|
</div>
|
|
@@ -3,25 +3,27 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class StatCardComponent < ViewComponent::Base
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
EMPHASIS_CLASSES = "ks-stat-card-emphasis"
|
|
7
|
+
HEADER_CLASSES = "ks-stat-card-header flex items-start justify-between"
|
|
8
|
+
CARD_CLASSES = "ks-metric-card relative"
|
|
9
|
+
LABEL_CLASSES = "ks-stat-card-label text-sm"
|
|
10
|
+
VALUE_BASE_CLASSES = "ks-stat-card-value text-3xl"
|
|
11
|
+
SUFFIX_CLASSES = "ks-stat-card-suffix text-lg"
|
|
12
|
+
DISCLOSURE_CLASSES = "ks-stat-card-disclosure hidden peer-hover:block peer-focus-visible:block absolute inset-x-0 top-full z-10 text-sm"
|
|
13
|
+
CHANGE_ROW_CLASSES = "ks-stat-card-change h-5 text-sm"
|
|
12
14
|
VALUE_LINK_CLASSES = "hover:underline focus:outline-none focus-visible:underline"
|
|
13
|
-
INFO_BUTTON_CLASSES = "peer shrink-0
|
|
15
|
+
INFO_BUTTON_CLASSES = "ks-stat-card-info peer shrink-0 transition"
|
|
14
16
|
|
|
15
17
|
INFO_ICON = <<~SVG.freeze
|
|
16
18
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
17
19
|
SVG
|
|
18
20
|
|
|
19
21
|
VARIANT_CLASSES = {
|
|
20
|
-
neutral: "
|
|
21
|
-
success: "
|
|
22
|
-
danger: "
|
|
23
|
-
warning: "
|
|
24
|
-
info: "
|
|
22
|
+
neutral: "ks-tone-neutral",
|
|
23
|
+
success: "ks-tone-success",
|
|
24
|
+
danger: "ks-tone-danger",
|
|
25
|
+
warning: "ks-tone-warning",
|
|
26
|
+
info: "ks-tone-info"
|
|
25
27
|
}.freeze
|
|
26
28
|
|
|
27
29
|
attr_reader :label, :value, :suffix, :definition, :calculation, :change, :href
|
|
@@ -87,10 +89,10 @@ module Keystone
|
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
def change_classes
|
|
90
|
-
return "
|
|
91
|
-
return "
|
|
92
|
+
return "ks-tone-muted" if !change? || @change.zero?
|
|
93
|
+
return "ks-tone-danger" if @change.negative?
|
|
92
94
|
|
|
93
|
-
"
|
|
95
|
+
"ks-tone-success"
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
def disclosure_classes
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
data-swipe-deck-empty-title-value="<%= @empty_title %>"
|
|
3
3
|
data-swipe-deck-empty-subtitle-value="<%= @empty_subtitle %>">
|
|
4
4
|
<% if empty? %>
|
|
5
|
-
<div class="
|
|
6
|
-
<p class="
|
|
5
|
+
<div class="<%= EMPTY_CLASSES %>" data-testid="swipe-empty">
|
|
6
|
+
<p class="<%= EMPTY_TITLE_CLASSES %>"><%= @empty_title %></p>
|
|
7
7
|
<% if @empty_subtitle %>
|
|
8
|
-
<p class="
|
|
8
|
+
<p class="<%= EMPTY_MESSAGE_CLASSES %>"><%= @empty_subtitle %></p>
|
|
9
9
|
<% end %>
|
|
10
10
|
</div>
|
|
11
11
|
<% else %>
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
<% end %>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="<%= ACTIONS_CLASSES %>">
|
|
25
25
|
<button data-action="swipe-deck#skip" data-testid="skip-button"
|
|
26
|
-
class="
|
|
26
|
+
class="<%= REJECT_BUTTON_CLASSES %>">
|
|
27
27
|
<svg class="size-6" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
28
28
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
|
29
29
|
</svg>
|
|
30
30
|
</button>
|
|
31
31
|
<button data-action="swipe-deck#complete" data-testid="complete-button"
|
|
32
|
-
class="
|
|
32
|
+
class="<%= ACCEPT_BUTTON_CLASSES %>">
|
|
33
33
|
<svg class="size-6" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
34
34
|
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
|
35
35
|
</svg>
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class SwipeDeckComponent < ViewComponent::Base
|
|
6
|
-
|
|
6
|
+
ACCEPT_BUTTON_CLASSES = "ks-swipe-button ks-swipe-button-accept flex items-center justify-center size-14 transition-colors"
|
|
7
|
+
REJECT_BUTTON_CLASSES = "ks-swipe-button ks-swipe-button-reject flex items-center justify-center size-14 transition-colors"
|
|
8
|
+
ACTIONS_CLASSES = "ks-swipe-actions flex justify-center"
|
|
9
|
+
EMPTY_MESSAGE_CLASSES = "ks-swipe-empty-message"
|
|
10
|
+
EMPTY_TITLE_CLASSES = "ks-swipe-empty-title text-2xl"
|
|
11
|
+
EMPTY_CLASSES = "ks-swipe-empty text-center"
|
|
12
|
+
CARD_CLASSES = "ks-swipe-card absolute inset-0 flex flex-col items-center justify-center transition-transform duration-300"
|
|
7
13
|
|
|
8
14
|
STACK_SCALE_STEP = 0.05
|
|
9
15
|
STACK_TRANSLATE_STEP = 8
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class TabSwitcherComponent < ViewComponent::Base
|
|
6
|
-
TAB_BAR_CLASSES = "
|
|
7
|
-
TAB_BASE_CLASSES = "
|
|
6
|
+
TAB_BAR_CLASSES = "ks-tab-bar flex flex-wrap justify-center"
|
|
7
|
+
TAB_BASE_CLASSES = "ks-tab text-sm transition"
|
|
8
8
|
PANEL_CLASSES = "hidden"
|
|
9
9
|
|
|
10
10
|
attr_reader :tabs
|
|
@@ -18,7 +18,7 @@ module Keystone
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def tab_classes
|
|
21
|
-
"#{TAB_BASE_CLASSES}
|
|
21
|
+
"#{TAB_BASE_CLASSES} ks-tab-active"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def panel_classes
|
|
@@ -4,8 +4,8 @@ module Keystone
|
|
|
4
4
|
module Ui
|
|
5
5
|
class ThemeToggleComponent < ViewComponent::Base
|
|
6
6
|
OPTIONS = [ [ "Light", "light" ], [ "Dark", "dark" ], [ "System", "system" ] ].freeze
|
|
7
|
-
GROUP_CLASSES = "inline-flex
|
|
8
|
-
OPTION_CLASSES = "ks-button ks-button-sm ks-button-secondary
|
|
7
|
+
GROUP_CLASSES = "ks-theme-toggle inline-flex"
|
|
8
|
+
OPTION_CLASSES = "ks-theme-toggle-option ks-button ks-button-sm ks-button-secondary"
|
|
9
9
|
|
|
10
10
|
def initialize(current: nil)
|
|
11
11
|
@current = current
|
|
@@ -46,7 +46,7 @@ module KeystoneUiHelper
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def keystone_theme_attributes
|
|
49
|
-
keystone_theme_choice.
|
|
49
|
+
keystone_theme_choice.html_attributes.merge(keystone_look_choice.html_attributes).map { |name, value| %(#{name}="#{value}") }.join(" ").html_safe
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def ui_copy_button(**args)
|
|
@@ -218,4 +218,9 @@ module KeystoneUiHelper
|
|
|
218
218
|
def keystone_theme_choice
|
|
219
219
|
KeystoneUi::ThemeChoice.new(cookies[KeystoneUi::ThemeChoice::COOKIE], supplied: KeystoneUi.configuration.supplied_theme_mode(self))
|
|
220
220
|
end
|
|
221
|
+
|
|
222
|
+
def keystone_look_choice
|
|
223
|
+
config = KeystoneUi.configuration
|
|
224
|
+
KeystoneUi::LookChoice.new(looks: config.looks.keys, default: config.default_look, supplied: config.supplied_look(self))
|
|
225
|
+
end
|
|
221
226
|
end
|
|
@@ -2,14 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
module KeystoneUi
|
|
4
4
|
class Configuration
|
|
5
|
-
attr_accessor :accent, :surface, :theme_mode_supplier
|
|
6
|
-
attr_reader :tailwind_imports, :tailwind_sources
|
|
5
|
+
attr_accessor :accent, :surface, :theme_mode_supplier, :look_supplier
|
|
6
|
+
attr_reader :tailwind_imports, :tailwind_sources, :looks, :default_look
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@accent = :blue
|
|
10
10
|
@surface = :zinc
|
|
11
11
|
@tailwind_imports = []
|
|
12
12
|
@tailwind_sources = []
|
|
13
|
+
@looks = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def default_look=(name)
|
|
17
|
+
@default_look = name&.to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def register_look(name, path)
|
|
21
|
+
@looks[name.to_s] = path.to_s
|
|
22
|
+
@tailwind_imports << path.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def supplied_look(view)
|
|
26
|
+
look_supplier&.call(view)&.to_s
|
|
13
27
|
end
|
|
14
28
|
|
|
15
29
|
def supplied_theme_mode(view)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
class LookChoice
|
|
5
|
+
def initialize(looks:, default:, supplied: nil)
|
|
6
|
+
@looks = looks
|
|
7
|
+
@default = default
|
|
8
|
+
@supplied = supplied
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def name
|
|
12
|
+
[ @supplied, @default ].find { |candidate| @looks.include?(candidate) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def html_attributes
|
|
16
|
+
name ? { "data-look" => name } : {}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -21,7 +21,6 @@ module KeystoneUi
|
|
|
21
21
|
[
|
|
22
22
|
%(@import "#{KeystoneUi::Styles.tailwind_file}";),
|
|
23
23
|
%(@source "#{@root}/app/components/**/*.{erb,rb}";),
|
|
24
|
-
%(@import "#{@root}/app/assets/tailwind/keystone_ui_engine/nav.css";),
|
|
25
24
|
%(@import "#{@root}/app/assets/tailwind/keystone_ui_engine/color_picker.css";),
|
|
26
25
|
%(@import "#{@root}/app/assets/tailwind/keystone_ui_engine/grid_safelist.css";),
|
|
27
26
|
*@imports.map { |import| %(@import "#{import}";) },
|
data/lib/keystone_ui/version.rb
CHANGED
data/lib/keystone_ui.rb
CHANGED
|
@@ -74,6 +74,25 @@ holds the catalog.
|
|
|
74
74
|
Tailwind's stock `gray-*` and `zinc-*` neutrals, or fixed status colors such
|
|
75
75
|
as green and amber, directly. Changing the defaults belongs to the install
|
|
76
76
|
local.
|
|
77
|
+
- **Looks.** A look is one CSS file that sets the `--ks-` variables from
|
|
78
|
+
keystone_ui-styles 0.6.0 or later: corner radius, font, label weight, border
|
|
79
|
+
width, padding and colours. Each colour variable has a `-dark` partner read
|
|
80
|
+
on a dark page. Buttons, panels, cards, alerts, badges, form fields, the
|
|
81
|
+
modal, the mobile action menu, the column picker, multi select, copy button,
|
|
82
|
+
theme toggle, checkbox row, radio card, option card, file upload and colour
|
|
83
|
+
picker read these variables. So do the data display components: stat card,
|
|
84
|
+
chart card, card link, CTA banner, feature grid, hero, data table, code,
|
|
85
|
+
accordion, disclosure, tab switcher, progress, funnel, bucket, pipeline and
|
|
86
|
+
swipe deck. So do the navigation components: navbar, nav item, nav dropdown,
|
|
87
|
+
bottom nav, mobile header and settings link. A host imports its look after `keystone_source.css`, and a gem ships one through
|
|
88
|
+
`tailwind_imports`, both set up through the install local.
|
|
89
|
+
- **Registered looks.** Looks can also be registered by name, and each page
|
|
90
|
+
gets one: the name another gem supplies for the request, then the configured
|
|
91
|
+
default, each used only when registered. The layout's `<html>` tag carries
|
|
92
|
+
`data-look="<name>"`, and a registered look file scopes its variables to
|
|
93
|
+
`:root[data-look="<name>"]`. A host with no registered looks gets no
|
|
94
|
+
`data-look`. Registering and choosing looks is set up through the install
|
|
95
|
+
local.
|
|
77
96
|
- **Light, dark, system and custom themes.** Every component has dark-mode
|
|
78
97
|
styling. The theme is chosen in this order: the user's choice stored in a
|
|
79
98
|
cookie, then a mode another gem supplies, then light. System leaves the page
|
|
@@ -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 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 and extra Tailwind imports and sources.
|
|
4
4
|
tools: Bash, Read, Edit
|
|
5
5
|
scope: UI — pages, forms, tables, navigation, dashboards
|
|
6
6
|
---
|
|
@@ -22,18 +22,23 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
22
22
|
`surface` (palette names, `:blue` and `:zinc` unless set),
|
|
23
23
|
`theme_mode_supplier` (a callable that supplies a light, dark, system or
|
|
24
24
|
custom mode),
|
|
25
|
+
`register_look` (a look's CSS file by name), `default_look` and
|
|
26
|
+
`look_supplier` (which registered look a page gets),
|
|
25
27
|
and the `tailwind_imports` and `tailwind_sources` lists (extra CSS files and
|
|
26
28
|
scan paths added to the Tailwind build).
|
|
27
29
|
- `keystone_theme_attributes` — a layout helper placed inside the `<html>` tag.
|
|
28
30
|
It writes `data-theme="light"`, `data-theme="dark"` or `data-theme="custom"`
|
|
29
31
|
for the page's mode, and writes nothing for system so the page follows the
|
|
30
32
|
operating system. The mode is the `keystone_theme` cookie's choice, then the
|
|
31
|
-
configured `theme_mode_supplier`, then light.
|
|
33
|
+
configured `theme_mode_supplier`, then light. It also writes
|
|
34
|
+
`data-look="<name>"` for the page's look: the `look_supplier`'s name, then
|
|
35
|
+
`default_look`, each used only when registered. With no look registered it
|
|
36
|
+
writes no `data-look`.
|
|
32
37
|
|
|
33
38
|
## How to use it
|
|
34
39
|
|
|
35
40
|
1. Confirm the prerequisites: Ruby >= 3.2 and **tailwindcss-rails v4+** in the
|
|
36
|
-
host app. The gem brings ViewComponent and keystone_ui-styles with it.
|
|
41
|
+
host app. The gem brings ViewComponent and keystone_ui-styles 0.6.0 or later with it.
|
|
37
42
|
Tailwind does not have to be initialized first, because the generator creates
|
|
38
43
|
the stylesheet if it is missing.
|
|
39
44
|
|
|
@@ -128,6 +133,32 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
128
133
|
or per-user colors generated at runtime by a companion theming gem. Ask which
|
|
129
134
|
the app wants before wiring either.
|
|
130
135
|
|
|
136
|
+
To change how components look beyond the palette, write a look file: one CSS
|
|
137
|
+
file holding a `:root` rule, outside any `@layer` block, that sets the
|
|
138
|
+
`--ks-` variables from keystone_ui-styles for corner radius, font, label
|
|
139
|
+
weight, border width, padding and colours. Set each colour's `-dark`
|
|
140
|
+
partner too, or dark pages keep the default colour. Import it after
|
|
141
|
+
Keystone:
|
|
142
|
+
|
|
143
|
+
```css
|
|
144
|
+
@import "tailwindcss";
|
|
145
|
+
@import "./keystone_source.css";
|
|
146
|
+
@import "./look.css";
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Buttons, panels, cards, alerts, badges, form fields, the modal, the mobile
|
|
150
|
+
action menu, the column picker, multi select, copy button, theme toggle,
|
|
151
|
+
checkbox row, radio card, option card, file upload and colour picker read
|
|
152
|
+
these variables. So do the data display components: stat card, chart card,
|
|
153
|
+
card link, CTA banner, feature grid, hero, data table, code, accordion,
|
|
154
|
+
disclosure, tab switcher, progress, funnel, bucket, pipeline and swipe deck.
|
|
155
|
+
So do the navigation components: navbar, nav item, nav dropdown, bottom nav,
|
|
156
|
+
mobile header and settings link. The keystone_ui-styles README lists every
|
|
157
|
+
variable and its default.
|
|
158
|
+
|
|
159
|
+
To offer several looks and choose one per page, register them by name in
|
|
160
|
+
step 7 rather than importing them here.
|
|
161
|
+
|
|
131
162
|
7. Write `config/initializers/keystone_ui.rb` only if one of the settings below
|
|
132
163
|
is wanted. Ask the developer about each rather than adding any by default.
|
|
133
164
|
The engine reads the configuration after initializers have run, so this file
|
|
@@ -140,6 +171,9 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
140
171
|
config.accent = :emerald
|
|
141
172
|
config.surface = :slate
|
|
142
173
|
config.theme_mode_supplier = ->(view) { view.current_user&.theme }
|
|
174
|
+
config.register_look :compact, "/absolute/path/to/compact.css"
|
|
175
|
+
config.default_look = :compact
|
|
176
|
+
config.look_supplier = ->(view) { view.current_user&.look }
|
|
143
177
|
config.tailwind_imports << "/absolute/path/to/extra.css"
|
|
144
178
|
config.tailwind_sources << "/absolute/path/to/components/**/*.{erb,rb}"
|
|
145
179
|
end
|
|
@@ -157,11 +191,21 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
157
191
|
`data-theme="custom"`. The theme toggle does not offer custom, so only a
|
|
158
192
|
supplier sets it. Ask the developer which gem or code supplies custom and
|
|
159
193
|
its colors before returning it.
|
|
194
|
+
- `register_look :name, path` — registers a look file by name and appends it
|
|
195
|
+
to `tailwind_imports`, so `keystone_source.css` imports it. A look offered
|
|
196
|
+
this way scopes its variables to `:root[data-look="<name>"]` instead of
|
|
197
|
+
`:root`, so several can be imported at once.
|
|
198
|
+
- `default_look = :name` — the look a page gets when nothing else chooses one.
|
|
199
|
+
- `look_supplier` — a callable that receives the view and returns a look
|
|
200
|
+
name for the request. A name that is not registered, or `nil`, leaves the
|
|
201
|
+
page on the default look.
|
|
160
202
|
- `tailwind_imports` and `tailwind_sources` — lists to append to, never
|
|
161
203
|
assign. Each import becomes an `@import` line and each source becomes an
|
|
162
204
|
`@source` line in `keystone_source.css` on the next boot. They are for
|
|
163
205
|
another gem or engine whose CSS or templates must be in the same Tailwind
|
|
164
|
-
build, and that gem normally appends its own entries.
|
|
206
|
+
build, and that gem normally appends its own entries. A gem that ships a
|
|
207
|
+
look appends its look file to `tailwind_imports` from its own initializer,
|
|
208
|
+
and `keystone_source.css` imports it after keystone_ui-styles.
|
|
165
209
|
|
|
166
210
|
## Conventions
|
|
167
211
|
|