kiso 0.1.0.pre → 0.2.0.pre

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.
Files changed (236) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -2
  3. data/README.md +67 -27
  4. data/Rakefile +8 -0
  5. data/app/assets/tailwind/kiso/checkbox.css +18 -0
  6. data/app/assets/tailwind/kiso/color-mode.css +9 -0
  7. data/app/assets/tailwind/kiso/dashboard.css +194 -0
  8. data/app/assets/tailwind/kiso/engine.css +117 -0
  9. data/app/assets/tailwind/kiso/input-otp.css +10 -0
  10. data/app/assets/tailwind/kiso/radio-group.css +17 -0
  11. data/app/helpers/kiso/component_helper.rb +46 -27
  12. data/app/helpers/kiso/icon_helper.rb +53 -9
  13. data/app/helpers/kiso/theme_helper.rb +38 -0
  14. data/app/javascript/controllers/kiso/combobox_controller.js +616 -0
  15. data/app/javascript/controllers/kiso/command_controller.js +184 -0
  16. data/app/javascript/controllers/kiso/command_dialog_controller.js +104 -0
  17. data/app/javascript/controllers/kiso/dropdown_menu_controller.js +684 -0
  18. data/app/javascript/controllers/kiso/index.d.ts +12 -0
  19. data/app/javascript/controllers/kiso/index.js +42 -0
  20. data/app/javascript/controllers/kiso/input_otp_controller.js +195 -0
  21. data/app/javascript/controllers/kiso/popover_controller.js +254 -0
  22. data/app/javascript/controllers/kiso/select_controller.js +307 -0
  23. data/app/javascript/controllers/kiso/sidebar_controller.js +84 -0
  24. data/app/javascript/controllers/kiso/theme_controller.js +89 -0
  25. data/app/javascript/controllers/kiso/toggle_controller.js +24 -0
  26. data/app/javascript/controllers/kiso/toggle_group_controller.js +128 -0
  27. data/app/javascript/kiso/utils/focusable.js +8 -0
  28. data/app/javascript/kiso/utils/highlight.js +43 -0
  29. data/app/javascript/kiso/utils/positioning.js +86 -0
  30. data/app/javascript/kiso/vendor/floating-ui-core.js +1 -0
  31. data/app/javascript/kiso/vendor/floating-ui-dom.js +1 -0
  32. data/app/views/kiso/components/_alert.html.erb +1 -1
  33. data/app/views/kiso/components/_avatar.html.erb +23 -0
  34. data/app/views/kiso/components/_badge.html.erb +1 -1
  35. data/app/views/kiso/components/_breadcrumb.html.erb +8 -0
  36. data/app/views/kiso/components/_button.html.erb +1 -1
  37. data/app/views/kiso/components/_card.html.erb +1 -1
  38. data/app/views/kiso/components/_checkbox.html.erb +7 -0
  39. data/app/views/kiso/components/_color_mode_button.html.erb +14 -0
  40. data/app/views/kiso/components/_color_mode_select.html.erb +24 -0
  41. data/app/views/kiso/components/_combobox.html.erb +12 -0
  42. data/app/views/kiso/components/_command.html.erb +7 -0
  43. data/app/views/kiso/components/_dashboard_group.html.erb +14 -0
  44. data/app/views/kiso/components/_dashboard_navbar.html.erb +7 -0
  45. data/app/views/kiso/components/_dashboard_panel.html.erb +7 -0
  46. data/app/views/kiso/components/_dashboard_sidebar.html.erb +11 -0
  47. data/app/views/kiso/components/_dashboard_toolbar.html.erb +7 -0
  48. data/app/views/kiso/components/_dropdown_menu.html.erb +7 -0
  49. data/app/views/kiso/components/{_empty_state.html.erb → _empty.html.erb} +2 -2
  50. data/app/views/kiso/components/_field.html.erb +12 -0
  51. data/app/views/kiso/components/_field_group.html.erb +7 -0
  52. data/app/views/kiso/components/_field_set.html.erb +7 -0
  53. data/app/views/kiso/components/_input.html.erb +8 -0
  54. data/app/views/kiso/components/_input_group.html.erb +8 -0
  55. data/app/views/kiso/components/_input_otp.html.erb +22 -0
  56. data/app/views/kiso/components/_kbd.html.erb +7 -0
  57. data/app/views/kiso/components/_label.html.erb +5 -0
  58. data/app/views/kiso/components/_nav.html.erb +7 -0
  59. data/app/views/kiso/components/_pagination.html.erb +9 -0
  60. data/app/views/kiso/components/_popover.html.erb +8 -0
  61. data/app/views/kiso/components/_radio_group.html.erb +8 -0
  62. data/app/views/kiso/components/_select.html.erb +8 -0
  63. data/app/views/kiso/components/_select_native.html.erb +16 -0
  64. data/app/views/kiso/components/_separator.html.erb +1 -1
  65. data/app/views/kiso/components/_stats_card.html.erb +1 -1
  66. data/app/views/kiso/components/_stats_grid.html.erb +1 -1
  67. data/app/views/kiso/components/_switch.html.erb +10 -0
  68. data/app/views/kiso/components/_table.html.erb +2 -1
  69. data/app/views/kiso/components/_textarea.html.erb +9 -0
  70. data/app/views/kiso/components/_toggle.html.erb +12 -0
  71. data/app/views/kiso/components/_toggle_group.html.erb +12 -0
  72. data/app/views/kiso/components/alert/_description.html.erb +1 -1
  73. data/app/views/kiso/components/alert/_title.html.erb +1 -1
  74. data/app/views/kiso/components/avatar/_badge.html.erb +7 -0
  75. data/app/views/kiso/components/avatar/_fallback.html.erb +7 -0
  76. data/app/views/kiso/components/avatar/_group.html.erb +7 -0
  77. data/app/views/kiso/components/avatar/_group_count.html.erb +7 -0
  78. data/app/views/kiso/components/avatar/_image.html.erb +6 -0
  79. data/app/views/kiso/components/breadcrumb/_ellipsis.html.erb +10 -0
  80. data/app/views/kiso/components/breadcrumb/_item.html.erb +7 -0
  81. data/app/views/kiso/components/breadcrumb/_link.html.erb +7 -0
  82. data/app/views/kiso/components/breadcrumb/_list.html.erb +7 -0
  83. data/app/views/kiso/components/breadcrumb/_page.html.erb +9 -0
  84. data/app/views/kiso/components/breadcrumb/_separator.html.erb +9 -0
  85. data/app/views/kiso/components/card/_action.html.erb +7 -0
  86. data/app/views/kiso/components/card/_content.html.erb +1 -1
  87. data/app/views/kiso/components/card/_description.html.erb +1 -1
  88. data/app/views/kiso/components/card/_footer.html.erb +1 -1
  89. data/app/views/kiso/components/card/_header.html.erb +1 -1
  90. data/app/views/kiso/components/card/_title.html.erb +1 -1
  91. data/app/views/kiso/components/combobox/_chip.html.erb +19 -0
  92. data/app/views/kiso/components/combobox/_chips.html.erb +20 -0
  93. data/app/views/kiso/components/combobox/_chips_input.html.erb +10 -0
  94. data/app/views/kiso/components/combobox/_content.html.erb +9 -0
  95. data/app/views/kiso/components/combobox/_empty.html.erb +9 -0
  96. data/app/views/kiso/components/combobox/_group.html.erb +8 -0
  97. data/app/views/kiso/components/combobox/_input.html.erb +23 -0
  98. data/app/views/kiso/components/combobox/_item.html.erb +19 -0
  99. data/app/views/kiso/components/combobox/_label.html.erb +7 -0
  100. data/app/views/kiso/components/combobox/_list.html.erb +10 -0
  101. data/app/views/kiso/components/combobox/_separator.html.erb +6 -0
  102. data/app/views/kiso/components/command/_dialog.html.erb +11 -0
  103. data/app/views/kiso/components/command/_empty.html.erb +9 -0
  104. data/app/views/kiso/components/command/_group.html.erb +14 -0
  105. data/app/views/kiso/components/command/_input.html.erb +16 -0
  106. data/app/views/kiso/components/command/_item.html.erb +13 -0
  107. data/app/views/kiso/components/command/_list.html.erb +10 -0
  108. data/app/views/kiso/components/command/_separator.html.erb +7 -0
  109. data/app/views/kiso/components/command/_shortcut.html.erb +7 -0
  110. data/app/views/kiso/components/dashboard_navbar/_toggle.html.erb +11 -0
  111. data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +12 -0
  112. data/app/views/kiso/components/dashboard_sidebar/_footer.html.erb +7 -0
  113. data/app/views/kiso/components/dashboard_sidebar/_header.html.erb +7 -0
  114. data/app/views/kiso/components/dashboard_sidebar/_toggle.html.erb +11 -0
  115. data/app/views/kiso/components/dashboard_toolbar/_left.html.erb +7 -0
  116. data/app/views/kiso/components/dashboard_toolbar/_right.html.erb +7 -0
  117. data/app/views/kiso/components/dropdown_menu/_checkbox_item.html.erb +18 -0
  118. data/app/views/kiso/components/dropdown_menu/_content.html.erb +10 -0
  119. data/app/views/kiso/components/dropdown_menu/_group.html.erb +8 -0
  120. data/app/views/kiso/components/dropdown_menu/_item.html.erb +15 -0
  121. data/app/views/kiso/components/dropdown_menu/_label.html.erb +8 -0
  122. data/app/views/kiso/components/dropdown_menu/_radio_group.html.erb +10 -0
  123. data/app/views/kiso/components/dropdown_menu/_radio_item.html.erb +19 -0
  124. data/app/views/kiso/components/dropdown_menu/_separator.html.erb +6 -0
  125. data/app/views/kiso/components/dropdown_menu/_shortcut.html.erb +7 -0
  126. data/app/views/kiso/components/dropdown_menu/_sub.html.erb +8 -0
  127. data/app/views/kiso/components/dropdown_menu/_sub_content.html.erb +10 -0
  128. data/app/views/kiso/components/dropdown_menu/_sub_trigger.html.erb +12 -0
  129. data/app/views/kiso/components/dropdown_menu/_trigger.html.erb +9 -0
  130. data/app/views/kiso/components/empty/_content.html.erb +7 -0
  131. data/app/views/kiso/components/empty/_description.html.erb +7 -0
  132. data/app/views/kiso/components/empty/_header.html.erb +7 -0
  133. data/app/views/kiso/components/empty/_media.html.erb +7 -0
  134. data/app/views/kiso/components/empty/_title.html.erb +7 -0
  135. data/app/views/kiso/components/field/_content.html.erb +7 -0
  136. data/app/views/kiso/components/field/_description.html.erb +7 -0
  137. data/app/views/kiso/components/field/_error.html.erb +22 -0
  138. data/app/views/kiso/components/field/_label.html.erb +5 -0
  139. data/app/views/kiso/components/field/_separator.html.erb +15 -0
  140. data/app/views/kiso/components/field/_title.html.erb +7 -0
  141. data/app/views/kiso/components/field_set/_legend.html.erb +9 -0
  142. data/app/views/kiso/components/input_group/_addon.html.erb +7 -0
  143. data/app/views/kiso/components/input_otp/_group.html.erb +7 -0
  144. data/app/views/kiso/components/input_otp/_separator.html.erb +8 -0
  145. data/app/views/kiso/components/input_otp/_slot.html.erb +11 -0
  146. data/app/views/kiso/components/kbd/_group.html.erb +7 -0
  147. data/app/views/kiso/components/nav/_item.html.erb +15 -0
  148. data/app/views/kiso/components/nav/_section.html.erb +37 -0
  149. data/app/views/kiso/components/nav/_section_title.html.erb +7 -0
  150. data/app/views/kiso/components/pagination/_content.html.erb +7 -0
  151. data/app/views/kiso/components/pagination/_ellipsis.html.erb +9 -0
  152. data/app/views/kiso/components/pagination/_item.html.erb +7 -0
  153. data/app/views/kiso/components/pagination/_link.html.erb +9 -0
  154. data/app/views/kiso/components/pagination/_next.html.erb +12 -0
  155. data/app/views/kiso/components/pagination/_previous.html.erb +12 -0
  156. data/app/views/kiso/components/popover/_anchor.html.erb +8 -0
  157. data/app/views/kiso/components/popover/_content.html.erb +11 -0
  158. data/app/views/kiso/components/popover/_description.html.erb +7 -0
  159. data/app/views/kiso/components/popover/_header.html.erb +7 -0
  160. data/app/views/kiso/components/popover/_title.html.erb +7 -0
  161. data/app/views/kiso/components/popover/_trigger.html.erb +9 -0
  162. data/app/views/kiso/components/radio_group/_item.html.erb +6 -0
  163. data/app/views/kiso/components/select/_content.html.erb +10 -0
  164. data/app/views/kiso/components/select/_group.html.erb +8 -0
  165. data/app/views/kiso/components/select/_item.html.erb +19 -0
  166. data/app/views/kiso/components/select/_label.html.erb +7 -0
  167. data/app/views/kiso/components/select/_separator.html.erb +6 -0
  168. data/app/views/kiso/components/select/_trigger.html.erb +13 -0
  169. data/app/views/kiso/components/select/_value.html.erb +11 -0
  170. data/app/views/kiso/components/stats_card/_description.html.erb +1 -1
  171. data/app/views/kiso/components/stats_card/_header.html.erb +1 -1
  172. data/app/views/kiso/components/stats_card/_label.html.erb +1 -1
  173. data/app/views/kiso/components/stats_card/_value.html.erb +1 -1
  174. data/app/views/kiso/components/table/_body.html.erb +1 -1
  175. data/app/views/kiso/components/table/_caption.html.erb +1 -1
  176. data/app/views/kiso/components/table/_cell.html.erb +1 -1
  177. data/app/views/kiso/components/table/_footer.html.erb +1 -1
  178. data/app/views/kiso/components/table/_head.html.erb +1 -1
  179. data/app/views/kiso/components/table/_header.html.erb +1 -1
  180. data/app/views/kiso/components/table/_row.html.erb +1 -1
  181. data/app/views/kiso/components/toggle_group/_item.html.erb +13 -0
  182. data/config/deploy.docs.yml +31 -0
  183. data/config/deploy.yml +34 -0
  184. data/config/importmap.rb +10 -0
  185. data/lib/kiso/cli/base.rb +15 -0
  186. data/lib/kiso/cli/icons.rb +2 -1
  187. data/lib/kiso/cli/main.rb +6 -0
  188. data/lib/kiso/cli/make.rb +22 -12
  189. data/lib/kiso/configuration.rb +54 -0
  190. data/lib/kiso/engine.rb +36 -1
  191. data/lib/kiso/theme_overrides.rb +130 -0
  192. data/lib/kiso/themes/alert.rb +16 -1
  193. data/lib/kiso/themes/avatar.rb +53 -0
  194. data/lib/kiso/themes/badge.rb +15 -5
  195. data/lib/kiso/themes/breadcrumb.rb +44 -0
  196. data/lib/kiso/themes/button.rb +15 -2
  197. data/lib/kiso/themes/card.rb +18 -2
  198. data/lib/kiso/themes/checkbox.rb +33 -0
  199. data/lib/kiso/themes/color_mode_button.rb +15 -0
  200. data/lib/kiso/themes/color_mode_select.rb +7 -0
  201. data/lib/kiso/themes/combobox.rb +97 -0
  202. data/lib/kiso/themes/command.rb +79 -0
  203. data/lib/kiso/themes/dashboard.rb +51 -0
  204. data/lib/kiso/themes/dropdown_menu.rb +108 -0
  205. data/lib/kiso/themes/empty.rb +54 -0
  206. data/lib/kiso/themes/field.rb +76 -0
  207. data/lib/kiso/themes/field_group.rb +15 -0
  208. data/lib/kiso/themes/field_set.rb +32 -0
  209. data/lib/kiso/themes/input.rb +33 -0
  210. data/lib/kiso/themes/input_group.rb +39 -0
  211. data/lib/kiso/themes/input_otp.rb +46 -0
  212. data/lib/kiso/themes/kbd.rb +31 -0
  213. data/lib/kiso/themes/label.rb +16 -0
  214. data/lib/kiso/themes/nav.rb +27 -0
  215. data/lib/kiso/themes/pagination.rb +73 -0
  216. data/lib/kiso/themes/popover.rb +32 -0
  217. data/lib/kiso/themes/radio_group.rb +43 -0
  218. data/lib/kiso/themes/select.rb +78 -0
  219. data/lib/kiso/themes/select_native.rb +49 -0
  220. data/lib/kiso/themes/separator.rb +8 -2
  221. data/lib/kiso/themes/shared.rb +51 -0
  222. data/lib/kiso/themes/stats_card.rb +26 -14
  223. data/lib/kiso/themes/switch.rb +56 -0
  224. data/lib/kiso/themes/table.rb +18 -15
  225. data/lib/kiso/themes/textarea.rb +33 -0
  226. data/lib/kiso/themes/toggle.rb +71 -0
  227. data/lib/kiso/themes/toggle_group.rb +13 -0
  228. data/lib/kiso/version.rb +4 -1
  229. data/lib/kiso.rb +70 -2
  230. metadata +183 -22
  231. data/app/views/kiso/components/empty_state/_content.html.erb +0 -7
  232. data/app/views/kiso/components/empty_state/_description.html.erb +0 -7
  233. data/app/views/kiso/components/empty_state/_header.html.erb +0 -7
  234. data/app/views/kiso/components/empty_state/_media.html.erb +0 -7
  235. data/app/views/kiso/components/empty_state/_title.html.erb +0 -7
  236. data/lib/kiso/themes/empty_state.rb +0 -42
