keystone_ui 0.7.0 → 0.9.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/index.js +2 -0
- data/app/assets/javascripts/keystone_ui/theme_toggle_controller.js +25 -0
- data/app/components/keystone/ui/button_component.rb +7 -7
- data/app/components/keystone/ui/checkbox_row_component.html.erb +9 -0
- data/app/components/keystone/ui/checkbox_row_component.rb +46 -0
- data/app/components/keystone/ui/form_field_component.html.erb +1 -1
- data/app/components/keystone/ui/form_field_component.rb +5 -5
- data/app/components/keystone/ui/input_component.rb +3 -5
- data/app/components/keystone/ui/panel_component.rb +1 -1
- data/app/components/keystone/ui/select_component.rb +3 -5
- data/app/components/keystone/ui/textarea_component.rb +3 -5
- data/app/components/keystone/ui/theme_toggle_component.html.erb +10 -0
- data/app/components/keystone/ui/theme_toggle_component.rb +23 -0
- data/app/helpers/keystone_ui_helper.rb +12 -0
- data/config/importmap.rb +1 -0
- data/lib/generators/keystone/install_generator.rb +25 -0
- data/lib/generators/keystone/layout_theme_attributes.rb +17 -0
- data/lib/keystone_ui/engine.rb +3 -15
- data/lib/keystone_ui/safelist.rb +3 -1
- data/lib/keystone_ui/source_css.rb +31 -0
- data/lib/keystone_ui/theme_choice.rb +20 -0
- data/lib/keystone_ui/version.rb +1 -1
- data/lib/keystone_ui.rb +2 -0
- data/the_local/agents/keystone_ui-develop.md +5 -0
- data/the_local/interface.yml +3 -0
- metadata +24 -3
- data/app/assets/tailwind/keystone_ui_engine/theme.css +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01e2ba0387ee8dd44d772be3546d44dada399d754099e486b607a73056c85606
|
|
4
|
+
data.tar.gz: ea5acc7537974e4bd59f7415e267b0038148fecd0890dc0b91a86f1af9a3801d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f7b432026de7ac8ededb44facb8488df5ded58f185b91b9981654c978d7a32ce567b43b5fe38359c036a1457092a633ca54e2b5a873654250aac36b99bd1a08
|
|
7
|
+
data.tar.gz: fb986e2fde289d1c0b969096a04a02be8664544fbe7046d93c358b8d054440da8eed66824e23a23644b02efb76e0d25c95647bf0b32d06582dd0a5b4a0fd5bb1
|
|
@@ -12,6 +12,7 @@ import AccordionController from "keystone_ui/accordion_controller"
|
|
|
12
12
|
import StatCardInfoController from "keystone_ui/stat_card_info_controller"
|
|
13
13
|
import AutoSubmitController from "keystone_ui/auto_submit_controller"
|
|
14
14
|
import LineChartController from "keystone_ui/line_chart_controller"
|
|
15
|
+
import ThemeToggleController from "keystone_ui/theme_toggle_controller"
|
|
15
16
|
|
|
16
17
|
export function registerControllers(application) {
|
|
17
18
|
application.register("color-picker", ColorPickerController)
|
|
@@ -28,6 +29,7 @@ export function registerControllers(application) {
|
|
|
28
29
|
application.register("stat-card-info", StatCardInfoController)
|
|
29
30
|
application.register("auto-submit", AutoSubmitController)
|
|
30
31
|
application.register("line-chart", LineChartController)
|
|
32
|
+
application.register("theme-toggle", ThemeToggleController)
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export { ColorPickerController, MultiSelectController, SwipeDeckController, ColumnPickerController, FileUploadController, DropdownController, DismissController, ModalController, ClipboardController, TabSwitcherController, AccordionController, StatCardInfoController, AutoSubmitController, LineChartController }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
const COOKIE = "keystone_theme"
|
|
4
|
+
const ONE_YEAR = 60 * 60 * 24 * 365
|
|
5
|
+
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
choose({ params: { mode } }) {
|
|
8
|
+
const page = this.element.ownerDocument
|
|
9
|
+
this.#showPressed(mode)
|
|
10
|
+
|
|
11
|
+
if (mode === "system") {
|
|
12
|
+
delete page.documentElement.dataset.theme
|
|
13
|
+
page.cookie = `${COOKIE}=; path=/; max-age=0; samesite=lax`
|
|
14
|
+
} else {
|
|
15
|
+
page.documentElement.dataset.theme = mode
|
|
16
|
+
page.cookie = `${COOKIE}=${mode}; path=/; max-age=${ONE_YEAR}; samesite=lax`
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#showPressed(mode) {
|
|
21
|
+
this.element.querySelectorAll("[data-theme-toggle-mode-param]").forEach((option) => {
|
|
22
|
+
option.setAttribute("aria-pressed", String(option.dataset.themeToggleModeParam === mode))
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class ButtonComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "
|
|
6
|
+
BASE_CLASSES = "ks-button"
|
|
7
7
|
|
|
8
8
|
VARIANT_CLASSES = {
|
|
9
|
-
secondary: "
|
|
10
|
-
danger: "
|
|
9
|
+
secondary: "ks-button-secondary",
|
|
10
|
+
danger: "ks-button-danger"
|
|
11
11
|
}.freeze
|
|
12
12
|
|
|
13
13
|
SIZE_CLASSES = {
|
|
14
|
-
sm: "
|
|
15
|
-
md: "
|
|
16
|
-
lg: "
|
|
14
|
+
sm: "ks-button-sm",
|
|
15
|
+
md: "ks-button-md",
|
|
16
|
+
lg: "ks-button-lg"
|
|
17
17
|
}.freeze
|
|
18
18
|
|
|
19
19
|
def initialize(label:, href: nil, variant: :primary, size: :md, type: :submit, data: nil)
|
|
@@ -27,7 +27,7 @@ module Keystone
|
|
|
27
27
|
|
|
28
28
|
def classes
|
|
29
29
|
variant_css = if @variant == :primary
|
|
30
|
-
"
|
|
30
|
+
"ks-button-primary"
|
|
31
31
|
else
|
|
32
32
|
VARIANT_CLASSES.fetch(@variant)
|
|
33
33
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<label class="<%= row_classes %>">
|
|
2
|
+
<input type="checkbox" name="<%= name %>" value="<%= value %>" <%= "checked" if checked? %> class="<%= input_classes %>">
|
|
3
|
+
<span>
|
|
4
|
+
<span class="<%= label_classes %>"><%= label %></span>
|
|
5
|
+
<% if hint? %>
|
|
6
|
+
<span class="<%= hint_classes %>"><%= hint %></span>
|
|
7
|
+
<% end %>
|
|
8
|
+
</span>
|
|
9
|
+
</label>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
module Ui
|
|
5
|
+
class CheckboxRowComponent < ViewComponent::Base
|
|
6
|
+
ROW_CLASSES = "flex items-start gap-3 py-3 cursor-pointer"
|
|
7
|
+
INPUT_CLASSES = "mt-0.5 size-4 shrink-0 rounded border-surface-300 text-accent-600 focus:ring-accent-500"
|
|
8
|
+
LABEL_CLASSES = "block text-sm font-medium text-surface-900"
|
|
9
|
+
HINT_CLASSES = "block mt-0.5 text-sm text-surface-500"
|
|
10
|
+
|
|
11
|
+
attr_reader :name, :value, :label, :hint
|
|
12
|
+
|
|
13
|
+
def initialize(name:, value:, label:, hint: nil, checked: false)
|
|
14
|
+
@name = name
|
|
15
|
+
@value = value
|
|
16
|
+
@label = label
|
|
17
|
+
@hint = hint
|
|
18
|
+
@checked = checked
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def checked?
|
|
22
|
+
@checked
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def hint?
|
|
26
|
+
!@hint.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def row_classes
|
|
30
|
+
ROW_CLASSES
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def input_classes
|
|
34
|
+
INPUT_CLASSES
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def label_classes
|
|
38
|
+
LABEL_CLASSES
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def hint_classes
|
|
42
|
+
HINT_CLASSES
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<% elsif textarea? %>
|
|
15
15
|
<textarea name="<%= @attribute %>" rows="3" class="<%= Keystone::Ui::TextareaComponent::BASE_CLASSES %>"<% if @placeholder %> placeholder="<%= @placeholder %>"<% end %>><%= @value %></textarea>
|
|
16
16
|
<% elsif select? %>
|
|
17
|
-
<select name="<%= @attribute %>" class="<%= Keystone::Ui::SelectComponent::BASE_CLASSES %>
|
|
17
|
+
<select name="<%= @attribute %>" class="<%= Keystone::Ui::SelectComponent::BASE_CLASSES %>">
|
|
18
18
|
<% unless required? %><option value=""></option><% end %>
|
|
19
19
|
<% select_options.each do |label, value| %>
|
|
20
20
|
<option value="<%= value %>" <%= "selected" if @value.to_s == value.to_s %>><%= label %></option>
|
|
@@ -4,11 +4,11 @@ module Keystone
|
|
|
4
4
|
module Ui
|
|
5
5
|
class FormFieldComponent < ViewComponent::Base
|
|
6
6
|
WRAPPER_CLASSES = "space-y-1"
|
|
7
|
-
LABEL_CLASSES = "
|
|
8
|
-
REQUIRED_CLASSES = "
|
|
9
|
-
HINT_CLASSES = "
|
|
10
|
-
ERROR_CLASSES = "
|
|
11
|
-
CHECKBOX_CLASSES = "
|
|
7
|
+
LABEL_CLASSES = "ks-label"
|
|
8
|
+
REQUIRED_CLASSES = "ks-required"
|
|
9
|
+
HINT_CLASSES = "ks-hint"
|
|
10
|
+
ERROR_CLASSES = "ks-error"
|
|
11
|
+
CHECKBOX_CLASSES = "ks-checkbox"
|
|
12
12
|
CHECKBOX_WRAPPER_CLASSES = "flex items-center gap-2"
|
|
13
13
|
|
|
14
14
|
def initialize(attribute:, label: nil, type: :text, required: false, hint: nil, placeholder: nil, min: nil, max: nil, step: nil, value: nil, options: [], errors: [])
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class InputComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "
|
|
6
|
+
BASE_CLASSES = "ks-input"
|
|
7
7
|
|
|
8
|
-
DISABLED_CLASSES = "
|
|
8
|
+
DISABLED_CLASSES = "ks-input-disabled"
|
|
9
9
|
|
|
10
10
|
TYPE_MAP = {
|
|
11
11
|
text: "text",
|
|
@@ -26,10 +26,8 @@ module Keystone
|
|
|
26
26
|
@step = step
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
FOCUS_CLASSES = "focus:border-accent-500 focus:ring-accent-500 dark:focus:border-accent-400 dark:focus:ring-accent-400"
|
|
30
|
-
|
|
31
29
|
def classes
|
|
32
|
-
tokens = [ BASE_CLASSES
|
|
30
|
+
tokens = [ BASE_CLASSES ]
|
|
33
31
|
tokens << DISABLED_CLASSES if @disabled
|
|
34
32
|
tokens.join(" ")
|
|
35
33
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class SelectComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "
|
|
6
|
+
BASE_CLASSES = "ks-input"
|
|
7
7
|
|
|
8
|
-
DISABLED_CLASSES = "
|
|
8
|
+
DISABLED_CLASSES = "ks-input-disabled"
|
|
9
9
|
|
|
10
10
|
attr_reader :options, :selected, :include_blank
|
|
11
11
|
|
|
@@ -17,10 +17,8 @@ module Keystone
|
|
|
17
17
|
@disabled = disabled
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
FOCUS_CLASSES = "focus:border-accent-500 focus:ring-accent-500 dark:focus:border-accent-400 dark:focus:ring-accent-400"
|
|
21
|
-
|
|
22
20
|
def classes
|
|
23
|
-
tokens = [ BASE_CLASSES
|
|
21
|
+
tokens = [ BASE_CLASSES ]
|
|
24
22
|
tokens << DISABLED_CLASSES if @disabled
|
|
25
23
|
tokens.join(" ")
|
|
26
24
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Keystone
|
|
4
4
|
module Ui
|
|
5
5
|
class TextareaComponent < ViewComponent::Base
|
|
6
|
-
BASE_CLASSES = "
|
|
6
|
+
BASE_CLASSES = "ks-input"
|
|
7
7
|
|
|
8
|
-
DISABLED_CLASSES = "
|
|
8
|
+
DISABLED_CLASSES = "ks-input-disabled"
|
|
9
9
|
|
|
10
10
|
def initialize(name:, value: nil, rows: 3, placeholder: nil, disabled: false)
|
|
11
11
|
@name = name
|
|
@@ -15,10 +15,8 @@ module Keystone
|
|
|
15
15
|
@disabled = disabled
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
FOCUS_CLASSES = "focus:border-accent-500 focus:ring-accent-500 dark:focus:border-accent-400 dark:focus:ring-accent-400"
|
|
19
|
-
|
|
20
18
|
def classes
|
|
21
|
-
tokens = [ BASE_CLASSES
|
|
19
|
+
tokens = [ BASE_CLASSES ]
|
|
22
20
|
tokens << DISABLED_CLASSES if @disabled
|
|
23
21
|
tokens.join(" ")
|
|
24
22
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="<%= GROUP_CLASSES %>" role="group" aria-label="Theme" data-controller="theme-toggle">
|
|
2
|
+
<% options.each do |label, mode| %>
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
class="<%= OPTION_CLASSES %>"
|
|
6
|
+
aria-pressed="<%= pressed?(mode) %>"
|
|
7
|
+
data-action="theme-toggle#choose"
|
|
8
|
+
data-theme-toggle-mode-param="<%= mode %>"><%= label %></button>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
module Ui
|
|
5
|
+
class ThemeToggleComponent < ViewComponent::Base
|
|
6
|
+
OPTIONS = [ [ "Light", "light" ], [ "Dark", "dark" ], [ "System", "system" ] ].freeze
|
|
7
|
+
GROUP_CLASSES = "inline-flex gap-1"
|
|
8
|
+
OPTION_CLASSES = "ks-button ks-button-sm ks-button-secondary aria-pressed:bg-accent-600 aria-pressed:hover:bg-accent-500"
|
|
9
|
+
|
|
10
|
+
def initialize(current: nil)
|
|
11
|
+
@current = current
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def options
|
|
15
|
+
OPTIONS
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def pressed?(mode)
|
|
19
|
+
mode == (KeystoneUi::ThemeChoice::MODES.include?(@current) ? @current : "system")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -41,6 +41,14 @@ module KeystoneUiHelper
|
|
|
41
41
|
render Keystone::Ui::BadgeComponent.new(**args)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def ui_theme_toggle
|
|
45
|
+
render Keystone::Ui::ThemeToggleComponent.new(current: cookies[KeystoneUi::ThemeChoice::COOKIE])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def keystone_theme_attributes
|
|
49
|
+
KeystoneUi::ThemeChoice.new(cookies[KeystoneUi::ThemeChoice::COOKIE]).html_attributes_markup.html_safe
|
|
50
|
+
end
|
|
51
|
+
|
|
44
52
|
def ui_copy_button(**args)
|
|
45
53
|
render Keystone::Ui::CopyButtonComponent.new(**args)
|
|
46
54
|
end
|
|
@@ -145,6 +153,10 @@ module KeystoneUiHelper
|
|
|
145
153
|
render Keystone::Ui::RadioCardComponent.new(**args)
|
|
146
154
|
end
|
|
147
155
|
|
|
156
|
+
def ui_checkbox_row(**args)
|
|
157
|
+
render Keystone::Ui::CheckboxRowComponent.new(**args)
|
|
158
|
+
end
|
|
159
|
+
|
|
148
160
|
def ui_progress(**args)
|
|
149
161
|
render Keystone::Ui::ProgressComponent.new(**args)
|
|
150
162
|
end
|
data/config/importmap.rb
CHANGED
|
@@ -13,6 +13,7 @@ pin "keystone_ui/accordion_controller", to: "keystone_ui/accordion_controller.js
|
|
|
13
13
|
pin "keystone_ui/stat_card_info_controller", to: "keystone_ui/stat_card_info_controller.js"
|
|
14
14
|
pin "keystone_ui/auto_submit_controller", to: "keystone_ui/auto_submit_controller.js"
|
|
15
15
|
pin "keystone_ui/line_chart_controller", to: "keystone_ui/line_chart_controller.js"
|
|
16
|
+
pin "keystone_ui/theme_toggle_controller", to: "keystone_ui/theme_toggle_controller.js"
|
|
16
17
|
|
|
17
18
|
# Chart.js for the line-chart controller, so host apps don't pin it themselves.
|
|
18
19
|
# Vendored as a single self-contained bundle (deps inlined, no external imports).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "layout_theme_attributes"
|
|
4
|
+
|
|
3
5
|
module Keystone
|
|
4
6
|
class InstallGenerator < Rails::Generators::Base
|
|
5
7
|
desc "Set up Keystone UI in your Rails application"
|
|
@@ -11,6 +13,8 @@ module Keystone
|
|
|
11
13
|
JS_IMPORT = 'import { registerControllers } from "keystone_ui/index"'
|
|
12
14
|
JS_REGISTER = "registerControllers(application)"
|
|
13
15
|
|
|
16
|
+
LAYOUT_PATH = "app/views/layouts/application.html.erb"
|
|
17
|
+
|
|
14
18
|
def setup_tailwind
|
|
15
19
|
say ""
|
|
16
20
|
say "Keystone UI — setup", :green
|
|
@@ -86,5 +90,26 @@ module Keystone
|
|
|
86
90
|
append_to_file js_path, "\n#{JS_IMPORT}\n#{JS_REGISTER}\n"
|
|
87
91
|
say " ✔ Registered Keystone UI controllers", :green
|
|
88
92
|
end
|
|
93
|
+
|
|
94
|
+
def setup_theme
|
|
95
|
+
say ""
|
|
96
|
+
say "Wiring the Keystone UI theme toggle...", :green
|
|
97
|
+
|
|
98
|
+
layout_path = Rails.root.join(LAYOUT_PATH)
|
|
99
|
+
unless layout_path.exist?
|
|
100
|
+
say " ⚠ #{LAYOUT_PATH} not found — add `<%= keystone_theme_attributes %>` to your <html> tag manually.", :yellow
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
layout = layout_path.read
|
|
105
|
+
updated = Keystone::LayoutThemeAttributes.new(layout).apply
|
|
106
|
+
|
|
107
|
+
if updated == layout
|
|
108
|
+
say " ✔ Layout already marks the theme", :green
|
|
109
|
+
else
|
|
110
|
+
File.write(layout_path, updated)
|
|
111
|
+
say " ✔ Added the theme attributes to the layout's html tag", :green
|
|
112
|
+
end
|
|
113
|
+
end
|
|
89
114
|
end
|
|
90
115
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
class LayoutThemeAttributes
|
|
5
|
+
HELPER_CALL = "<%= keystone_theme_attributes %>"
|
|
6
|
+
|
|
7
|
+
def initialize(layout)
|
|
8
|
+
@layout = layout
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def apply
|
|
12
|
+
return @layout if @layout.include?(HELPER_CALL)
|
|
13
|
+
|
|
14
|
+
@layout.sub(/<html\b/, "<html #{HELPER_CALL}")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/keystone_ui/engine.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "keystone_ui/source_css"
|
|
4
|
+
|
|
3
5
|
module KeystoneUi
|
|
4
6
|
class Engine < ::Rails::Engine
|
|
5
7
|
config.autoload_paths << root.join("app/components")
|
|
@@ -27,21 +29,7 @@ module KeystoneUi
|
|
|
27
29
|
keystone_import = '@import "./keystone_source.css";'
|
|
28
30
|
next unless css_path.read.include?(keystone_import)
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
lines = [ %(@source "#{root}/app/components/**/*.{erb,rb}";) ]
|
|
32
|
-
|
|
33
|
-
# Import theme CSS (accent + surface custom property defaults)
|
|
34
|
-
theme_css = root.join("app/assets/tailwind/keystone_ui_engine/theme.css")
|
|
35
|
-
lines << %(@import "#{theme_css}";) if theme_css.exist?
|
|
36
|
-
|
|
37
|
-
# Import component CSS files shipped with the gem
|
|
38
|
-
nav_css = root.join("app/assets/tailwind/keystone_ui_engine/nav.css")
|
|
39
|
-
lines << %(@import "#{nav_css}";) if nav_css.exist?
|
|
40
|
-
|
|
41
|
-
color_picker_css = root.join("app/assets/tailwind/keystone_ui_engine/color_picker.css")
|
|
42
|
-
lines << %(@import "#{color_picker_css}";) if color_picker_css.exist?
|
|
43
|
-
|
|
44
|
-
source_css.write(lines.join("\n") + "\n")
|
|
32
|
+
tailwind_dir.join("keystone_source.css").write(KeystoneUi::SourceCss.new(root).to_s)
|
|
45
33
|
end
|
|
46
34
|
end
|
|
47
35
|
end
|
data/lib/keystone_ui/safelist.rb
CHANGED
|
@@ -42,6 +42,7 @@ module Keystone
|
|
|
42
42
|
Keystone::Ui::ShowPageComponent,
|
|
43
43
|
Keystone::Ui::OptionCardComponent,
|
|
44
44
|
Keystone::Ui::RadioCardComponent,
|
|
45
|
+
Keystone::Ui::CheckboxRowComponent,
|
|
45
46
|
Keystone::Ui::ProgressComponent,
|
|
46
47
|
Keystone::Ui::ColorPickerComponent,
|
|
47
48
|
Keystone::Ui::MultiSelectComponent,
|
|
@@ -52,7 +53,8 @@ module Keystone
|
|
|
52
53
|
Keystone::Ui::FunnelComponent,
|
|
53
54
|
Keystone::Ui::PipelineComponent,
|
|
54
55
|
Keystone::Ui::CodeComponent,
|
|
55
|
-
Keystone::Ui::DisclosureComponent
|
|
56
|
+
Keystone::Ui::DisclosureComponent,
|
|
57
|
+
Keystone::Ui::ThemeToggleComponent
|
|
56
58
|
].freeze
|
|
57
59
|
|
|
58
60
|
# Constants that hold non-CSS values (e.g. HTML input type maps)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "keystone_ui/styles"
|
|
5
|
+
|
|
6
|
+
module KeystoneUi
|
|
7
|
+
class SourceCss
|
|
8
|
+
def initialize(root)
|
|
9
|
+
@root = Pathname.new(root)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
lines.join("\n") + "\n"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def styles_entry
|
|
19
|
+
KeystoneUi::Styles::Engine.root.join("app/assets/tailwind/keystone_ui_styles/engine.css")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def lines
|
|
23
|
+
[
|
|
24
|
+
%(@import "#{styles_entry}";),
|
|
25
|
+
%(@source "#{@root}/app/components/**/*.{erb,rb}";),
|
|
26
|
+
%(@import "#{@root}/app/assets/tailwind/keystone_ui_engine/nav.css";),
|
|
27
|
+
%(@import "#{@root}/app/assets/tailwind/keystone_ui_engine/color_picker.css";)
|
|
28
|
+
]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
class ThemeChoice
|
|
5
|
+
COOKIE = "keystone_theme"
|
|
6
|
+
MODES = %w[light dark].freeze
|
|
7
|
+
|
|
8
|
+
def initialize(mode)
|
|
9
|
+
@mode = mode
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def html_attributes
|
|
13
|
+
MODES.include?(@mode) ? { "data-theme" => @mode } : {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def html_attributes_markup
|
|
17
|
+
html_attributes.map { |name, value| %(#{name}="#{value}") }.join(" ")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/keystone_ui/version.rb
CHANGED
data/lib/keystone_ui.rb
CHANGED
|
@@ -117,6 +117,11 @@ one raises at render time.
|
|
|
117
117
|
hue/saturation panel and writes the hex into a hidden input named `name`.
|
|
118
118
|
- `ui_radio_card(name:, value:, label:, hint: nil, checked: false)` — a
|
|
119
119
|
selectable card backed by a real radio input; selection styling is pure CSS.
|
|
120
|
+
- `ui_checkbox_row(name:, value:, label:, hint: nil, checked: false)` — a real
|
|
121
|
+
checkbox with its label and optional hint inside one `<label>`, so a tap
|
|
122
|
+
anywhere on the row toggles the box. A checked row submits `value` under
|
|
123
|
+
`name`; give several rows the same array name, e.g. `"shown[]"`, to submit the
|
|
124
|
+
checked values as a list. An unchecked row submits nothing. No JavaScript.
|
|
120
125
|
- `ui_option_card(name:, value:, selected: false, input_data: {}, label_data: {})`
|
|
121
126
|
— takes a block. A radio whose visible body is whatever the block renders.
|
|
122
127
|
`input_data:`/`label_data:` become `data-*` attributes on the input and label.
|
data/the_local/interface.yml
CHANGED
|
@@ -41,6 +41,7 @@ develop:
|
|
|
41
41
|
- ui_bottom_nav_item
|
|
42
42
|
- ui_option_card
|
|
43
43
|
- ui_radio_card
|
|
44
|
+
- ui_checkbox_row
|
|
44
45
|
- ui_progress
|
|
45
46
|
- ui_settings_link
|
|
46
47
|
- ui_color_picker
|
|
@@ -68,6 +69,8 @@ sources:
|
|
|
68
69
|
- app/components/keystone/ui/card_component.rb
|
|
69
70
|
- app/components/keystone/ui/card_link_component.rb
|
|
70
71
|
- app/components/keystone/ui/chart_card_component.rb
|
|
72
|
+
- app/components/keystone/ui/checkbox_row_component.rb
|
|
73
|
+
- app/components/keystone/ui/checkbox_row_component.html.erb
|
|
71
74
|
- app/components/keystone/ui/code_component.rb
|
|
72
75
|
- app/components/keystone/ui/color_picker_component.rb
|
|
73
76
|
- app/components/keystone/ui/column_picker_component.rb
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: keystone_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler Schneider
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: keystone_ui-styles
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.1.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.1.0
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: view_component
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -54,10 +68,10 @@ files:
|
|
|
54
68
|
- app/assets/javascripts/keystone_ui/stat_card_info_controller.js
|
|
55
69
|
- app/assets/javascripts/keystone_ui/swipe_deck_controller.js
|
|
56
70
|
- app/assets/javascripts/keystone_ui/tab_switcher_controller.js
|
|
71
|
+
- app/assets/javascripts/keystone_ui/theme_toggle_controller.js
|
|
57
72
|
- app/assets/tailwind/keystone_ui_engine/color_picker.css
|
|
58
73
|
- app/assets/tailwind/keystone_ui_engine/engine.css
|
|
59
74
|
- app/assets/tailwind/keystone_ui_engine/nav.css
|
|
60
|
-
- app/assets/tailwind/keystone_ui_engine/theme.css
|
|
61
75
|
- app/components/keystone/ui/accordion_component.html.erb
|
|
62
76
|
- app/components/keystone/ui/accordion_component.rb
|
|
63
77
|
- app/components/keystone/ui/alert_component.html.erb
|
|
@@ -76,6 +90,8 @@ files:
|
|
|
76
90
|
- app/components/keystone/ui/card_link_component.rb
|
|
77
91
|
- app/components/keystone/ui/chart_card_component.html.erb
|
|
78
92
|
- app/components/keystone/ui/chart_card_component.rb
|
|
93
|
+
- app/components/keystone/ui/checkbox_row_component.html.erb
|
|
94
|
+
- app/components/keystone/ui/checkbox_row_component.rb
|
|
79
95
|
- app/components/keystone/ui/code_component.html.erb
|
|
80
96
|
- app/components/keystone/ui/code_component.rb
|
|
81
97
|
- app/components/keystone/ui/color_picker_component.html.erb
|
|
@@ -155,13 +171,18 @@ files:
|
|
|
155
171
|
- app/components/keystone/ui/tab_switcher_component.rb
|
|
156
172
|
- app/components/keystone/ui/textarea_component.html.erb
|
|
157
173
|
- app/components/keystone/ui/textarea_component.rb
|
|
174
|
+
- app/components/keystone/ui/theme_toggle_component.html.erb
|
|
175
|
+
- app/components/keystone/ui/theme_toggle_component.rb
|
|
158
176
|
- app/helpers/keystone_ui_helper.rb
|
|
159
177
|
- config/importmap.rb
|
|
160
178
|
- lib/generators/keystone/install_generator.rb
|
|
179
|
+
- lib/generators/keystone/layout_theme_attributes.rb
|
|
161
180
|
- lib/keystone_ui.rb
|
|
162
181
|
- lib/keystone_ui/configuration.rb
|
|
163
182
|
- lib/keystone_ui/engine.rb
|
|
164
183
|
- lib/keystone_ui/safelist.rb
|
|
184
|
+
- lib/keystone_ui/source_css.rb
|
|
185
|
+
- lib/keystone_ui/theme_choice.rb
|
|
165
186
|
- lib/keystone_ui/version.rb
|
|
166
187
|
- the_local/agents/keystone_ui-develop.md
|
|
167
188
|
- the_local/agents/keystone_ui-info.md
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/* Keystone UI theme — defines accent and surface color tokens as CSS custom properties.
|
|
2
|
-
Apps override these variables to customize the color palette. */
|
|
3
|
-
|
|
4
|
-
@theme {
|
|
5
|
-
/* Accent — defaults to blue */
|
|
6
|
-
--color-accent-50: #eff6ff;
|
|
7
|
-
--color-accent-100: #dbeafe;
|
|
8
|
-
--color-accent-200: #bfdbfe;
|
|
9
|
-
--color-accent-300: #93c5fd;
|
|
10
|
-
--color-accent-400: #60a5fa;
|
|
11
|
-
--color-accent-500: #3b82f6;
|
|
12
|
-
--color-accent-600: #2563eb;
|
|
13
|
-
--color-accent-700: #1d4ed8;
|
|
14
|
-
--color-accent-800: #1e40af;
|
|
15
|
-
--color-accent-900: #1e3a8a;
|
|
16
|
-
|
|
17
|
-
/* Surface — defaults to zinc */
|
|
18
|
-
--color-surface-50: #fafafa;
|
|
19
|
-
--color-surface-100: #f4f4f5;
|
|
20
|
-
--color-surface-200: #e4e4e7;
|
|
21
|
-
--color-surface-300: #d4d4d8;
|
|
22
|
-
--color-surface-400: #a1a1aa;
|
|
23
|
-
--color-surface-500: #71717a;
|
|
24
|
-
--color-surface-600: #52525b;
|
|
25
|
-
--color-surface-700: #3f3f46;
|
|
26
|
-
--color-surface-800: #27272a;
|
|
27
|
-
--color-surface-900: #18181b;
|
|
28
|
-
--color-surface-950: #09090b;
|
|
29
|
-
|
|
30
|
-
/* Accent — darkest shade */
|
|
31
|
-
--color-accent-950: #172554;
|
|
32
|
-
}
|