phlex_kit 0.2.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.
Files changed (405) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.md +135 -0
  4. data/app/assets/stylesheets/phlex_kit/_tokens.css +91 -0
  5. data/app/assets/stylesheets/phlex_kit/phlex_kit.css +82 -0
  6. data/app/components/phlex_kit/accordion/accordion.css +14 -0
  7. data/app/components/phlex_kit/accordion/accordion.rb +10 -0
  8. data/app/components/phlex_kit/accordion/accordion_content.rb +8 -0
  9. data/app/components/phlex_kit/accordion/accordion_default_content.rb +6 -0
  10. data/app/components/phlex_kit/accordion/accordion_default_trigger.rb +12 -0
  11. data/app/components/phlex_kit/accordion/accordion_icon.rb +16 -0
  12. data/app/components/phlex_kit/accordion/accordion_item.rb +12 -0
  13. data/app/components/phlex_kit/accordion/accordion_trigger.rb +8 -0
  14. data/app/components/phlex_kit/alert/alert.css +32 -0
  15. data/app/components/phlex_kit/alert/alert.rb +37 -0
  16. data/app/components/phlex_kit/alert/alert_description.rb +12 -0
  17. data/app/components/phlex_kit/alert/alert_title.rb +12 -0
  18. data/app/components/phlex_kit/alert_dialog/alert_dialog.css +37 -0
  19. data/app/components/phlex_kit/alert_dialog/alert_dialog.rb +20 -0
  20. data/app/components/phlex_kit/alert_dialog/alert_dialog_action.rb +14 -0
  21. data/app/components/phlex_kit/alert_dialog/alert_dialog_cancel.rb +13 -0
  22. data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +19 -0
  23. data/app/components/phlex_kit/alert_dialog/alert_dialog_description.rb +12 -0
  24. data/app/components/phlex_kit/alert_dialog/alert_dialog_footer.rb +12 -0
  25. data/app/components/phlex_kit/alert_dialog/alert_dialog_header.rb +12 -0
  26. data/app/components/phlex_kit/alert_dialog/alert_dialog_title.rb +12 -0
  27. data/app/components/phlex_kit/alert_dialog/alert_dialog_trigger.rb +12 -0
  28. data/app/components/phlex_kit/aspect_ratio/aspect_ratio.css +12 -0
  29. data/app/components/phlex_kit/aspect_ratio/aspect_ratio.rb +27 -0
  30. data/app/components/phlex_kit/attachment/attachment.css +62 -0
  31. data/app/components/phlex_kit/attachment/attachment.rb +16 -0
  32. data/app/components/phlex_kit/attachment/attachment_action.rb +24 -0
  33. data/app/components/phlex_kit/attachment/attachment_actions.rb +7 -0
  34. data/app/components/phlex_kit/attachment/attachment_content.rb +7 -0
  35. data/app/components/phlex_kit/attachment/attachment_description.rb +7 -0
  36. data/app/components/phlex_kit/attachment/attachment_media.rb +8 -0
  37. data/app/components/phlex_kit/attachment/attachment_title.rb +7 -0
  38. data/app/components/phlex_kit/avatar/avatar.css +35 -0
  39. data/app/components/phlex_kit/avatar/avatar.rb +25 -0
  40. data/app/components/phlex_kit/avatar/avatar_fallback.rb +13 -0
  41. data/app/components/phlex_kit/avatar/avatar_group.rb +7 -0
  42. data/app/components/phlex_kit/avatar/avatar_image.rb +24 -0
  43. data/app/components/phlex_kit/badge/badge.css +50 -0
  44. data/app/components/phlex_kit/badge/badge.rb +39 -0
  45. data/app/components/phlex_kit/breadcrumb/breadcrumb.css +18 -0
  46. data/app/components/phlex_kit/breadcrumb/breadcrumb.rb +8 -0
  47. data/app/components/phlex_kit/breadcrumb/breadcrumb_ellipsis.rb +15 -0
  48. data/app/components/phlex_kit/breadcrumb/breadcrumb_item.rb +6 -0
  49. data/app/components/phlex_kit/breadcrumb/breadcrumb_link.rb +9 -0
  50. data/app/components/phlex_kit/breadcrumb/breadcrumb_list.rb +6 -0
  51. data/app/components/phlex_kit/breadcrumb/breadcrumb_page.rb +8 -0
  52. data/app/components/phlex_kit/breadcrumb/breadcrumb_separator.rb +10 -0
  53. data/app/components/phlex_kit/bubble/bubble.css +32 -0
  54. data/app/components/phlex_kit/bubble/bubble.rb +17 -0
  55. data/app/components/phlex_kit/bubble/bubble_content.rb +11 -0
  56. data/app/components/phlex_kit/bubble/bubble_group.rb +6 -0
  57. data/app/components/phlex_kit/bubble/bubble_reactions.rb +12 -0
  58. data/app/components/phlex_kit/button/button.css +72 -0
  59. data/app/components/phlex_kit/button/button.rb +51 -0
  60. data/app/components/phlex_kit/button_group/button_group.css +8 -0
  61. data/app/components/phlex_kit/button_group/button_group.rb +14 -0
  62. data/app/components/phlex_kit/calendar/calendar.css +109 -0
  63. data/app/components/phlex_kit/calendar/calendar.rb +47 -0
  64. data/app/components/phlex_kit/calendar/calendar_body.rb +13 -0
  65. data/app/components/phlex_kit/calendar/calendar_days.rb +98 -0
  66. data/app/components/phlex_kit/calendar/calendar_header.rb +13 -0
  67. data/app/components/phlex_kit/calendar/calendar_next.rb +40 -0
  68. data/app/components/phlex_kit/calendar/calendar_prev.rb +40 -0
  69. data/app/components/phlex_kit/calendar/calendar_title.rb +19 -0
  70. data/app/components/phlex_kit/calendar/calendar_weekdays.rb +27 -0
  71. data/app/components/phlex_kit/card/card.css +15 -0
  72. data/app/components/phlex_kit/card/card.rb +29 -0
  73. data/app/components/phlex_kit/card/card_content.rb +12 -0
  74. data/app/components/phlex_kit/card/card_description.rb +12 -0
  75. data/app/components/phlex_kit/card/card_footer.rb +12 -0
  76. data/app/components/phlex_kit/card/card_header.rb +12 -0
  77. data/app/components/phlex_kit/card/card_title.rb +12 -0
  78. data/app/components/phlex_kit/carousel/carousel.css +41 -0
  79. data/app/components/phlex_kit/carousel/carousel.rb +37 -0
  80. data/app/components/phlex_kit/carousel/carousel_content.rb +16 -0
  81. data/app/components/phlex_kit/carousel/carousel_item.rb +17 -0
  82. data/app/components/phlex_kit/carousel/carousel_next.rb +39 -0
  83. data/app/components/phlex_kit/carousel/carousel_previous.rb +40 -0
  84. data/app/components/phlex_kit/chart/chart.css +9 -0
  85. data/app/components/phlex_kit/chart/chart.rb +31 -0
  86. data/app/components/phlex_kit/checkbox/checkbox.css +27 -0
  87. data/app/components/phlex_kit/checkbox/checkbox.rb +26 -0
  88. data/app/components/phlex_kit/clipboard/clipboard.css +8 -0
  89. data/app/components/phlex_kit/clipboard/clipboard.rb +19 -0
  90. data/app/components/phlex_kit/clipboard/clipboard_popover.rb +14 -0
  91. data/app/components/phlex_kit/clipboard/clipboard_source.rb +6 -0
  92. data/app/components/phlex_kit/clipboard/clipboard_trigger.rb +6 -0
  93. data/app/components/phlex_kit/codeblock/codeblock.css +7 -0
  94. data/app/components/phlex_kit/codeblock/codeblock.rb +23 -0
  95. data/app/components/phlex_kit/collapsible/collapsible.css +3 -0
  96. data/app/components/phlex_kit/collapsible/collapsible.rb +13 -0
  97. data/app/components/phlex_kit/collapsible/collapsible_content.rb +8 -0
  98. data/app/components/phlex_kit/collapsible/collapsible_trigger.rb +8 -0
  99. data/app/components/phlex_kit/combobox/combobox.css +310 -0
  100. data/app/components/phlex_kit/combobox/combobox.rb +33 -0
  101. data/app/components/phlex_kit/combobox/combobox_badge.rb +15 -0
  102. data/app/components/phlex_kit/combobox/combobox_badge_trigger.rb +55 -0
  103. data/app/components/phlex_kit/combobox/combobox_checkbox.rb +21 -0
  104. data/app/components/phlex_kit/combobox/combobox_clear_button.rb +39 -0
  105. data/app/components/phlex_kit/combobox/combobox_empty_state.rb +17 -0
  106. data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +69 -0
  107. data/app/components/phlex_kit/combobox/combobox_item.rb +19 -0
  108. data/app/components/phlex_kit/combobox/combobox_item_indicator.rb +27 -0
  109. data/app/components/phlex_kit/combobox/combobox_list.rb +12 -0
  110. data/app/components/phlex_kit/combobox/combobox_list_group.rb +14 -0
  111. data/app/components/phlex_kit/combobox/combobox_popover.rb +27 -0
  112. data/app/components/phlex_kit/combobox/combobox_radio.rb +26 -0
  113. data/app/components/phlex_kit/combobox/combobox_search_input.rb +49 -0
  114. data/app/components/phlex_kit/combobox/combobox_toggle_all_checkbox.rb +21 -0
  115. data/app/components/phlex_kit/combobox/combobox_trigger.rb +48 -0
  116. data/app/components/phlex_kit/command/command.css +104 -0
  117. data/app/components/phlex_kit/command/command.rb +18 -0
  118. data/app/components/phlex_kit/command/command_dialog.rb +19 -0
  119. data/app/components/phlex_kit/command/command_dialog_content.rb +37 -0
  120. data/app/components/phlex_kit/command/command_dialog_trigger.rb +22 -0
  121. data/app/components/phlex_kit/command/command_empty.rb +17 -0
  122. data/app/components/phlex_kit/command/command_group.rb +32 -0
  123. data/app/components/phlex_kit/command/command_input.rb +56 -0
  124. data/app/components/phlex_kit/command/command_item.rb +22 -0
  125. data/app/components/phlex_kit/command/command_list.rb +12 -0
  126. data/app/components/phlex_kit/context_menu/context_menu.css +19 -0
  127. data/app/components/phlex_kit/context_menu/context_menu.rb +11 -0
  128. data/app/components/phlex_kit/context_menu/context_menu_content.rb +8 -0
  129. data/app/components/phlex_kit/context_menu/context_menu_item.rb +25 -0
  130. data/app/components/phlex_kit/context_menu/context_menu_label.rb +11 -0
  131. data/app/components/phlex_kit/context_menu/context_menu_separator.rb +8 -0
  132. data/app/components/phlex_kit/context_menu/context_menu_trigger.rb +8 -0
  133. data/app/components/phlex_kit/data_table/data_table.css +110 -0
  134. data/app/components/phlex_kit/data_table/data_table.rb +25 -0
  135. data/app/components/phlex_kit/data_table/data_table_bulk_actions.rb +15 -0
  136. data/app/components/phlex_kit/data_table/data_table_column_toggle.rb +61 -0
  137. data/app/components/phlex_kit/data_table/data_table_expand_toggle.rb +40 -0
  138. data/app/components/phlex_kit/data_table/data_table_form.rb +36 -0
  139. data/app/components/phlex_kit/data_table/data_table_kaminari_adapter.rb +17 -0
  140. data/app/components/phlex_kit/data_table/data_table_manual_adapter.rb +18 -0
  141. data/app/components/phlex_kit/data_table/data_table_pagination.rb +98 -0
  142. data/app/components/phlex_kit/data_table/data_table_pagination_bar.rb +13 -0
  143. data/app/components/phlex_kit/data_table/data_table_pagy_adapter.rb +17 -0
  144. data/app/components/phlex_kit/data_table/data_table_per_page_select.rb +29 -0
  145. data/app/components/phlex_kit/data_table/data_table_row_checkbox.rb +24 -0
  146. data/app/components/phlex_kit/data_table/data_table_search.rb +51 -0
  147. data/app/components/phlex_kit/data_table/data_table_select_all_checkbox.rb +19 -0
  148. data/app/components/phlex_kit/data_table/data_table_selection_summary.rb +19 -0
  149. data/app/components/phlex_kit/data_table/data_table_sort_head.rb +82 -0
  150. data/app/components/phlex_kit/data_table/data_table_toolbar.rb +13 -0
  151. data/app/components/phlex_kit/date_picker/date_picker.css +28 -0
  152. data/app/components/phlex_kit/date_picker/date_picker.rb +71 -0
  153. data/app/components/phlex_kit/dialog/dialog.css +32 -0
  154. data/app/components/phlex_kit/dialog/dialog.rb +14 -0
  155. data/app/components/phlex_kit/dialog/dialog_content.rb +21 -0
  156. data/app/components/phlex_kit/dialog/dialog_description.rb +6 -0
  157. data/app/components/phlex_kit/dialog/dialog_footer.rb +6 -0
  158. data/app/components/phlex_kit/dialog/dialog_header.rb +6 -0
  159. data/app/components/phlex_kit/dialog/dialog_middle.rb +6 -0
  160. data/app/components/phlex_kit/dialog/dialog_title.rb +6 -0
  161. data/app/components/phlex_kit/dialog/dialog_trigger.rb +8 -0
  162. data/app/components/phlex_kit/drawer/drawer.css +54 -0
  163. data/app/components/phlex_kit/drawer/drawer.rb +18 -0
  164. data/app/components/phlex_kit/drawer/drawer_close.rb +9 -0
  165. data/app/components/phlex_kit/drawer/drawer_content.rb +21 -0
  166. data/app/components/phlex_kit/drawer/drawer_description.rb +7 -0
  167. data/app/components/phlex_kit/drawer/drawer_footer.rb +7 -0
  168. data/app/components/phlex_kit/drawer/drawer_header.rb +7 -0
  169. data/app/components/phlex_kit/drawer/drawer_title.rb +7 -0
  170. data/app/components/phlex_kit/drawer/drawer_trigger.rb +9 -0
  171. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +38 -0
  172. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.rb +22 -0
  173. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_content.rb +23 -0
  174. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_item.rb +22 -0
  175. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_label.rb +12 -0
  176. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_separator.rb +12 -0
  177. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_trigger.rb +15 -0
  178. data/app/components/phlex_kit/empty/empty.css +25 -0
  179. data/app/components/phlex_kit/empty/empty.rb +6 -0
  180. data/app/components/phlex_kit/empty/empty_content.rb +6 -0
  181. data/app/components/phlex_kit/empty/empty_description.rb +6 -0
  182. data/app/components/phlex_kit/empty/empty_header.rb +6 -0
  183. data/app/components/phlex_kit/empty/empty_media.rb +14 -0
  184. data/app/components/phlex_kit/empty/empty_title.rb +6 -0
  185. data/app/components/phlex_kit/form/form.css +15 -0
  186. data/app/components/phlex_kit/form/form.rb +27 -0
  187. data/app/components/phlex_kit/form_field/form_field.css +31 -0
  188. data/app/components/phlex_kit/form_field/form_field.rb +31 -0
  189. data/app/components/phlex_kit/form_field/form_field_error.rb +19 -0
  190. data/app/components/phlex_kit/form_field/form_field_hint.rb +13 -0
  191. data/app/components/phlex_kit/form_field/form_field_label.rb +13 -0
  192. data/app/components/phlex_kit/hover_card/hover_card.css +8 -0
  193. data/app/components/phlex_kit/hover_card/hover_card.rb +10 -0
  194. data/app/components/phlex_kit/hover_card/hover_card_content.rb +8 -0
  195. data/app/components/phlex_kit/hover_card/hover_card_trigger.rb +6 -0
  196. data/app/components/phlex_kit/input/input.css +29 -0
  197. data/app/components/phlex_kit/input/input.rb +34 -0
  198. data/app/components/phlex_kit/input_group/input_group.css +35 -0
  199. data/app/components/phlex_kit/input_group/input_group.rb +15 -0
  200. data/app/components/phlex_kit/input_group/input_group_addon.rb +16 -0
  201. data/app/components/phlex_kit/input_group/input_group_text.rb +7 -0
  202. data/app/components/phlex_kit/input_otp/input_otp.css +32 -0
  203. data/app/components/phlex_kit/input_otp/input_otp.rb +29 -0
  204. data/app/components/phlex_kit/input_otp/input_otp_group.rb +7 -0
  205. data/app/components/phlex_kit/input_otp/input_otp_separator.rb +7 -0
  206. data/app/components/phlex_kit/input_otp/input_otp_slot.rb +27 -0
  207. data/app/components/phlex_kit/item/item.css +32 -0
  208. data/app/components/phlex_kit/item/item.rb +18 -0
  209. data/app/components/phlex_kit/item/item_actions.rb +7 -0
  210. data/app/components/phlex_kit/item/item_content.rb +7 -0
  211. data/app/components/phlex_kit/item/item_description.rb +7 -0
  212. data/app/components/phlex_kit/item/item_group.rb +7 -0
  213. data/app/components/phlex_kit/item/item_media.rb +7 -0
  214. data/app/components/phlex_kit/item/item_title.rb +7 -0
  215. data/app/components/phlex_kit/kbd/kbd.css +17 -0
  216. data/app/components/phlex_kit/kbd/kbd.rb +14 -0
  217. data/app/components/phlex_kit/kbd/kbd_group.rb +12 -0
  218. data/app/components/phlex_kit/label/label.css +12 -0
  219. data/app/components/phlex_kit/label/label.rb +14 -0
  220. data/app/components/phlex_kit/link/link.css +6 -0
  221. data/app/components/phlex_kit/link/link.rb +47 -0
  222. data/app/components/phlex_kit/masked_input/masked_input.rb +12 -0
  223. data/app/components/phlex_kit/menubar/menubar.css +66 -0
  224. data/app/components/phlex_kit/menubar/menubar.rb +24 -0
  225. data/app/components/phlex_kit/menubar/menubar_content.rb +9 -0
  226. data/app/components/phlex_kit/menubar/menubar_item.rb +26 -0
  227. data/app/components/phlex_kit/menubar/menubar_menu.rb +9 -0
  228. data/app/components/phlex_kit/menubar/menubar_separator.rb +7 -0
  229. data/app/components/phlex_kit/menubar/menubar_trigger.rb +14 -0
  230. data/app/components/phlex_kit/message/message.css +20 -0
  231. data/app/components/phlex_kit/message/message.rb +14 -0
  232. data/app/components/phlex_kit/message/message_avatar.rb +6 -0
  233. data/app/components/phlex_kit/message/message_content.rb +6 -0
  234. data/app/components/phlex_kit/message/message_footer.rb +6 -0
  235. data/app/components/phlex_kit/message/message_group.rb +6 -0
  236. data/app/components/phlex_kit/message/message_header.rb +6 -0
  237. data/app/components/phlex_kit/message_scroller/message_scroller.css +2 -0
  238. data/app/components/phlex_kit/message_scroller/message_scroller.rb +11 -0
  239. data/app/components/phlex_kit/native_select/native_select.css +51 -0
  240. data/app/components/phlex_kit/native_select/native_select.rb +48 -0
  241. data/app/components/phlex_kit/native_select/native_select_group.rb +13 -0
  242. data/app/components/phlex_kit/native_select/native_select_icon.rb +32 -0
  243. data/app/components/phlex_kit/native_select/native_select_option.rb +14 -0
  244. data/app/components/phlex_kit/navigation_menu/navigation_menu.css +67 -0
  245. data/app/components/phlex_kit/navigation_menu/navigation_menu.rb +23 -0
  246. data/app/components/phlex_kit/navigation_menu/navigation_menu_content.rb +9 -0
  247. data/app/components/phlex_kit/navigation_menu/navigation_menu_item.rb +9 -0
  248. data/app/components/phlex_kit/navigation_menu/navigation_menu_link.rb +13 -0
  249. data/app/components/phlex_kit/navigation_menu/navigation_menu_list.rb +7 -0
  250. data/app/components/phlex_kit/navigation_menu/navigation_menu_trigger.rb +27 -0
  251. data/app/components/phlex_kit/pagination/pagination.css +5 -0
  252. data/app/components/phlex_kit/pagination/pagination.rb +10 -0
  253. data/app/components/phlex_kit/pagination/pagination_content.rb +6 -0
  254. data/app/components/phlex_kit/pagination/pagination_ellipsis.rb +17 -0
  255. data/app/components/phlex_kit/pagination/pagination_item.rb +14 -0
  256. data/app/components/phlex_kit/popover/popover.css +9 -0
  257. data/app/components/phlex_kit/popover/popover.rb +11 -0
  258. data/app/components/phlex_kit/popover/popover_content.rb +8 -0
  259. data/app/components/phlex_kit/popover/popover_trigger.rb +8 -0
  260. data/app/components/phlex_kit/progress/progress.css +17 -0
  261. data/app/components/phlex_kit/progress/progress.rb +25 -0
  262. data/app/components/phlex_kit/radio_button/radio_button.css +9 -0
  263. data/app/components/phlex_kit/radio_button/radio_button.rb +19 -0
  264. data/app/components/phlex_kit/radio_group/radio_group.css +3 -0
  265. data/app/components/phlex_kit/radio_group/radio_group.rb +14 -0
  266. data/app/components/phlex_kit/resizable/resizable.css +23 -0
  267. data/app/components/phlex_kit/resizable/resizable_handle.rb +21 -0
  268. data/app/components/phlex_kit/resizable/resizable_panel.rb +19 -0
  269. data/app/components/phlex_kit/resizable/resizable_panel_group.rb +26 -0
  270. data/app/components/phlex_kit/scroll_area/scroll_area.css +21 -0
  271. data/app/components/phlex_kit/scroll_area/scroll_area.rb +15 -0
  272. data/app/components/phlex_kit/select/select.css +80 -0
  273. data/app/components/phlex_kit/select/select.rb +43 -0
  274. data/app/components/phlex_kit/select/select_content.rb +24 -0
  275. data/app/components/phlex_kit/select/select_group.rb +13 -0
  276. data/app/components/phlex_kit/select/select_input.rb +23 -0
  277. data/app/components/phlex_kit/select/select_item.rb +52 -0
  278. data/app/components/phlex_kit/select/select_label.rb +13 -0
  279. data/app/components/phlex_kit/select/select_trigger.rb +42 -0
  280. data/app/components/phlex_kit/select/select_value.rb +21 -0
  281. data/app/components/phlex_kit/separator/separator.css +6 -0
  282. data/app/components/phlex_kit/separator/separator.rb +30 -0
  283. data/app/components/phlex_kit/sheet/sheet.css +17 -0
  284. data/app/components/phlex_kit/sheet/sheet.rb +14 -0
  285. data/app/components/phlex_kit/sheet/sheet_content.rb +25 -0
  286. data/app/components/phlex_kit/sheet/sheet_description.rb +6 -0
  287. data/app/components/phlex_kit/sheet/sheet_footer.rb +6 -0
  288. data/app/components/phlex_kit/sheet/sheet_header.rb +6 -0
  289. data/app/components/phlex_kit/sheet/sheet_middle.rb +6 -0
  290. data/app/components/phlex_kit/sheet/sheet_title.rb +6 -0
  291. data/app/components/phlex_kit/sheet/sheet_trigger.rb +6 -0
  292. data/app/components/phlex_kit/shortcut_key/shortcut_key.css +17 -0
  293. data/app/components/phlex_kit/shortcut_key/shortcut_key.rb +9 -0
  294. data/app/components/phlex_kit/sidebar/sidebar.css +42 -0
  295. data/app/components/phlex_kit/sidebar/sidebar.rb +19 -0
  296. data/app/components/phlex_kit/sidebar/sidebar_content.rb +12 -0
  297. data/app/components/phlex_kit/sidebar/sidebar_footer.rb +12 -0
  298. data/app/components/phlex_kit/sidebar/sidebar_group.rb +12 -0
  299. data/app/components/phlex_kit/sidebar/sidebar_header.rb +12 -0
  300. data/app/components/phlex_kit/sidebar/sidebar_inset.rb +14 -0
  301. data/app/components/phlex_kit/sidebar/sidebar_menu.rb +12 -0
  302. data/app/components/phlex_kit/sidebar/sidebar_menu_button.rb +17 -0
  303. data/app/components/phlex_kit/sidebar/sidebar_menu_item.rb +12 -0
  304. data/app/components/phlex_kit/sidebar/sidebar_wrapper.rb +13 -0
  305. data/app/components/phlex_kit/skeleton/skeleton.css +7 -0
  306. data/app/components/phlex_kit/skeleton/skeleton.rb +9 -0
  307. data/app/components/phlex_kit/slider/slider.css +52 -0
  308. data/app/components/phlex_kit/slider/slider.rb +39 -0
  309. data/app/components/phlex_kit/spinner/spinner.css +5 -0
  310. data/app/components/phlex_kit/spinner/spinner.rb +27 -0
  311. data/app/components/phlex_kit/stars/stars.css +4 -0
  312. data/app/components/phlex_kit/stars/stars.rb +19 -0
  313. data/app/components/phlex_kit/switch/switch.css +28 -0
  314. data/app/components/phlex_kit/switch/switch.rb +21 -0
  315. data/app/components/phlex_kit/table/table.css +24 -0
  316. data/app/components/phlex_kit/table/table.rb +35 -0
  317. data/app/components/phlex_kit/table/table_body.rb +12 -0
  318. data/app/components/phlex_kit/table/table_caption.rb +12 -0
  319. data/app/components/phlex_kit/table/table_cell.rb +12 -0
  320. data/app/components/phlex_kit/table/table_footer.rb +12 -0
  321. data/app/components/phlex_kit/table/table_head.rb +12 -0
  322. data/app/components/phlex_kit/table/table_header.rb +12 -0
  323. data/app/components/phlex_kit/table/table_row.rb +12 -0
  324. data/app/components/phlex_kit/tabs/tabs.css +13 -0
  325. data/app/components/phlex_kit/tabs/tabs.rb +13 -0
  326. data/app/components/phlex_kit/tabs/tabs_content.rb +11 -0
  327. data/app/components/phlex_kit/tabs/tabs_list.rb +6 -0
  328. data/app/components/phlex_kit/tabs/tabs_trigger.rb +17 -0
  329. data/app/components/phlex_kit/textarea/textarea.css +27 -0
  330. data/app/components/phlex_kit/textarea/textarea.rb +24 -0
  331. data/app/components/phlex_kit/theme_toggle/theme_toggle.rb +15 -0
  332. data/app/components/phlex_kit/toast/toast.css +163 -0
  333. data/app/components/phlex_kit/toast/toast.rb +21 -0
  334. data/app/components/phlex_kit/toast/toast_action.rb +19 -0
  335. data/app/components/phlex_kit/toast/toast_cancel.rb +18 -0
  336. data/app/components/phlex_kit/toast/toast_close.rb +35 -0
  337. data/app/components/phlex_kit/toast/toast_description.rb +13 -0
  338. data/app/components/phlex_kit/toast/toast_icon.rb +63 -0
  339. data/app/components/phlex_kit/toast/toast_item.rb +70 -0
  340. data/app/components/phlex_kit/toast/toast_region.rb +121 -0
  341. data/app/components/phlex_kit/toast/toast_title.rb +13 -0
  342. data/app/components/phlex_kit/toggle/toggle.css +16 -0
  343. data/app/components/phlex_kit/toggle/toggle.rb +59 -0
  344. data/app/components/phlex_kit/toggle_group/toggle_group.css +10 -0
  345. data/app/components/phlex_kit/toggle_group/toggle_group.rb +65 -0
  346. data/app/components/phlex_kit/toggle_group/toggle_group_item.rb +37 -0
  347. data/app/components/phlex_kit/tooltip/tooltip.css +28 -0
  348. data/app/components/phlex_kit/tooltip/tooltip.rb +15 -0
  349. data/app/components/phlex_kit/tooltip/tooltip_content.rb +12 -0
  350. data/app/components/phlex_kit/tooltip/tooltip_trigger.rb +13 -0
  351. data/app/components/phlex_kit/typography/blockquote.rb +12 -0
  352. data/app/components/phlex_kit/typography/heading.rb +38 -0
  353. data/app/components/phlex_kit/typography/inline_code.rb +13 -0
  354. data/app/components/phlex_kit/typography/inline_link.rb +15 -0
  355. data/app/components/phlex_kit/typography/text.rb +48 -0
  356. data/app/components/phlex_kit/typography/typography.css +50 -0
  357. data/app/javascript/phlex_kit/controllers/accordion_controller.js +59 -0
  358. data/app/javascript/phlex_kit/controllers/alert_dialog_controller.js +24 -0
  359. data/app/javascript/phlex_kit/controllers/avatar_controller.js +30 -0
  360. data/app/javascript/phlex_kit/controllers/calendar_controller.js +316 -0
  361. data/app/javascript/phlex_kit/controllers/calendar_input_controller.js +10 -0
  362. data/app/javascript/phlex_kit/controllers/carousel_controller.js +189 -0
  363. data/app/javascript/phlex_kit/controllers/chart_controller.js +135 -0
  364. data/app/javascript/phlex_kit/controllers/clipboard_controller.js +30 -0
  365. data/app/javascript/phlex_kit/controllers/collapsible_controller.js +14 -0
  366. data/app/javascript/phlex_kit/controllers/combobox_controller.js +303 -0
  367. data/app/javascript/phlex_kit/controllers/command_controller.js +161 -0
  368. data/app/javascript/phlex_kit/controllers/command_dialog_controller.js +37 -0
  369. data/app/javascript/phlex_kit/controllers/context_menu_controller.js +28 -0
  370. data/app/javascript/phlex_kit/controllers/data_table_column_visibility_controller.js +18 -0
  371. data/app/javascript/phlex_kit/controllers/data_table_controller.js +61 -0
  372. data/app/javascript/phlex_kit/controllers/data_table_search_controller.js +67 -0
  373. data/app/javascript/phlex_kit/controllers/dialog_controller.js +20 -0
  374. data/app/javascript/phlex_kit/controllers/dropdown_menu_controller.js +106 -0
  375. data/app/javascript/phlex_kit/controllers/form_field_controller.js +69 -0
  376. data/app/javascript/phlex_kit/controllers/hover_card_controller.js +11 -0
  377. data/app/javascript/phlex_kit/controllers/index.js +87 -0
  378. data/app/javascript/phlex_kit/controllers/input_otp_controller.js +66 -0
  379. data/app/javascript/phlex_kit/controllers/masked_input_controller.js +26 -0
  380. data/app/javascript/phlex_kit/controllers/menubar_controller.js +43 -0
  381. data/app/javascript/phlex_kit/controllers/message_scroller_controller.js +335 -0
  382. data/app/javascript/phlex_kit/controllers/popover_controller.js +12 -0
  383. data/app/javascript/phlex_kit/controllers/resizable_controller.js +42 -0
  384. data/app/javascript/phlex_kit/controllers/select_controller.js +141 -0
  385. data/app/javascript/phlex_kit/controllers/select_item_controller.js +14 -0
  386. data/app/javascript/phlex_kit/controllers/sheet_content_controller.js +6 -0
  387. data/app/javascript/phlex_kit/controllers/sheet_controller.js +10 -0
  388. data/app/javascript/phlex_kit/controllers/slider_controller.js +19 -0
  389. data/app/javascript/phlex_kit/controllers/tabs_controller.js +28 -0
  390. data/app/javascript/phlex_kit/controllers/theme_toggle_controller.js +22 -0
  391. data/app/javascript/phlex_kit/controllers/toast_controller.js +153 -0
  392. data/app/javascript/phlex_kit/controllers/toaster_controller.js +321 -0
  393. data/app/javascript/phlex_kit/controllers/toggle_controller.js +25 -0
  394. data/app/javascript/phlex_kit/controllers/toggle_group_controller.js +91 -0
  395. data/config/importmap.rb +7 -0
  396. data/lib/generators/phlex_kit/component/component_generator.rb +41 -0
  397. data/lib/generators/phlex_kit/install/install_generator.rb +51 -0
  398. data/lib/generators/phlex_kit/install/templates/phlex_kit.rb +11 -0
  399. data/lib/phlex_kit/base_component.rb +22 -0
  400. data/lib/phlex_kit/configuration.rb +46 -0
  401. data/lib/phlex_kit/engine.rb +50 -0
  402. data/lib/phlex_kit/propshaft_skip_source.rb +22 -0
  403. data/lib/phlex_kit/version.rb +5 -0
  404. data/lib/phlex_kit.rb +21 -0
  405. metadata +545 -0
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class EmptyContent < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-empty-content" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class EmptyDescription < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-empty-description" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class EmptyHeader < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-empty-header" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # The icon/avatar slot inside an Empty state. variant: :icon gives the tinted
3
+ # rounded chip; :default is bare. Ported from ruby_ui's RubyUI::EmptyMedia.
4
+ class EmptyMedia < BaseComponent
5
+ VARIANTS = { default: nil, icon: "icon" }.freeze
6
+ def initialize(variant: :default, **attrs)
7
+ @variant = variant.to_sym
8
+ @attrs = attrs
9
+ end
10
+ def view_template(&)
11
+ div(**mix({ class: ["pk-empty-media", VARIANTS.fetch(@variant)].compact.join(" ") }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class EmptyTitle < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-empty-title" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ /* Co-located with form.rb — UI::Form. Form-level layout (ruby_ui applies
2
+ `space-y-*` on the <form>): `.pk-form` stacks fields with rhythm;
3
+ `.pk-form-actions` lays the submit/cancel row out horizontally. Field-level
4
+ styles live with form_field.rb. Theme tokens come from the global
5
+ stylesheet. */
6
+ .pk-form {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 1.25rem;
10
+ }
11
+ .pk-form-actions {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: .5rem;
15
+ }
@@ -0,0 +1,27 @@
1
+ module PhlexKit
2
+ # Form shell, ported from ruby_ui's RubyUI::Form. A plain <form> that stacks
3
+ # PhlexKit::FormFields with rhythm (`.pk-form`, form.css — plus
4
+ # `.pk-form-actions` for the submit/cancel row). Live validation lives on each
5
+ # FormField (see form_field.rb), so this stays markup-only:
6
+ #
7
+ # render PhlexKit::Form.new(action: "/reviews", method: "post") do
8
+ # render PhlexKit::FormField.new do
9
+ # render PhlexKit::FormFieldLabel.new(for: "email") { "Email" }
10
+ # render PhlexKit::Input.new(type: :email, id: "email", name: "email", required: true)
11
+ # render PhlexKit::FormFieldError.new
12
+ # end
13
+ # div(class: "pk-form-actions") { render PhlexKit::Button.new(type: :submit) { "Save" } }
14
+ # end
15
+ #
16
+ # With Rails form helpers, put the class on the builder instead:
17
+ # `form_with(..., class: "pk-form")`.
18
+ class Form < BaseComponent
19
+ def initialize(**attrs)
20
+ @attrs = attrs
21
+ end
22
+
23
+ def view_template(&)
24
+ form(**mix({ class: "pk-form" }, @attrs), &)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ /* Co-located with form_field.rb — UI::FormField (+ Label / Hint / Error).
2
+ ruby_ui's structure kept (a vertical stack: label, control, hint, error); only
3
+ the styling is ours, vanilla CSS on the palette tokens. Geometry from ruby_ui
4
+ (flex-col gap-2, text-sm, empty:hidden, text-muted-foreground / text-destructive).
5
+ Theme tokens come from the global stylesheet. */
6
+ .pk-form-field {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: .5rem;
10
+ }
11
+ .pk-form-field-label {
12
+ font-size: .875rem;
13
+ font-weight: 500;
14
+ line-height: 1.1;
15
+ }
16
+ .pk-form-field-label:empty { display: none; }
17
+ .pk-form-field-hint {
18
+ margin: 0;
19
+ font-size: .875rem;
20
+ color: var(--pk-muted);
21
+ }
22
+ .pk-form-field-hint:empty { display: none; }
23
+ .pk-form-field-error {
24
+ margin: 0;
25
+ font-size: .875rem;
26
+ font-weight: 500;
27
+ color: var(--pk-red);
28
+ }
29
+ .pk-form-field-error:empty { display: none; }
30
+
31
+ /* Form-level layout (.pk-form / .pk-form-actions) lives with form.rb. */
@@ -0,0 +1,31 @@
1
+ module PhlexKit
2
+ # Form field group, ported from ruby_ui's RubyUI::FormField: a vertical stack
3
+ # grouping a FormFieldLabel + a control (PhlexKit::Input / Textarea / NativeSelect /
4
+ # Checkbox / RadioButton …) + an optional FormFieldHint / FormFieldError.
5
+ # Carries the phlex-kit--form-field controller for live validation: the kit's
6
+ # controls register as its input target, and once the browser first flags the
7
+ # control invalid (a submit attempt) — or the error renders with server
8
+ # content — the FormFieldError live-updates as the user types, with
9
+ # per-constraint messages via data-* on the control (data-value-missing,
10
+ # data-type-mismatch, data-too-short, …). Compose inside PhlexKit::Form (form.rb).
11
+ #
12
+ # This replaces the legacy `.field` wrapper in converted views — and crucially
13
+ # does NOT carry `.field`'s descendant `input/select/textarea` styling (whose
14
+ # specificity would otherwise override `.pk-input` &c.), so the kit's controls
15
+ # render with their own look. Multi-part, like PhlexKit::Card:
16
+ #
17
+ # render PhlexKit::FormField.new do
18
+ # render PhlexKit::FormFieldLabel.new(for: "user_email") { "Email" }
19
+ # render PhlexKit::Input.new(type: :email, id: "user_email", name: "user[email]")
20
+ # render PhlexKit::FormFieldError.new { "is invalid" }
21
+ # end
22
+ class FormField < BaseComponent
23
+ def initialize(**attrs)
24
+ @attrs = attrs
25
+ end
26
+
27
+ def view_template(&block)
28
+ div(**mix({ class: "pk-form-field", data: { controller: "phlex-kit--form-field" } }, @attrs), &block)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ module PhlexKit
2
+ # Validation error text for a PhlexKit::FormField, styled with the destructive token
3
+ # and hidden when empty. Render it empty for live client-side messages (the
4
+ # phlex-kit--form-field controller fills it once the control goes invalid), or
5
+ # with server-rendered model errors — non-empty content arms validation on
6
+ # connect. See form_field.rb.
7
+ class FormFieldError < BaseComponent
8
+ def initialize(**attrs)
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&block)
13
+ p(**mix({
14
+ class: "pk-form-field-error",
15
+ data: { phlex_kit__form_field_target: "error" }
16
+ }, @attrs), &block)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # Muted helper text under a PhlexKit::FormField control. Hidden when empty.
3
+ # See form_field.rb.
4
+ class FormFieldHint < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&block)
10
+ p(**mix({ class: "pk-form-field-hint" }, @attrs), &block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # Label for a PhlexKit::FormField control. Pass `for:` to bind it to the control's id
3
+ # (e.g. `for: f.field_id(:email)`). Hidden when empty. See form_field.rb.
4
+ class FormFieldLabel < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&block)
10
+ label(**mix({ class: "pk-form-field-label" }, @attrs), &block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ /* Co-located with hover_card.rb. Tokens from the global stylesheet. */
2
+ .pk-hover-card { position: relative; display: inline-block; }
3
+ .pk-hover-card-trigger { display: inline-block; }
4
+ .pk-hover-card-content {
5
+ position: absolute; z-index: 50; top: calc(100% + .5rem); left: 0; width: 16rem;
6
+ border: 1px solid var(--pk-border); border-radius: calc(var(--pk-radius) - 2px); background: var(--pk-surface);
7
+ color: var(--pk-text); padding: 1rem; box-shadow: 0 8px 24px rgb(0 0 0 / .3); outline: none;
8
+ }
@@ -0,0 +1,10 @@
1
+ module PhlexKit
2
+ # Hover/focus-triggered floating card, positioned with CSS. Ported from ruby_ui's
3
+ # RubyUI::HoverCard. Compose HoverCard > (HoverCardTrigger + HoverCardContent).
4
+ class HoverCard < BaseComponent
5
+ def initialize(**attrs) = (@attrs = attrs)
6
+ def view_template(&)
7
+ div(**mix({ class: "pk-hover-card", data: { controller: "phlex-kit--hover-card", action: "mouseenter->phlex-kit--hover-card#show mouseleave->phlex-kit--hover-card#hide focusin->phlex-kit--hover-card#show focusout->phlex-kit--hover-card#hide" } }, @attrs), &)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module PhlexKit
2
+ class HoverCardContent < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&)
5
+ div(**mix({ class: "pk-hover-card-content pk-hidden", data: { phlex_kit__hover_card_target: "content", state: "closed" } }, @attrs), &)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class HoverCardTrigger < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-hover-card-trigger", data: { phlex_kit__hover_card_target: "trigger" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ /* Co-located with input.rb — UI::Input. ruby_ui's Tailwind input replaced with
2
+ our own vanilla CSS: the geometry is ruby_ui's (h-9 / rounded-md / px-3 py-1 /
3
+ text-sm / shadow-xs / ring-on-focus), realised against this app's palette
4
+ tokens. Not styled to match the legacy `.field input`; this is the kit's own
5
+ look and will be revisited in the visual redesign. Theme tokens come from the
6
+ global stylesheet. */
7
+ .pk-input {
8
+ height: 2rem;
9
+ width: 100%;
10
+ border-radius: var(--pk-radius);
11
+ border: 1px solid var(--pk-input);
12
+ background: color-mix(in oklab, var(--pk-input) 30%, transparent);
13
+ color: var(--pk-text);
14
+ padding: .25rem .625rem;
15
+ font: inherit;
16
+ font-size: .875rem;
17
+ transition: color .15s ease, border-color .15s ease, box-shadow .15s ease;
18
+ }
19
+ .pk-input::placeholder { color: var(--pk-muted); }
20
+ .pk-input:focus-visible {
21
+ outline: none;
22
+ border-color: var(--pk-brand);
23
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
24
+ }
25
+ .pk-input:disabled { cursor: not-allowed; opacity: .5; }
26
+ .pk-input[aria-invalid="true"] {
27
+ border-color: var(--pk-red);
28
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 30%, transparent);
29
+ }
@@ -0,0 +1,34 @@
1
+ module PhlexKit
2
+ # Form text input, ported from ruby_ui's RubyUI::Input (https://ruby-ui.com).
3
+ # ruby_ui's Tailwind strings are replaced with plain `.pk-input` CSS
4
+ # (input.css) on the app's design tokens. Like upstream, it registers as a
5
+ # phlex-kit--form-field input target — live validation kicks in when wrapped
6
+ # in a PhlexKit::FormField, and Stimulus ignores the wiring otherwise.
7
+ #
8
+ # `type:` picks the HTML input type (:text, :email, :password, :number, …);
9
+ # everything else — `name:`, `value:`, `id:`, `placeholder:`, `required:`,
10
+ # `**on(...)` — rides the `mix` pass-through onto the <input>, so a caller's
11
+ # `class:` augments rather than clobbers ours. This is a presentational
12
+ # primitive, NOT a Rails form-builder helper: in a Phlex view you supply the
13
+ # name/value yourself, exactly as Admin::ReviewRow composes PhlexKit::Button.
14
+ #
15
+ # No VARIANTS: an input is an input. Sizing/state live in CSS (:focus,
16
+ # :disabled, [aria-invalid]).
17
+ class Input < BaseComponent
18
+ def initialize(type: :text, **attrs)
19
+ @type = type
20
+ @attrs = attrs
21
+ end
22
+
23
+ def view_template
24
+ input(**mix({
25
+ type: @type,
26
+ class: "pk-input",
27
+ data: {
28
+ phlex_kit__form_field_target: "input",
29
+ action: "input->phlex-kit--form-field#onInput invalid->phlex-kit--form-field#onInvalid"
30
+ }
31
+ }, @attrs))
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ /* Co-located with input_group.rb — UI::InputGroup, shadcn parity: the shell
2
+ carries the border/fill and focus ring; the inner .pk-input goes naked. */
3
+ .pk-input-group {
4
+ display: flex;
5
+ align-items: center;
6
+ width: 100%;
7
+ border: 1px solid var(--pk-input);
8
+ border-radius: var(--pk-radius);
9
+ background: color-mix(in oklab, var(--pk-input) 30%, transparent);
10
+ transition: border-color .15s ease, box-shadow .15s ease;
11
+ }
12
+ .pk-input-group:focus-within {
13
+ border-color: var(--pk-brand);
14
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
15
+ }
16
+ .pk-input-group .pk-input {
17
+ border: 0;
18
+ background: transparent;
19
+ box-shadow: none;
20
+ flex: 1;
21
+ }
22
+ .pk-input-group .pk-input:focus-visible { outline: none; box-shadow: none; border: 0; }
23
+ .pk-input-group-addon {
24
+ display: flex;
25
+ align-items: center;
26
+ gap: .25rem;
27
+ flex: none;
28
+ padding: 0 .625rem;
29
+ color: var(--pk-muted);
30
+ font-size: .875rem;
31
+ }
32
+ .pk-input-group-addon.start { padding-right: 0; }
33
+ .pk-input-group-addon.end { padding-left: 0; }
34
+ .pk-input-group-addon svg { width: 1rem; height: 1rem; }
35
+ .pk-input-group-text { color: var(--pk-muted); }
@@ -0,0 +1,15 @@
1
+ module PhlexKit
2
+ # Input with attached addons, ported from shadcn/ui's InputGroup: a bordered
3
+ # shell that focuses as one control; compose InputGroupAddon(align:) around a
4
+ # plain PhlexKit::Input (its own border/bg collapse inside the group).
5
+ # `.pk-input-group*` (input_group.css).
6
+ class InputGroup < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&)
12
+ div(**mix({ class: "pk-input-group", role: "group" }, @attrs), &)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module PhlexKit
2
+ # Leading/trailing addon inside an InputGroup — icons, text or small buttons.
3
+ # See input_group.rb.
4
+ class InputGroupAddon < BaseComponent
5
+ ALIGNS = { start: "start", end: "end" }.freeze
6
+
7
+ def initialize(align: :start, **attrs)
8
+ @align = align.to_sym
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&)
13
+ div(**mix({ class: "pk-input-group-addon #{ALIGNS.fetch(@align)}" }, @attrs), &)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Muted text inside an InputGroupAddon (e.g. a unit or prefix). See input_group.rb.
3
+ class InputGroupText < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = span(**mix({ class: "pk-input-group-text" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ /* Co-located with input_otp.rb — UI::InputOTP, shadcn parity: joined cells
2
+ (shared borders, outer corners rounded), ring on the active cell. */
3
+ .pk-input-otp { display: flex; align-items: center; gap: .5rem; }
4
+ .pk-input-otp-group { display: flex; align-items: center; }
5
+ .pk-input-otp-slot {
6
+ width: 2.25rem;
7
+ height: 2.25rem;
8
+ border: 1px solid var(--pk-input);
9
+ background: color-mix(in oklab, var(--pk-input) 30%, transparent);
10
+ color: var(--pk-text);
11
+ font: inherit;
12
+ font-size: .875rem;
13
+ text-align: center;
14
+ outline: none;
15
+ transition: border-color .15s ease, box-shadow .15s ease;
16
+ }
17
+ .pk-input-otp-slot:not(:first-child) { margin-left: -1px; }
18
+ .pk-input-otp-group .pk-input-otp-slot:first-child {
19
+ border-top-left-radius: var(--pk-radius);
20
+ border-bottom-left-radius: var(--pk-radius);
21
+ }
22
+ .pk-input-otp-group .pk-input-otp-slot:last-child {
23
+ border-top-right-radius: var(--pk-radius);
24
+ border-bottom-right-radius: var(--pk-radius);
25
+ }
26
+ .pk-input-otp-slot:focus {
27
+ position: relative;
28
+ z-index: 1;
29
+ border-color: var(--pk-brand);
30
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
31
+ }
32
+ .pk-input-otp-separator { color: var(--pk-muted); }
@@ -0,0 +1,29 @@
1
+ module PhlexKit
2
+ # One-time-code input, ported from shadcn/ui's InputOTP (the input-otp npm
3
+ # lib is replaced by the phlex-kit--input-otp controller: per-slot typing with
4
+ # auto-advance, backspace retreat, paste distribution, arrow movement). The
5
+ # hidden input carries the joined value under `name:`. Compose InputOtp >
6
+ # InputOtpGroup(InputOtpSlot…) [+ InputOtpSeparator + more groups]; slot
7
+ # count must total `length:`. `.pk-input-otp*` (input_otp.css).
8
+ class InputOtp < BaseComponent
9
+ def initialize(length: 6, name: nil, value: nil, **attrs)
10
+ @length = length
11
+ @name = name
12
+ @value = value
13
+ @attrs = attrs
14
+ end
15
+
16
+ def view_template(&block)
17
+ div(**mix({
18
+ class: "pk-input-otp",
19
+ data: {
20
+ controller: "phlex-kit--input-otp",
21
+ phlex_kit__input_otp_length_value: @length
22
+ }
23
+ }, @attrs)) do
24
+ input(type: :hidden, name: @name, value: @value, data: { phlex_kit__input_otp_target: "value" }) if @name
25
+ yield if block
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Joined run of OTP slots. See input_otp.rb.
3
+ class InputOtpGroup < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-input-otp-group" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Divider between OTP groups. See input_otp.rb.
3
+ class InputOtpSeparator < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template = div(**mix({ class: "pk-input-otp-separator", role: "separator" }, @attrs)) { "-" }
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ module PhlexKit
2
+ # One character cell of an InputOtp. See input_otp.rb.
3
+ class InputOtpSlot < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template
9
+ input(**mix({
10
+ type: :text,
11
+ inputmode: "numeric",
12
+ autocomplete: "one-time-code",
13
+ maxlength: "1",
14
+ class: "pk-input-otp-slot",
15
+ data: {
16
+ phlex_kit__input_otp_target: "slot",
17
+ action: [
18
+ "input->phlex-kit--input-otp#onInput",
19
+ "keydown->phlex-kit--input-otp#onKeydown",
20
+ "paste->phlex-kit--input-otp#onPaste",
21
+ "focus->phlex-kit--input-otp#onFocus"
22
+ ].join(" ")
23
+ }
24
+ }, @attrs))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ /* Co-located with item.rb — UI::Item family, shadcn parity. */
2
+ .pk-item {
3
+ display: flex;
4
+ align-items: center;
5
+ gap: .625rem;
6
+ width: 100%;
7
+ border: 1px solid transparent;
8
+ border-radius: var(--pk-radius);
9
+ padding: .625rem .875rem;
10
+ font-size: .875rem;
11
+ }
12
+ .pk-item.outline { border-color: var(--pk-border); }
13
+ .pk-item.muted { background: var(--pk-surface-2); }
14
+ .pk-item-media {
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ flex: none;
19
+ color: var(--pk-muted);
20
+ }
21
+ .pk-item-media svg { width: 1rem; height: 1rem; }
22
+ .pk-item-content { display: flex; flex: 1; flex-direction: column; gap: .125rem; min-width: 0; }
23
+ .pk-item-title { font-weight: 500; line-height: 1.25; }
24
+ .pk-item-description {
25
+ color: var(--pk-muted);
26
+ font-size: .875rem;
27
+ line-height: 1.375;
28
+ overflow: hidden;
29
+ text-overflow: ellipsis;
30
+ }
31
+ .pk-item-actions { display: flex; align-items: center; gap: .5rem; flex: none; }
32
+ .pk-item-group { display: flex; flex-direction: column; width: 100%; gap: .5rem; }
@@ -0,0 +1,18 @@
1
+ module PhlexKit
2
+ # List row, ported from shadcn/ui's Item: a flex row of ItemMedia +
3
+ # ItemContent(ItemTitle + ItemDescription) + ItemActions, optionally grouped
4
+ # in an ItemGroup. variant :outline draws a bordered card row.
5
+ # `.pk-item*` (item.css).
6
+ class Item < BaseComponent
7
+ VARIANTS = { default: nil, outline: "outline", muted: "muted" }.freeze
8
+
9
+ def initialize(variant: :default, **attrs)
10
+ @variant = variant.to_sym
11
+ @attrs = attrs
12
+ end
13
+
14
+ def view_template(&)
15
+ div(**mix({ class: [ "pk-item", VARIANTS.fetch(@variant) ].compact.join(" ") }, @attrs), &)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Trailing actions slot of an Item. See item.rb.
3
+ class ItemActions < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-actions" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Title/description column of an Item. See item.rb.
3
+ class ItemContent < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-content" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See item.rb.
3
+ class ItemDescription < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-description" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Stack of Items. See item.rb.
3
+ class ItemGroup < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-group", role: "list" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Leading icon/avatar slot of an Item. See item.rb.
3
+ class ItemMedia < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-media" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See item.rb.
3
+ class ItemTitle < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-item-title" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ /* Co-located with kbd.rb — UI::Kbd (+ KbdGroup), shadcn parity. */
2
+ .pk-kbd {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ gap: .25rem;
7
+ min-width: 1.25rem;
8
+ height: 1.25rem;
9
+ padding: 0 .25rem;
10
+ border-radius: calc(var(--pk-radius) - 4px);
11
+ background: var(--pk-surface-2);
12
+ color: var(--pk-muted);
13
+ font-family: inherit;
14
+ font-size: .75rem;
15
+ font-weight: 500;
16
+ }
17
+ .pk-kbd-group { display: inline-flex; align-items: center; gap: .25rem; font-family: inherit; }
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # Keyboard-key chip, ported from shadcn/ui's Kbd. The kit's ShortcutKey
3
+ # predates it (ruby_ui naming) — Kbd is the shadcn-parity spelling; group
4
+ # several in a KbdGroup. `.pk-kbd` (kbd.css).
5
+ class Kbd < BaseComponent
6
+ def initialize(**attrs)
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template(&)
11
+ kbd(**mix({ class: "pk-kbd" }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Row of Kbd chips (e.g. ⌘ + K). See kbd.rb.
3
+ class KbdGroup < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&)
9
+ kbd(**mix({ class: "pk-kbd-group" }, @attrs), &)
10
+ end
11
+ end
12
+ end