@@ -1,7 +1,7 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
2
  <%= content_tag :thead,
3
3
  class: Kiso::Themes::TableHeader.render(class: css_classes),
4
- data: { component: :table, table_part: :header },
4
+ data: kiso_prepare_options(component_options, slot: "table-header"),
5
5
  **component_options do %>
6
6
  <%= yield %>
7
7
  <% end %>
@@ -1,7 +1,7 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
2
  <%= content_tag :tr,
3
3
  class: Kiso::Themes::TableRow.render(class: css_classes),
4
- data: { component: :table, table_part: :row },
4
+ data: kiso_prepare_options(component_options, slot: "table-row"),
5
5
  **component_options do %>
6
6
  <%= yield %>
7
7
  <% end %>
@@ -0,0 +1,13 @@
1
+ <%# locals: (value: nil, variant: :default, size: :default, pressed: false, css_classes: "", **component_options) %>
2
+ <% component_options[:type] ||= :button
3
+ component_options[:"aria-pressed"] = pressed %>
4
+ <%= content_tag :button,
5
+ class: Kiso::Themes::ToggleGroupItem.render(variant: variant, size: size, class: css_classes),
6
+ data: kiso_prepare_options(component_options, slot: "toggle-group-item",
7
+ state: pressed ? "on" : "off",
8
+ value: value,
9
+ kiso__toggle_group_target: "item",
10
+ action: "click->kiso--toggle-group#toggle"),
11
+ **component_options do %>
12
+ <%= yield %>
13
+ <% end %>
@@ -0,0 +1,31 @@
1
+ service: kiso-docs
2
+ image: steveclarke/kiso-docs
3
+
4
+ servers:
5
+ web:
6
+ hosts:
7
+ - <%= ENV["DEPLOY_HOST"] %>
8
+
9
+ proxy:
10
+ ssl: true
11
+ host: kisoui.com
12
+ app_port: 80
13
+
14
+ registry:
15
+ server: localhost:5555
16
+
17
+ env:
18
+ clear: {}
19
+ secret: []
20
+
21
+ builder:
22
+ arch: amd64
23
+ context: docs
24
+ dockerfile: docs/Dockerfile
25
+
26
+ ssh:
27
+ user: deploy
28
+
29
+ aliases:
30
+ shell: app exec --interactive --reuse "sh"
31
+ logs: app logs -f
data/config/deploy.yml ADDED
@@ -0,0 +1,34 @@
1
+ service: kiso-lookbook
2
+ image: steveclarke/kiso-lookbook
3
+
4
+ servers:
5
+ web:
6
+ hosts:
7
+ - <%= ENV["DEPLOY_HOST"] %>
8
+
9
+ proxy:
10
+ ssl: true
11
+ host: lookbook.kisoui.com
12
+ app_port: 3000
13
+
14
+ registry:
15
+ server: localhost:5555
16
+
17
+ env:
18
+ clear:
19
+ RAILS_ENV: production
20
+ RAILS_LOG_TO_STDOUT: true
21
+ RAILS_SERVE_STATIC_FILES: true
22
+ secret:
23
+ - RAILS_MASTER_KEY
24
+
25
+ builder:
26
+ arch: amd64
27
+
28
+ ssh:
29
+ user: deploy
30
+
31
+ aliases:
32
+ console: app exec --interactive --reuse "cd lookbook && bin/rails console"
33
+ shell: app exec --interactive --reuse "bash"
34
+ logs: app logs -f
@@ -0,0 +1,10 @@
1
+ pin_all_from Kiso::Engine.root.join("app/javascript/controllers"),
2
+ under: "controllers"
3
+
4
+ pin_all_from Kiso::Engine.root.join("app/javascript/kiso/utils"),
5
+ under: "kiso-ui/utils",
6
+ to: "kiso/utils"
7
+
8
+ # Floating UI — vendored browser ESM builds for smart positioning
9
+ pin "@floating-ui/core", to: "kiso/vendor/floating-ui-core.js"
10
+ pin "@floating-ui/dom", to: "kiso/vendor/floating-ui-dom.js"
data/lib/kiso/cli/base.rb CHANGED
@@ -2,15 +2,25 @@
2
2
 
