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,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUiHelper
|
|
4
|
+
def ui_accordion(**args)
|
|
5
|
+
render Keystone::Ui::AccordionComponent.new(**args)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def ui_tab_switcher(**args, &block)
|
|
9
|
+
render Keystone::Ui::TabSwitcherComponent.new(**args), &block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def ui_stat_card(**args)
|
|
13
|
+
render Keystone::Ui::StatCardComponent.new(**args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ui_chart_card(**args, &block)
|
|
17
|
+
render Keystone::Ui::ChartCardComponent.new(**args), &block
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def ui_line_chart(**args)
|
|
21
|
+
render Keystone::Ui::LineChartComponent.new(**args)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ui_cta_banner(**args, &block)
|
|
25
|
+
render Keystone::Ui::CtaBannerComponent.new(**args), &block
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ui_feature_grid(**args)
|
|
29
|
+
render Keystone::Ui::FeatureGridComponent.new(**args)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ui_hero(**args, &block)
|
|
33
|
+
render Keystone::Ui::HeroComponent.new(**args), &block
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def ui_modal(**args, &block)
|
|
37
|
+
render Keystone::Ui::ModalComponent.new(**args), &block
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ui_badge(**args)
|
|
41
|
+
render Keystone::Ui::BadgeComponent.new(**args)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def ui_copy_button(**args)
|
|
45
|
+
render Keystone::Ui::CopyButtonComponent.new(**args)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ui_card(**args)
|
|
49
|
+
render Keystone::Ui::CardComponent.new(**args)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def ui_button(**args)
|
|
53
|
+
render Keystone::Ui::ButtonComponent.new(**args)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ui_data_table(**args, &block)
|
|
57
|
+
render Keystone::Ui::DataTableComponent.new(**args), &block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ui_page(**args, &block)
|
|
61
|
+
render Keystone::Ui::PageComponent.new(**args), &block
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def ui_section(**args, &block)
|
|
65
|
+
render Keystone::Ui::SectionComponent.new(**args), &block
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ui_grid(**args, &block)
|
|
69
|
+
render Keystone::Ui::GridComponent.new(**args), &block
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ui_panel(**args, &block)
|
|
73
|
+
render Keystone::Ui::PanelComponent.new(**args), &block
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ui_card_link(**args, &block)
|
|
77
|
+
render Keystone::Ui::CardLinkComponent.new(**args), &block
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ui_input(**args)
|
|
81
|
+
render Keystone::Ui::InputComponent.new(**args)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ui_textarea(**args)
|
|
85
|
+
render Keystone::Ui::TextareaComponent.new(**args)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ui_select(**args)
|
|
89
|
+
render Keystone::Ui::SelectComponent.new(**args)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def ui_form_field(**args)
|
|
93
|
+
render Keystone::Ui::FormFieldComponent.new(**args)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ui_page_header(**args, &block)
|
|
97
|
+
render Keystone::Ui::PageHeaderComponent.new(**args), &block
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def ui_alert(**args)
|
|
101
|
+
render Keystone::Ui::AlertComponent.new(**args)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def ui_form_page(**args)
|
|
105
|
+
render Keystone::Ui::FormPageComponent.new(**args)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def ui_show_page(**args)
|
|
109
|
+
render Keystone::Ui::ShowPageComponent.new(**args)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def ui_mobile_header(**args)
|
|
113
|
+
render Keystone::Ui::MobileHeaderComponent.new(**args)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def ui_mobile_actions(**args, &block)
|
|
117
|
+
render Keystone::Ui::MobileActionsComponent.new(**args), &block
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def ui_navbar(**args, &block)
|
|
121
|
+
render Keystone::Ui::NavbarComponent.new(**args), &block
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def ui_nav_item(**args)
|
|
125
|
+
render Keystone::Ui::NavItemComponent.new(**args)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def ui_nav_dropdown(**args, &block)
|
|
129
|
+
render Keystone::Ui::NavDropdownComponent.new(**args), &block
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def ui_bottom_nav(**args, &block)
|
|
133
|
+
render Keystone::Ui::BottomNavComponent.new(**args), &block
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ui_bottom_nav_item(**args)
|
|
137
|
+
render Keystone::Ui::BottomNavItemComponent.new(**args)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def ui_option_card(**args, &block)
|
|
141
|
+
render Keystone::Ui::OptionCardComponent.new(**args), &block
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def ui_radio_card(**args)
|
|
145
|
+
render Keystone::Ui::RadioCardComponent.new(**args)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def ui_progress(**args)
|
|
149
|
+
render Keystone::Ui::ProgressComponent.new(**args)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def ui_settings_link(**args)
|
|
153
|
+
render Keystone::Ui::SettingsLinkComponent.new(**args)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def ui_color_picker(**args)
|
|
157
|
+
render Keystone::Ui::ColorPickerComponent.new(**args)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def ui_multi_select(**args)
|
|
161
|
+
render Keystone::Ui::MultiSelectComponent.new(**args)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def ui_swipe_deck(**args, &block)
|
|
165
|
+
render Keystone::Ui::SwipeDeckComponent.new(**args), &block
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def ui_column_picker(**args)
|
|
169
|
+
render Keystone::Ui::ColumnPickerComponent.new(**args)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def ui_form(**args, &block)
|
|
173
|
+
render Keystone::Ui::FormComponent.new(**args), &block
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def ui_file_upload(**args)
|
|
177
|
+
render Keystone::Ui::FileUploadComponent.new(**args)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def ui_funnel(**args)
|
|
181
|
+
render Keystone::Ui::FunnelComponent.new(**args)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def ui_pipeline(**args)
|
|
185
|
+
render Keystone::Ui::PipelineComponent.new(**args)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def ui_code(**args, &block)
|
|
189
|
+
render Keystone::Ui::CodeComponent.new(**args), &block
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def ui_disclosure(**args, &block)
|
|
193
|
+
render Keystone::Ui::DisclosureComponent.new(**args), &block
|
|
194
|
+
end
|
|
195
|
+
end
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
pin "keystone_ui/index", to: "keystone_ui/index.js"
|
|
2
|
+
pin "keystone_ui/color_picker_controller", to: "keystone_ui/color_picker_controller.js"
|
|
3
|
+
pin "keystone_ui/multi_select_controller", to: "keystone_ui/multi_select_controller.js"
|
|
4
|
+
pin "keystone_ui/swipe_deck_controller", to: "keystone_ui/swipe_deck_controller.js"
|
|
5
|
+
pin "keystone_ui/column_picker_controller", to: "keystone_ui/column_picker_controller.js"
|
|
6
|
+
pin "keystone_ui/file_upload_controller", to: "keystone_ui/file_upload_controller.js"
|
|
7
|
+
pin "keystone_ui/dropdown_controller", to: "keystone_ui/dropdown_controller.js"
|
|
8
|
+
pin "keystone_ui/dismiss_controller", to: "keystone_ui/dismiss_controller.js"
|
|
9
|
+
pin "keystone_ui/modal_controller", to: "keystone_ui/modal_controller.js"
|
|
10
|
+
pin "keystone_ui/clipboard_controller", to: "keystone_ui/clipboard_controller.js"
|
|
11
|
+
pin "keystone_ui/tab_switcher_controller", to: "keystone_ui/tab_switcher_controller.js"
|
|
12
|
+
pin "keystone_ui/accordion_controller", to: "keystone_ui/accordion_controller.js"
|
|
13
|
+
pin "keystone_ui/stat_card_info_controller", to: "keystone_ui/stat_card_info_controller.js"
|
|
14
|
+
pin "keystone_ui/auto_submit_controller", to: "keystone_ui/auto_submit_controller.js"
|
|
15
|
+
pin "keystone_ui/line_chart_controller", to: "keystone_ui/line_chart_controller.js"
|
|
16
|
+
|
|
17
|
+
# Chart.js for the line-chart controller, so host apps don't pin it themselves.
|
|
18
|
+
# Vendored as a single self-contained bundle (deps inlined, no external imports).
|
|
19
|
+
pin "chart.js", to: "keystone_ui/chartjs.js"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
|
5
|
+
desc "Set up Keystone UI in your Rails application"
|
|
6
|
+
|
|
7
|
+
TAILWIND_IMPORT = '@import "tailwindcss";'
|
|
8
|
+
KEYSTONE_IMPORT = '@import "./keystone_source.css";'
|
|
9
|
+
|
|
10
|
+
JS_CONTROLLERS_PATH = "app/javascript/controllers/index.js"
|
|
11
|
+
JS_IMPORT = 'import { registerControllers } from "keystone_ui/index"'
|
|
12
|
+
JS_REGISTER = "registerControllers(application)"
|
|
13
|
+
|
|
14
|
+
def setup_tailwind
|
|
15
|
+
say ""
|
|
16
|
+
say "Keystone UI — setup", :green
|
|
17
|
+
say "=" * 40
|
|
18
|
+
say ""
|
|
19
|
+
|
|
20
|
+
css_path = Rails.root.join("app/assets/tailwind/application.css")
|
|
21
|
+
unless css_path.exist?
|
|
22
|
+
FileUtils.mkdir_p(css_path.dirname)
|
|
23
|
+
File.write(css_path, "#{TAILWIND_IMPORT}\n#{KEYSTONE_IMPORT}\n")
|
|
24
|
+
say " ✔ Created application.css with Tailwind and Keystone imports", :green
|
|
25
|
+
say ""
|
|
26
|
+
say "Done! See the README for component usage.", :green
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
content = css_path.read
|
|
31
|
+
changed = false
|
|
32
|
+
|
|
33
|
+
# Remove legacy @import for engine CSS (no longer needed)
|
|
34
|
+
legacy_import = '@import "../builds/tailwind/keystone_components_engine";' # also handles pre-rename installs
|
|
35
|
+
if content.include?(legacy_import)
|
|
36
|
+
gsub_file css_path, /#{Regexp.escape(legacy_import)}\n?/, ""
|
|
37
|
+
say " ✔ Removed legacy engine CSS import", :green
|
|
38
|
+
changed = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Remove legacy inline safelist (replaced by @source path injection)
|
|
42
|
+
legacy_safelist = "/* keystone:safelist */"
|
|
43
|
+
if content.include?(legacy_safelist)
|
|
44
|
+
gsub_file css_path, /#{Regexp.escape(legacy_safelist)}.*\n?/, ""
|
|
45
|
+
say " ✔ Removed legacy inline safelist", :green
|
|
46
|
+
changed = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Remove legacy marker comment (replaced by @import for keystone_source.css)
|
|
50
|
+
legacy_marker = "/* keystone:source */"
|
|
51
|
+
content = css_path.read # re-read after removals
|
|
52
|
+
if content.include?(legacy_marker)
|
|
53
|
+
gsub_file css_path, /#{Regexp.escape(legacy_marker)}.*\n?/, ""
|
|
54
|
+
say " ✔ Removed legacy source marker", :green
|
|
55
|
+
changed = true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Inject @import for keystone_source.css (written at boot by engine initializer)
|
|
59
|
+
content = css_path.read # re-read after removals
|
|
60
|
+
unless content.include?(KEYSTONE_IMPORT)
|
|
61
|
+
inject_into_file css_path, "#{KEYSTONE_IMPORT}\n", after: /#{Regexp.escape(TAILWIND_IMPORT)}\n/
|
|
62
|
+
say " ✔ Added Keystone source import", :green
|
|
63
|
+
changed = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
say " ✔ application.css already up to date", :green unless changed
|
|
67
|
+
say ""
|
|
68
|
+
say "Done! See the README for component usage.", :green
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def setup_javascript
|
|
72
|
+
say ""
|
|
73
|
+
say "Wiring Keystone UI Stimulus controllers...", :green
|
|
74
|
+
|
|
75
|
+
js_path = Rails.root.join(JS_CONTROLLERS_PATH)
|
|
76
|
+
unless js_path.exist?
|
|
77
|
+
say " ⚠ #{JS_CONTROLLERS_PATH} not found — add `#{JS_REGISTER}` to your Stimulus setup manually.", :yellow
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if js_path.read.include?(JS_REGISTER)
|
|
82
|
+
say " ✔ Keystone UI controllers already registered", :green
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
append_to_file js_path, "\n#{JS_IMPORT}\n#{JS_REGISTER}\n"
|
|
87
|
+
say " ✔ Registered Keystone UI controllers", :green
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :accent, :surface
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@accent = :blue
|
|
9
|
+
@surface = :zinc
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.configuration
|
|
14
|
+
@configuration ||= Configuration.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.configure
|
|
18
|
+
yield(configuration)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.reset_configuration!
|
|
22
|
+
@configuration = Configuration.new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
config.autoload_paths << root.join("app/components")
|
|
6
|
+
|
|
7
|
+
# Pin JavaScript controllers for importmap-based host apps.
|
|
8
|
+
initializer "keystone_ui.importmap", before: "importmap" do |app|
|
|
9
|
+
if app.config.respond_to?(:importmap)
|
|
10
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Write a separate keystone_source.css with the gem's @source directive
|
|
15
|
+
# so Tailwind can scan component files during asset compilation.
|
|
16
|
+
#
|
|
17
|
+
# Uses after_initialize so host app config/initializers (where
|
|
18
|
+
# KeystoneUi.configure is called) have already run. Dependent engines
|
|
19
|
+
# that also need the palette should use config.after_initialize too —
|
|
20
|
+
# Rails runs these in engine dependency order, so KeystoneUi's block
|
|
21
|
+
# executes before any engine that depends on it.
|
|
22
|
+
config.after_initialize do
|
|
23
|
+
tailwind_dir = Rails.root.join("app/assets/tailwind")
|
|
24
|
+
css_path = tailwind_dir.join("application.css")
|
|
25
|
+
next unless css_path.exist?
|
|
26
|
+
|
|
27
|
+
keystone_import = '@import "./keystone_source.css";'
|
|
28
|
+
next unless css_path.read.include?(keystone_import)
|
|
29
|
+
|
|
30
|
+
source_css = tailwind_dir.join("keystone_source.css")
|
|
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")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
module Safelist
|
|
5
|
+
# All component classes to scan for Tailwind CSS classes.
|
|
6
|
+
# The safelist_spec verifies this list matches Keystone::Ui components.
|
|
7
|
+
COMPONENTS = [
|
|
8
|
+
Keystone::Ui::AccordionComponent,
|
|
9
|
+
Keystone::Ui::TabSwitcherComponent,
|
|
10
|
+
Keystone::Ui::StatCardComponent,
|
|
11
|
+
Keystone::Ui::ChartCardComponent,
|
|
12
|
+
Keystone::Ui::LineChartComponent,
|
|
13
|
+
Keystone::Ui::CtaBannerComponent,
|
|
14
|
+
Keystone::Ui::FeatureGridComponent,
|
|
15
|
+
Keystone::Ui::HeroComponent,
|
|
16
|
+
Keystone::Ui::ModalComponent,
|
|
17
|
+
Keystone::Ui::SelectComponent,
|
|
18
|
+
Keystone::Ui::BadgeComponent,
|
|
19
|
+
Keystone::Ui::CopyButtonComponent,
|
|
20
|
+
Keystone::Ui::CardComponent,
|
|
21
|
+
Keystone::Ui::ButtonComponent,
|
|
22
|
+
Keystone::Ui::DataTableComponent,
|
|
23
|
+
Keystone::Ui::PageComponent,
|
|
24
|
+
Keystone::Ui::SectionComponent,
|
|
25
|
+
Keystone::Ui::GridComponent,
|
|
26
|
+
Keystone::Ui::PanelComponent,
|
|
27
|
+
Keystone::Ui::CardLinkComponent,
|
|
28
|
+
Keystone::Ui::InputComponent,
|
|
29
|
+
Keystone::Ui::TextareaComponent,
|
|
30
|
+
Keystone::Ui::FormFieldComponent,
|
|
31
|
+
Keystone::Ui::PageHeaderComponent,
|
|
32
|
+
Keystone::Ui::AlertComponent,
|
|
33
|
+
Keystone::Ui::NavDropdownComponent,
|
|
34
|
+
Keystone::Ui::NavItemComponent,
|
|
35
|
+
Keystone::Ui::BottomNavItemComponent,
|
|
36
|
+
Keystone::Ui::NavbarComponent,
|
|
37
|
+
Keystone::Ui::BottomNavComponent,
|
|
38
|
+
Keystone::Ui::SettingsLinkComponent,
|
|
39
|
+
Keystone::Ui::FormPageComponent,
|
|
40
|
+
Keystone::Ui::MobileActionsComponent,
|
|
41
|
+
Keystone::Ui::MobileHeaderComponent,
|
|
42
|
+
Keystone::Ui::ShowPageComponent,
|
|
43
|
+
Keystone::Ui::OptionCardComponent,
|
|
44
|
+
Keystone::Ui::RadioCardComponent,
|
|
45
|
+
Keystone::Ui::ProgressComponent,
|
|
46
|
+
Keystone::Ui::ColorPickerComponent,
|
|
47
|
+
Keystone::Ui::MultiSelectComponent,
|
|
48
|
+
Keystone::Ui::SwipeDeckComponent,
|
|
49
|
+
Keystone::Ui::ColumnPickerComponent,
|
|
50
|
+
Keystone::Ui::FormComponent,
|
|
51
|
+
Keystone::Ui::FileUploadComponent,
|
|
52
|
+
Keystone::Ui::FunnelComponent,
|
|
53
|
+
Keystone::Ui::PipelineComponent,
|
|
54
|
+
Keystone::Ui::CodeComponent,
|
|
55
|
+
Keystone::Ui::DisclosureComponent
|
|
56
|
+
].freeze
|
|
57
|
+
|
|
58
|
+
# Constants that hold non-CSS values (e.g. HTML input type maps)
|
|
59
|
+
SKIP_CONSTANTS = %i[TYPE_MAP ELLIPSIS_ICON BACK_ICON CARET_ICON CLOSE_ICON COPY_ICON SORT_ASC_ICON SORT_DESC_ICON SORT_NEUTRAL_ICON COLUMNS_ICON UPLOAD_ICON].freeze
|
|
60
|
+
|
|
61
|
+
# Classes used in Ruby methods or ERB templates, not in frozen constants.
|
|
62
|
+
# These cannot be auto-extracted and must be listed manually.
|
|
63
|
+
# The safelist_spec ensures every entry here appears in SAFELIST.
|
|
64
|
+
NON_CONSTANT_CLASSES = %w[
|
|
65
|
+
grid
|
|
66
|
+
mx-auto
|
|
67
|
+
overflow-hidden rounded-lg border border-gray-200 shadow-sm
|
|
68
|
+
dark:border-zinc-700 dark:shadow-none
|
|
69
|
+
relative min-w-full divide-y divide-gray-300 dark:divide-white/15
|
|
70
|
+
bg-gray-50 dark:bg-gray-800/75
|
|
71
|
+
divide-gray-200 bg-white dark:divide-white/10 dark:bg-gray-800/50
|
|
72
|
+
px-3 py-4 text-sm text-gray-500 dark:text-gray-400
|
|
73
|
+
].freeze
|
|
74
|
+
|
|
75
|
+
def self.extract_classes_from_hash(hash, classes)
|
|
76
|
+
hash.each_value do |v|
|
|
77
|
+
case v
|
|
78
|
+
when String then classes.concat(v.split)
|
|
79
|
+
when Hash then extract_classes_from_hash(v, classes)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.generate
|
|
85
|
+
classes = []
|
|
86
|
+
|
|
87
|
+
COMPONENTS.each do |klass|
|
|
88
|
+
klass.constants(false).each do |const_name|
|
|
89
|
+
next if SKIP_CONSTANTS.include?(const_name)
|
|
90
|
+
|
|
91
|
+
value = klass.const_get(const_name)
|
|
92
|
+
case value
|
|
93
|
+
when String then classes.concat(value.split)
|
|
94
|
+
when Hash then extract_classes_from_hash(value, classes)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
classes.concat(NON_CONSTANT_CLASSES)
|
|
100
|
+
|
|
101
|
+
classes.uniq.sort.join(" ")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
SAFELIST = Safelist.generate
|
|
106
|
+
end
|