3
3
  require "fileutils"
4
4
 
5
+ # Base class for all Kiso CLI commands.
6
+ #
7
+ # Provides file generation helpers used by subcommands like {Make}.
5
8
  class Kiso::Cli::Base < Thor
6
9
  def self.exit_on_failure? = true
7
10
 
8
11
  private
9
12
 
13
+ # @return [String] absolute path to the kiso gem root directory
10
14
  def gem_root
11
15
  File.expand_path("../../..", __dir__)
12
16
  end
13
17
 
18
+ # Creates a file at +path+ (relative to gem root) with +content+.
19
+ # Skips if the file already exists.
20
+ #
21
+ # @param path [String] relative path from gem root
22
+ # @param content [String] file contents to write
23
+ # @return [Boolean] +true+ if created, +false+ if skipped
14
24
  def write_file(path, content)
15
25
  full_path = File.join(gem_root, path)
16
26
 
@@ -25,6 +35,11 @@ class Kiso::Cli::Base < Thor
25
35
  true
26
36
  end
27
37
 
38
+ # Appends +line+ to an existing file unless it already contains that line.
39
+ #
40
+ # @param path [String] relative path from gem root
41
+ # @param line [String] the line to append
42
+ # @return [void]
28
43
  def append_to_file(path, line)
29
44
  full_path = File.join(gem_root, path)
30
45
  content = File.read(full_path)
@@ -2,5 +2,6 @@
2
2
 
3
3
  require "kiso/icons/commands"
4
4
 
5
- # Delegate bin/kiso icons kiso-icons gem's Commands class
5
+ # Delegates +bin/kiso icons+ subcommands to the kiso-icons gem's Commands class.
6
+ # @see https://github.com/steveclarke/kiso-icons
6
7
  Kiso::Cli::Icons = Kiso::Icons::Commands
data/lib/kiso/cli/main.rb CHANGED
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Top-level CLI entry point. Routes to subcommands ({Icons}, {Make}).
4
+ #
5
+ # @example
6
+ # $ bin/kiso version
7
+ # $ bin/kiso make component alert
8
+ # $ bin/kiso icons list
3
9
  class Kiso::Cli::Main < Kiso::Cli::Base
4
10
  desc "version", "Show Kiso version"
5
11
  def version
data/lib/kiso/cli/make.rb CHANGED
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Generates scaffolding for new Kiso components.
4
+ #
5
+ # Creates theme module, ERB partial, Lookbook preview, and updates
6
+ # the require list and skill reference docs.
7
+ #
8
+ # @example
9
+ # $ bin/kiso make component alert
10
+ # $ bin/kiso make component toggle_group --no-colored
3
11
  class Kiso::Cli::Make < Kiso::Cli::Base
4
12
  desc "component NAME", "Generate a new Kiso component"
5
13
  long_desc <<~DESC
@@ -99,7 +107,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
99
107
  **Defaults:** `color: :primary, variant: :soft, size: :md`
100
108
 
101
109
  ```erb
102
- <%%= kiso(:#{@name}, color: :primary, variant: :soft) { "Label" } %>
110
+ <%%= kui(:#{@name}, color: :primary, variant: :soft) { "Label" } %>
103
111
  ```
104
112
 
105
113
  **Theme module:** `Kiso::Themes::#{@class_name}` (`lib/kiso/themes/#{@name}.rb`)
@@ -114,7 +122,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
114
122
  **Defaults:** `variant: :default, size: :md`
115
123
 
116
124
  ```erb
117
- <%%= kiso(:#{@name}) { "Label" } %>
125
+ <%%= kui(:#{@name}) { "Label" } %>
118
126
  ```
119
127
 
120
128
  **Theme module:** `Kiso::Themes::#{@class_name}` (`lib/kiso/themes/#{@name}.rb`)
@@ -155,7 +163,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
155
163
  { color: :info, variant: :solid, class: "bg-info text-info-foreground" },
156
164
  { color: :warning, variant: :solid, class: "bg-warning text-warning-foreground" },
157
165
  { color: :error, variant: :solid, class: "bg-error text-error-foreground" },
158
- { color: :neutral, variant: :solid, class: "bg-inverted text-inverted" },
166
+ { color: :neutral, variant: :solid, class: "bg-inverted text-inverted-foreground" },
159
167
 
160
168
  # -- outline --
161
169
  { color: :primary, variant: :outline, class: "text-primary ring-primary/50" },
@@ -173,7 +181,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
173
181
  { color: :info, variant: :soft, class: "bg-info/10 text-info" },
174
182
  { color: :warning, variant: :soft, class: "bg-warning/10 text-warning" },
175
183
  { color: :error, variant: :soft, class: "bg-error/10 text-error" },
176
- { color: :neutral, variant: :soft, class: "text-foreground bg-muted" },
184
+ { color: :neutral, variant: :soft, class: "text-foreground bg-elevated" },
177
185
 
178
186
  # -- subtle --
179
187
  { color: :primary, variant: :subtle, class: "bg-primary/10 text-primary ring-primary/25" },
@@ -182,7 +190,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
182
190
  { color: :info, variant: :subtle, class: "bg-info/10 text-info ring-info/25" },
183
191
  { color: :warning, variant: :subtle, class: "bg-warning/10 text-warning ring-warning/25" },
184
192
  { color: :error, variant: :subtle, class: "bg-error/10 text-error ring-error/25" },
185
- { color: :neutral, variant: :subtle, class: "ring-accented text-foreground bg-muted" }
193
+ { color: :neutral, variant: :subtle, class: "text-foreground bg-elevated ring-accented" }
186
194
  ],
187
195
  defaults: { color: :primary, variant: :soft, size: :md }
188
196
  )
@@ -217,11 +225,12 @@ class Kiso::Cli::Make < Kiso::Cli::Base
217
225
  # -- Partial templates --
218
226
 
219
227
  def colored_partial_template
228
+ slot = @name.tr("_", "-")
220
229
  <<~ERB
221
230
  <%# locals: (color: :primary, variant: :soft, size: :md, css_classes: "", **component_options) %>
222
231
  <%= content_tag :div,
223
232
  class: Kiso::Themes::#{@class_name}.render(color: color, variant: variant, size: size, class: css_classes),
224
- data: { component: :#{@name} },
233
+ data: kiso_prepare_options(component_options, slot: "#{slot}"),
225
234
  **component_options do %>
226
235
  <%= yield %>
227
236
  <% end %>
@@ -229,11 +238,12 @@ class Kiso::Cli::Make < Kiso::Cli::Base
229
238
  end
230
239
 
231
240
  def simple_partial_template
241
+ slot = @name.tr("_", "-")
232
242
  <<~ERB
233
243
  <%# locals: (variant: :default, size: :md, css_classes: "", **component_options) %>
234
244
  <%= content_tag :div,
235
245
  class: Kiso::Themes::#{@class_name}.render(variant: variant, size: size, class: css_classes),
236
- data: { component: :#{@name} },
246
+ data: kiso_prepare_options(component_options, slot: "#{slot}"),
237
247
  **component_options do %>
238
248
  <%= yield %>
239
249
  <% end %>
@@ -310,13 +320,13 @@ class Kiso::Cli::Make < Kiso::Cli::Base
310
320
  if @colored
311
321
  <<~ERB
312
322
  <div class="flex gap-4 items-center p-8">
313
- <%%= kiso(:#{@name}, color: color, variant: variant, size: size) { text } %>
323
+ <%%= kui(:#{@name}, color: color, variant: variant, size: size) { text } %>
314
324
  </div>
315
325
  ERB
316
326
  else
317
327
  <<~ERB
318
328
  <div class="flex gap-4 items-center p-8">
319
- <%%= kiso(:#{@name}, variant: variant, size: size) { text } %>
329
+ <%%= kui(:#{@name}, variant: variant, size: size) { text } %>
320
330
  </div>
321
331
  ERB
322
332
  end
@@ -324,7 +334,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
324
334
 
325
335
  def colors_template
326
336
  colors = %w[primary secondary success info warning error neutral]
327
- lines = colors.map { |c| " <%%= kiso(:#{@name}, color: :#{c}) { \"#{c.capitalize}\" } %>" }
337
+ lines = colors.map { |c| " <%%= kui(:#{@name}, color: :#{c}) { \"#{c.capitalize}\" } %>" }
328
338
 
329
339
  <<~ERB
330
340
  <div class="flex flex-wrap gap-3 items-center p-8">
@@ -338,7 +348,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
338
348
  colors = %w[primary secondary success info warning error neutral]
339
349
 
340
350
  sections = variants.map do |v|
341
- lines = colors.map { |c| " <%%= kiso(:#{@name}, color: :#{c}, variant: :#{v}) { \"#{c.capitalize}\" } %>" }
351
+ lines = colors.map { |c| " <%%= kui(:#{@name}, color: :#{c}, variant: :#{v}) { \"#{c.capitalize}\" } %>" }
342
352
  <<~SECTION
343
353
  <div>
344
354
  <p class="text-sm text-muted-foreground mb-2">#{v.capitalize}</p>
@@ -358,7 +368,7 @@ class Kiso::Cli::Make < Kiso::Cli::Base
358
368
 
359
369
  def sizes_template
360
370
  sizes = %w[sm md lg]
361
- lines = sizes.map { |s| " <%%= kiso(:#{@name}, size: :#{s}) { \"#{s.capitalize}\" } %>" }
371
+ lines = sizes.map { |s| " <%%= kui(:#{@name}, size: :#{s}) { \"#{s.capitalize}\" } %>" }
362
372
 
363
373
  <<~ERB
364
374
  <div class="flex gap-4 items-center p-8">
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiso
4
+ # Global configuration for the Kiso engine.
5
+ #
6
+ # Access via {Kiso.config} or set values in an initializer:
7
+ #
8
+ # # config/initializers/kiso.rb
9
+ # Kiso.configure do |config|
10
+ # config.icons[:chevron_right] = "heroicons:chevron-right"
11
+ # config.theme[:button] = { base: "rounded-full" }
12
+ # end
13
+ class Configuration
14
+ # @return [Hash{Symbol => String}] semantic icon name to icon identifier mapping.
15
+ # Keys are semantic names used by components (e.g. +:chevron_right+),
16
+ # values are icon identifiers passed to +kiso_icon+ (e.g. +"heroicons:chevron-right"+).
17
+ attr_reader :icons
18
+
19
+ # @return [Hash{Symbol => Hash}] global theme overrides keyed by component name.
20
+ # Keys are snake_case component names (e.g. +:button+, +:card_header+),
21
+ # values are hashes accepted by +ClassVariants::Instance#merge+:
22
+ # +base:+, +variants:+, +compound_variants:+, +defaults:+.
23
+ # Applied once at boot by {ThemeOverrides.apply!}.
24
+ attr_reader :theme
25
+
26
+ def initialize
27
+ @icons = default_icons
28
+ @theme = {}
29
+ end
30
+
31
+ private
32
+
33
+ # @return [Hash{Symbol => String}] the default icon mapping using Lucide icon names
34
+ def default_icons
35
+ {
36
+ chevron_right: "chevron-right",
37
+ chevron_left: "chevron-left",
38
+ chevron_down: "chevron-down",
39
+ check: "check",
40
+ ellipsis: "ellipsis",
41
+ x: "x",
42
+ search: "search",
43
+ circle: "circle",
44
+ sun: "sun",
45
+ moon: "moon",
46
+ monitor: "monitor",
47
+ menu: "menu",
48
+ minus: "minus",
49
+ panel_left_close: "panel-left-close",
50
+ panel_left_open: "panel-left-open"
51
+ }
52
+ end
53
+ end
54
+ end
data/lib/kiso/engine.rb CHANGED
@@ -1,8 +1,18 @@
1
1
  module Kiso
2
+ # Rails engine that integrates Kiso into host applications.
3
+ #
4
+ # Registers initializers for:
5
+ # - ClassVariants merger (TailwindMerge for class deduplication)
6
+ # - Global theme overrides ({ThemeOverrides})
7
+ # - View helpers ({ComponentHelper}, {IconHelper})
8
+ # - Importmap and asset pipeline paths
9
+ # - Lookbook preview path registration
2
10
  class Engine < ::Rails::Engine
3
11
  isolate_namespace Kiso
4
- engine_name "kiso_engine"
5
12
 
13
+ # Configures ClassVariants to use TailwindMerge for class deduplication.
14
+ # This ensures conflicting Tailwind utilities are resolved correctly
15
+ # when merging base, variant, and override classes.
6
16
  initializer "kiso.class_variants" do
7
17
  ClassVariants.configure do |config|
8
18
  merger = TailwindMerge::Merger.new
@@ -10,13 +20,38 @@ module Kiso
10
20
  end
11
21
  end
12
22
 
23
+ # Applies global theme overrides from {Configuration#theme} to
24
+ # theme constants. Runs after app initializers so the host app's
25
+ # +config/initializers/kiso.rb+ has populated +config.theme+.
26
+ initializer "kiso.theme_overrides", after: :load_config_initializers do
27
+ Kiso::ThemeOverrides.apply!
28
+ end
29
+
30
+ # Makes {ComponentHelper} and {IconHelper} available in all views.
13
31
  initializer "kiso.helpers" do
14
32
  ActiveSupport.on_load(:action_view) do
15
33
  include Kiso::ComponentHelper
16
34
  include Kiso::IconHelper
35
+ include Kiso::ThemeHelper
36
+ end
37
+ end
38
+
39
+ # Appends Kiso's importmap config and JavaScript assets for importmap-rails apps.
40
+ initializer "kiso.importmap", before: "importmap" do |app|
41
+ if app.config.respond_to?(:importmap)
42
+ app.config.importmap.paths << root.join("config/importmap.rb")
43
+ app.config.importmap.cache_sweepers << root.join("app/javascript")
44
+ end
45
+ end
46
+
47
+ # Appends Kiso's JavaScript directory to the asset pipeline (Propshaft/Sprockets).
48
+ initializer "kiso.assets" do |app|
49
+ if app.config.respond_to?(:assets)
50
+ app.config.assets.paths << root.join("app/javascript")
17
51
  end
18
52
  end
19
53
 
54
+ # Registers Kiso's component previews with Lookbook when available.
20
55
  initializer "kiso.lookbook", after: :load_config_initializers do
21
56
  if defined?(Lookbook)
22
57
  Lookbook.config.preview_paths << root.join("test/components/previews").to_s
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiso
4
+ # Applies global theme overrides from {Configuration#theme} to
5
+ # the corresponding {Themes} constants at boot time.
6
+ #
7
+ # Each override hash is passed to +ClassVariants::Instance#merge+,
8
+ # which appends base/variant classes (TailwindMerge resolves conflicts
9
+ # at render time) and replaces defaults via +Hash#merge!+.
10
+ #
11
+ # @example Overriding button defaults
12
+ # Kiso.configure do |config|
13
+ # config.theme[:button] = { base: "rounded-full", defaults: { variant: :outline } }
14
+ # end
15
+ # # At boot, ThemeOverrides.apply! merges these into Kiso::Themes::Button
16
+ #
17
+ # @see Configuration#theme
18
+ module ThemeOverrides
19
+ class << self
20
+ # Apply all theme overrides to theme constants.
21
+ # Called once during the +kiso.theme_overrides+ engine initializer.
22
+ # Guarded against double application.
23
+ #
24
+ # @raise [ArgumentError] if any key does not map to a valid theme constant
25
+ # @return [void]
26
+ def apply!
27
+ return if @applied
28
+
29
+ overrides = Kiso.config.theme
30
+ return if overrides.empty?
31
+
32
+ validate_keys!(overrides.keys)
33
+
34
+ overrides.each do |key, options|
35
+ resolve_constant(key).merge(**options)
36
+ end
37
+
38
+ @applied = true
39
+ end
40
+
41
+ # Clears the applied flag so {.apply!} can run again.
42
+ # Used in test teardown.
43
+ #
44
+ # @return [void]
45
+ def reset!
46
+ @applied = false
47
+ end
48
+
49
+ private
50
+
51
+ # Maps a snake_case symbol to its PascalCase theme constant.
52
+ #
53
+ # @param key [Symbol] e.g. +:card_header+
54
+ # @return [ClassVariants::Instance] e.g. +Kiso::Themes::CardHeader+
55
+ def resolve_constant(key)
56
+ Kiso::Themes.const_get(camelize(key))
57
+ end
58
+
59
+ # Validates that all keys map to ClassVariants::Instance constants.
60
+ #
61
+ # @param keys [Array<Symbol>]
62
+ # @raise [ArgumentError] with typo suggestion for invalid keys
63
+ def validate_keys!(keys)
64
+ keys.each do |key|
65
+ name = camelize(key)
66
+ unless Kiso::Themes.const_defined?(name) &&
67
+ Kiso::Themes.const_get(name).is_a?(ClassVariants::Instance)
68
+ suggestion = find_closest(key)
69
+ msg = "Unknown theme key :#{key}."
70
+ msg += " Did you mean :#{suggestion}?" if suggestion
71
+ raise ArgumentError, msg
72
+ end
73
+ end
74
+ end
75
+
76
+ # Converts a snake_case symbol to PascalCase string.
77
+ #
78
+ # @param key [Symbol]
79
+ # @return [String]
80
+ def camelize(key)
81
+ key.to_s.split("_").map(&:capitalize).join
82
+ end
83
+
84
+ # Finds the closest valid theme key by edit distance.
85
+ #
86
+ # @param key [Symbol]
87
+ # @return [Symbol, nil] closest match within distance 3, or nil
88
+ def find_closest(key)
89
+ valid = valid_theme_keys
90
+ closest = valid.min_by { |vk| levenshtein(key.to_s, vk.to_s) }
91
+ closest if closest && levenshtein(key.to_s, closest.to_s) <= 3
92
+ end
93
+
94
+ # Lists all valid snake_case theme keys.
95
+ #
96
+ # @return [Array<Symbol>]
97
+ def valid_theme_keys
98
+ Kiso::Themes.constants
99
+ .select { |name| Kiso::Themes.const_get(name).is_a?(ClassVariants::Instance) }
100
+ .map { |name| name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym }
101
+ .sort
102
+ end
103
+
104
+ # Minimal Levenshtein distance for typo detection.
105
+ #
106
+ # @param a [String]
107
+ # @param b [String]
108
+ # @return [Integer]
109
+ def levenshtein(a, b)
110
+ m = a.length
111
+ n = b.length
112
+ d = Array.new(m + 1) { |i| i }
113
+ (1..n).each do |j|
114
+ prev = d[0]
115
+ d[0] = j
116
+ (1..m).each do |i|
117
+ temp = d[i]
118
+ d[i] = if a[i - 1] == b[j - 1]
119
+ prev
120
+ else
121
+ [d[i] + 1, d[i - 1] + 1, prev + 1].min
122
+ end
123
+ prev = temp
124
+ end
125
+ end
126
+ d[m]
127
+ end
128
+ end
129
+ end
130
+ end
@@ -1,5 +1,18 @@
1
1
  module Kiso
2
2
  module Themes
3
+ # Contextual alert banner with optional icon, title, and description.
4
+ #
5
+ # Uses CSS Grid with +has-[>svg]+ to auto-allocate a column for the icon
6
+ # when an SVG is present as a direct child.
7
+ #
8
+ # @example
9
+ # Alert.render(color: :error, variant: :soft)
10
+ #
11
+ # Variants:
12
+ # - +color+ — :primary (default), :secondary, :success, :info, :warning, :error, :neutral
13
+ # - +variant+ — :solid, :outline, :soft (default), :subtle
14
+ #
15
+ # Sub-parts: {AlertTitle}, {AlertDescription}
3
16
  Alert = ClassVariants.build(
4
17
  base: "relative w-full rounded-lg px-4 py-3 text-sm " \
5
18
  "grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] " \
@@ -54,12 +67,14 @@ module Kiso
54
67
  defaults: {color: :primary, variant: :soft}
55
68
  )
56
69
 
70
+ # Alert title text. Rendered in the second grid column (after the icon column).
57
71
  AlertTitle = ClassVariants.build(
58
72
  base: "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight"
59
73
  )
60
74
 
75
+ # Alert body text. Inherits parent text color for contrast on colored backgrounds.
61
76
  AlertDescription = ClassVariants.build(
62
- base: "col-start-2 grid justify-items-start gap-1 text-sm opacity-90 [&_p]:leading-relaxed"
77
+ base: "col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed"
63
78
  )
64
79
  end
65
80
  end
@@ -0,0 +1,53 @@
1
+ module Kiso
2
+ module Themes
3
+ Avatar = ClassVariants.build(
4
+ base: "group/avatar relative flex shrink-0 rounded-full select-none items-center justify-center bg-muted",
5
+ variants: {
6
+ size: {
7
+ sm: "size-6",
8
+ md: "size-8",
9
+ lg: "size-10"
10
+ }
11
+ },
12
+ defaults: {size: :md}
13
+ )
14
+
15
+ AvatarImage = ClassVariants.build(
16
+ base: "absolute inset-0 aspect-square size-full rounded-full object-cover"
17
+ )
18
+
19
+ AvatarFallback = ClassVariants.build(
20
+ base: "flex size-full items-center justify-center rounded-full bg-muted text-muted-foreground font-medium",
21
+ variants: {
22
+ size: {
23
+ sm: "text-xs",
24
+ md: "text-sm",
25
+ lg: "text-base"
26
+ }
27
+ },
28
+ defaults: {size: :md}
29
+ )
30
+
31
+ AvatarBadge = ClassVariants.build(
32
+ base: "bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 " \
33
+ "inline-flex items-center justify-center rounded-full ring-2 select-none " \
34
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden " \
35
+ "group-data-[size=md]/avatar:size-2.5 group-data-[size=md]/avatar:[&>svg]:size-2 " \
36
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2"
37
+ )
38
+
39
+ AvatarGroup = ClassVariants.build(
40
+ base: "*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 " \
41
+ "*:data-[slot=avatar]:ring-2"
42
+ )
43
+
44
+ AvatarGroupCount = ClassVariants.build(
45
+ base: "bg-muted text-muted-foreground ring-background relative flex size-8 shrink-0 " \
46
+ "items-center justify-center rounded-full text-sm ring-2 " \
47
+ "group-has-data-[size=lg]/avatar-group:size-10 " \
48
+ "group-has-data-[size=sm]/avatar-group:size-6 " \
49
+ "[&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 " \
50
+ "group-has-data-[size=sm]/avatar-group:[&>svg]:size-3"
51
+ )
52
+ end
53
+ end
@@ -1,7 +1,17 @@
1
1
  module Kiso
2
2
  module Themes
3
+ # @return [Array<Symbol>] the seven semantic colors available to all colored components
3
4
  COLORS = %i[primary secondary success info warning error neutral].freeze
4
5
 
6
+ # Inline badge for status labels, counts, and tags.
7
+ #
8
+ # @example
9
+ # Badge.render(color: :success, variant: :soft, size: :md)
10
+ #
11
+ # Variants:
12
+ # - +color+ — :primary (default), :secondary, :success, :info, :warning, :error, :neutral
13
+ # - +variant+ — :solid, :outline, :soft (default), :subtle
14
+ # - +size+ — :xs, :sm, :md (default), :lg, :xl
5
15
  Badge = ClassVariants.build(
6
16
  base: "inline-flex items-center justify-center font-medium whitespace-nowrap shrink-0 overflow-hidden " \
7
17
  "transition-[color,box-shadow] " \
@@ -15,11 +25,11 @@ module Kiso
15
25
  subtle: "ring ring-inset"
16
26
  },
17
27
  size: {
18
- xs: "px-1 py-0.5 text-[8px]/3 rounded-full gap-0.5 [&>svg]:size-2.5",
19
- sm: "px-1.5 py-0.5 text-[10px]/3 rounded-full gap-0.5 [&>svg]:size-3",
20
- md: "px-2 py-0.5 text-xs rounded-full gap-1 [&>svg]:size-3",
21
- lg: "px-2.5 py-1 text-sm rounded-full gap-1 [&>svg]:size-3.5",
22
- xl: "px-2.5 py-1 text-base rounded-full gap-1.5 [&>svg]:size-4"
28
+ xs: "px-2 py-0.5 text-xs rounded-full gap-1 [&>svg]:size-3",
29
+ sm: "px-2.5 py-0.5 text-xs rounded-full gap-1 [&>svg]:size-3",
30
+ md: "px-3 py-1 text-xs rounded-full gap-1.5 [&>svg]:size-3.5",
31
+ lg: "px-3.5 py-1 text-sm rounded-full gap-1.5 [&>svg]:size-4",
32
+ xl: "px-4 py-1.5 text-sm rounded-full gap-2 [&>svg]:size-4"
23
33
  },
24
34
  color: COLORS.index_with { "" }
25
35
  },