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,12 @@
1
+ /* Co-located with label.rb — UI::Label, shadcn parity (text-sm font-medium,
2
+ dims when its control is disabled). */
3
+ .pk-label {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: .5rem;
7
+ font-size: .875rem;
8
+ font-weight: 500;
9
+ line-height: 1;
10
+ user-select: none;
11
+ }
12
+ .pk-label:has(+ :disabled), .pk-label:has(:disabled) { cursor: not-allowed; opacity: .5; }
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # Standalone form label, ported from shadcn/ui's Label (FormFieldLabel is the
3
+ # ruby_ui-shaped equivalent inside a FormField). Pass `for:` to bind it.
4
+ # `.pk-label` (label.css).
5
+ class Label < BaseComponent
6
+ def initialize(**attrs)
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template(&)
11
+ label(**mix({ class: "pk-label" }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ /* Co-located with link.rb — UI::Link. The button-styled variants deliberately
2
+ REUSE the `.pk-button*` classes from button.css (Link shares Button's exact
3
+ variant/size vocabulary), so this file only carries the link-specific niceties
4
+ on top: ruby_ui's `underline-offset-4` for the inline `:link` variant. Theme
5
+ tokens come from the global stylesheet. */
6
+ .pk-button.link { text-underline-offset: 4px; }
@@ -0,0 +1,47 @@
1
+ module PhlexKit
2
+ # Link, ported from ruby_ui's RubyUI::Link — an <a> that wears PhlexKit::Button's
3
+ # look. Same variant/size vocabulary as Button, so it reuses the `.pk-button*`
4
+ # classes from button.css (link.css only adds the inline :link treatment).
5
+ # Default variant is :link (an inline underline-on-hover link); the button-style
6
+ # variants turn it into a button-styled anchor (ruby_ui's "Link" concept — what
7
+ # we already do ad-hoc with `a(class: "pk-button outline sm")`).
8
+ #
9
+ # Presentational; `mix` passes `**@attrs` (incl. data-*) straight through.
10
+ # `VARIANTS.fetch`/`SIZES.fetch` fail loud.
11
+ class Link < BaseComponent
12
+ # Mirrors PhlexKit::Button's vocabulary 1:1 so the shared `.pk-button*` CSS applies.
13
+ VARIANTS = {
14
+ link: "link",
15
+ primary: "primary",
16
+ secondary: "secondary",
17
+ destructive: "destructive",
18
+ outline: "outline",
19
+ ghost: "ghost"
20
+ }.freeze
21
+
22
+ SIZES = {
23
+ sm: "sm",
24
+ md: nil,
25
+ lg: "lg",
26
+ xl: "xl"
27
+ }.freeze
28
+
29
+ def initialize(href: "#", variant: :link, size: :md, icon: false, **attrs)
30
+ @href = href
31
+ @variant = variant.to_sym
32
+ @size = size.to_sym
33
+ @icon = icon
34
+ @attrs = attrs
35
+ end
36
+
37
+ def view_template(&block)
38
+ a(**mix({ href: @href, class: classes }, @attrs), &block)
39
+ end
40
+
41
+ private
42
+
43
+ def classes
44
+ [ "pk-button", VARIANTS.fetch(@variant), SIZES.fetch(@size), ("icon" if @icon) ].compact.join(" ")
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Text input with an inline mask. Ported from ruby_ui's RubyUI::MaskedInput —
3
+ # ruby_ui uses the `maska` JS lib; PhlexKit ships a small dependency-free mask
4
+ # controller (#=digit, A=letter, *=any) driven by a data-mask attribute. Swap in
5
+ # maska by replacing masked_input_controller.js if you need its full feature set.
6
+ class MaskedInput < BaseComponent
7
+ def initialize(**attrs) = (@attrs = attrs)
8
+ def view_template
9
+ render PhlexKit::Input.new(type: "text", **mix({ data: { controller: "phlex-kit--masked-input" } }, @attrs))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,66 @@
1
+ /* Co-located with menubar.rb — UI::Menubar, shadcn parity (Radix replaced by
2
+ the phlex-kit--menubar controller). NavigationMenu shares the item look. */
3
+ .pk-menubar {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: .25rem;
7
+ border: 1px solid var(--pk-border);
8
+ border-radius: var(--pk-radius);
9
+ background: var(--pk-surface);
10
+ padding: .25rem;
11
+ }
12
+ .pk-menubar-menu { position: relative; }
13
+ .pk-menubar-trigger {
14
+ display: flex;
15
+ align-items: center;
16
+ border: 0;
17
+ border-radius: calc(var(--pk-radius) - 4px);
18
+ background: transparent;
19
+ color: var(--pk-text);
20
+ padding: .25rem .5rem;
21
+ font: inherit;
22
+ font-size: .875rem;
23
+ font-weight: 500;
24
+ cursor: pointer;
25
+ outline: none;
26
+ }
27
+ .pk-menubar-trigger:hover,
28
+ .pk-menubar-trigger[aria-expanded="true"] { background: var(--pk-accent); }
29
+ .pk-menubar-content {
30
+ position: absolute;
31
+ top: 100%;
32
+ left: 0;
33
+ z-index: 50;
34
+ min-width: 12rem;
35
+ margin-top: .5rem;
36
+ border: 1px solid var(--pk-border);
37
+ border-radius: var(--pk-radius);
38
+ background: var(--pk-surface);
39
+ color: var(--pk-text);
40
+ padding: .25rem;
41
+ box-shadow: 0 8px 24px rgb(0 0 0 / .3);
42
+ }
43
+ .pk-menubar-item {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: .5rem;
47
+ border-radius: calc(var(--pk-radius) - 4px);
48
+ padding: .375rem .5rem;
49
+ font-size: .875rem;
50
+ color: var(--pk-text);
51
+ text-decoration: none;
52
+ cursor: pointer;
53
+ user-select: none;
54
+ outline: none;
55
+ }
56
+ .pk-menubar-item:hover, .pk-menubar-item:focus { background: var(--pk-accent); }
57
+ .pk-menubar-shortcut { margin-left: auto; font-size: .75rem; color: var(--pk-muted); letter-spacing: .1em; }
58
+ .pk-menubar-separator { height: 1px; margin: .25rem -0.25rem; background: var(--pk-border); }
59
+ .pk-menubar-content::before {
60
+ content: "";
61
+ position: absolute;
62
+ top: calc(-.5rem - 1px);
63
+ left: 0;
64
+ right: 0;
65
+ height: calc(.5rem + 1px);
66
+ }
@@ -0,0 +1,24 @@
1
+ module PhlexKit
2
+ # Desktop-style menu bar, ported from shadcn/ui's Menubar (Radix replaced by
3
+ # the phlex-kit--menubar controller: click opens a menu, hover switches
4
+ # between open menus, Escape/outside closes). Compose Menubar > MenubarMenu >
5
+ # (MenubarTrigger + MenubarContent > MenubarItem/MenubarSeparator).
6
+ # NavigationMenu shares the controller in hover mode. `.pk-menubar*`
7
+ # (menubar.css).
8
+ class Menubar < BaseComponent
9
+ def initialize(**attrs)
10
+ @attrs = attrs
11
+ end
12
+
13
+ def view_template(&)
14
+ div(**mix({
15
+ class: "pk-menubar",
16
+ role: "menubar",
17
+ data: {
18
+ controller: "phlex-kit--menubar",
19
+ action: "click@window->phlex-kit--menubar#onClickOutside keydown.esc->phlex-kit--menubar#close"
20
+ }
21
+ }, @attrs), &)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module PhlexKit
2
+ # Dropdown panel of a MenubarMenu. See menubar.rb.
3
+ class MenubarContent < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&)
6
+ div(**mix({ class: "pk-menubar-content pk-hidden", role: "menu" }, @attrs), &)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module PhlexKit
2
+ # Menu row (an <a> — pass href:, or as: :div for buttons). Closes the bar.
3
+ # See menubar.rb.
4
+ class MenubarItem < BaseComponent
5
+ def initialize(as: :a, href: "#", shortcut: nil, **attrs)
6
+ @as = as.to_sym
7
+ @href = href
8
+ @shortcut = shortcut
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&block)
13
+ base = {
14
+ role: "menuitem",
15
+ tabindex: "-1",
16
+ class: "pk-menubar-item",
17
+ data: { action: "click->phlex-kit--menubar#close" }
18
+ }
19
+ base[:href] = @href unless @as == :div
20
+ send(@as, **mix(base, @attrs)) do
21
+ block&.call
22
+ span(class: "pk-menubar-shortcut") { @shortcut } if @shortcut
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module PhlexKit
2
+ # One trigger+panel pair in a Menubar. See menubar.rb.
3
+ class MenubarMenu < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&)
6
+ div(**mix({ class: "pk-menubar-menu", data: { phlex_kit__menubar_target: "menu" } }, @attrs), &)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See menubar.rb.
3
+ class MenubarSeparator < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template = div(**mix({ class: "pk-menubar-separator", role: "separator" }, @attrs))
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # See menubar.rb.
3
+ class MenubarTrigger < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&)
6
+ button(**mix({
7
+ type: :button,
8
+ class: "pk-menubar-trigger",
9
+ aria: { haspopup: "menu", expanded: "false" },
10
+ data: { action: "click->phlex-kit--menubar#toggle mouseenter->phlex-kit--menubar#switch" }
11
+ }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ /* Co-located with message.rb. Theme tokens from the global stylesheet. */
2
+ .pk-message {
3
+ position: relative; display: flex; width: 100%; min-width: 0;
4
+ gap: .5rem; font-size: .875rem;
5
+ }
6
+ .pk-message[data-align="end"] { flex-direction: row-reverse; }
7
+ .pk-message-content { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: .625rem; overflow-wrap: break-word; }
8
+ .pk-message[data-align="end"] .pk-message-content > [data-slot] { align-self: flex-end; }
9
+ .pk-message-group { display: flex; min-width: 0; flex-direction: column; gap: .5rem; }
10
+ .pk-message-header,
11
+ .pk-message-footer {
12
+ display: flex; max-width: 100%; min-width: 0; align-items: center;
13
+ padding-inline: .75rem; font-size: .75rem; font-weight: 500; color: var(--pk-muted);
14
+ }
15
+ .pk-message[data-align="end"] .pk-message-footer { justify-content: flex-end; }
16
+ .pk-message-avatar {
17
+ display: flex; width: fit-content; min-width: 2rem; flex: none;
18
+ align-items: center; justify-content: center; align-self: flex-end;
19
+ overflow: hidden; border-radius: 9999px; background: var(--pk-surface-2);
20
+ }
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # Chat message row (avatar + content), ported from ruby_ui's RubyUI::Message.
3
+ # align: :end reverses the row. Compose Message > MessageContent (+ Avatar/
4
+ # Header/Footer); group several in a MessageGroup.
5
+ class Message < BaseComponent
6
+ def initialize(align: :start, **attrs)
7
+ @align = align.to_sym
8
+ @attrs = attrs
9
+ end
10
+ def view_template(&)
11
+ div(**mix({ class: "pk-message", data: { slot: "message", align: @align } }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class MessageAvatar < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-message-avatar", data: { slot: "message-avatar" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class MessageContent < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-message-content", data: { slot: "message-content" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class MessageFooter < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-message-footer", data: { slot: "message-footer" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class MessageGroup < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-message-group", data: { slot: "message-group" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class MessageHeader < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-message-header", data: { slot: "message-header" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,2 @@
1
+ /* Co-located with message_scroller.rb. Tokens from the global stylesheet. */
2
+ .pk-message-scroller { position: relative; display: flex; height: 100%; width: 100%; min-height: 0; flex-direction: column; overflow: hidden; }
@@ -0,0 +1,11 @@
1
+ module PhlexKit
2
+ # Height-constrained chat transcript with autoscroll/anchoring. Ported from
3
+ # ruby_ui's RubyUI::MessageScroller. Mark the scroll viewport, content list, and
4
+ # jump button inside with data-phlex-kit--message-scroller-target attributes.
5
+ class MessageScroller < BaseComponent
6
+ def initialize(**attrs) = (@attrs = attrs)
7
+ def view_template(&)
8
+ div(**mix({ class: "pk-message-scroller", data: { controller: "phlex-kit--message-scroller", slot: "message-scroller" } }, @attrs), &)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ /* Co-located with native_select.rb — UI::NativeSelect (+ Icon / Option / Group).
2
+ ruby_ui's structure kept verbatim (wrapper > appearance:none select + absolute
3
+ chevron); only the styling is ours, vanilla CSS on the palette tokens. Geometry
4
+ from ruby_ui (h-9 default / h-7 sm, rounded-md, pr-8 pl-2.5, ring-on-focus).
5
+ Theme tokens come from the global stylesheet. */
6
+ .pk-native-select {
7
+ position: relative;
8
+ display: inline-flex;
9
+ width: 100%;
10
+ }
11
+ .pk-native-select-field {
12
+ width: 100%;
13
+ min-width: 0;
14
+ height: 2rem;
15
+ appearance: none;
16
+ -webkit-appearance: none;
17
+ border-radius: calc(var(--pk-radius) - 2px);
18
+ border: 1px solid var(--pk-input);
19
+ background: transparent;
20
+ color: var(--pk-text);
21
+ font: inherit;
22
+ font-size: .875rem;
23
+ padding: .25rem 2rem .25rem .625rem;
24
+ cursor: pointer;
25
+ transition: color .15s ease, border-color .15s ease, box-shadow .15s ease;
26
+ }
27
+ .pk-native-select-field:focus-visible {
28
+ outline: none;
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-native-select-field:disabled { cursor: not-allowed; opacity: .5; }
33
+ .pk-native-select-field.sm {
34
+ height: 1.75rem;
35
+ padding-top: .125rem;
36
+ padding-bottom: .125rem;
37
+ }
38
+ /* Native option lists are OS-drawn; force a readable palette where honoured. */
39
+ .pk-native-select-field option,
40
+ .pk-native-select-field optgroup { background: var(--pk-surface); color: var(--pk-text); }
41
+ .pk-native-select-icon {
42
+ position: absolute;
43
+ top: 50%;
44
+ right: .625rem;
45
+ transform: translateY(-50%);
46
+ display: flex;
47
+ pointer-events: none;
48
+ user-select: none;
49
+ color: var(--pk-muted);
50
+ }
51
+ .pk-native-select-icon svg { width: 1rem; height: 1rem; }
@@ -0,0 +1,48 @@
1
+ module PhlexKit
2
+ # Styled native <select>, ported from ruby_ui's RubyUI::NativeSelect. A real
3
+ # browser select (native option list, mobile-friendly) wrapped so we can hide
4
+ # the OS chevron (appearance:none) and draw our own — otherwise the native
5
+ # arrow renders an inconsistent light-on-dark box. Multi-part, like PhlexKit::Card:
6
+ #
7
+ # render PhlexKit::NativeSelect.new(name: "store_id") do
8
+ # render PhlexKit::NativeSelectOption.new(value: "") { "All stores" }
9
+ # render PhlexKit::NativeSelectGroup.new(label: "Live") do
10
+ # render PhlexKit::NativeSelectOption.new(value: store.id) { store.name }
11
+ # end
12
+ # end
13
+ #
14
+ # The yielded block is the <option>/<optgroup> list; `name:`/`id:`/`required:`/
15
+ # `**on(...)` pass through to the <select> via `mix`. `size:` is the kit's lone
16
+ # selector here (default | sm) — `SIZES.fetch` fails loud on anything else.
17
+ class NativeSelect < BaseComponent
18
+ # size => extra class appended after the base select class (nil = default).
19
+ SIZES = {
20
+ default: nil,
21
+ sm: "sm"
22
+ }.freeze
23
+
24
+ def initialize(size: :default, **attrs)
25
+ @size = size.to_sym
26
+ @attrs = attrs
27
+ end
28
+
29
+ def view_template(&block)
30
+ div(class: "pk-native-select") do
31
+ select(**mix({
32
+ class: classes,
33
+ data: {
34
+ phlex_kit__form_field_target: "input",
35
+ action: "change->phlex-kit--form-field#onChange invalid->phlex-kit--form-field#onInvalid"
36
+ }
37
+ }, @attrs), &block)
38
+ render PhlexKit::NativeSelectIcon.new
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def classes
45
+ [ "pk-native-select-field", SIZES.fetch(@size) ].compact.join(" ")
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # An <optgroup> inside PhlexKit::NativeSelect — labels a cluster of options
3
+ # (`label:` passes through). Plain pass-through wrapper. See native_select.rb.
4
+ class NativeSelectGroup < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&block)
10
+ optgroup(**@attrs, &block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module PhlexKit
2
+ # The chevron that sits over the appearance:none <select> in PhlexKit::NativeSelect.
3
+ # Mirrors ruby_ui's NativeSelectIcon: renders a default chevron SVG, or a
4
+ # caller-supplied block in its place. The svg uses `currentColor`, so the icon
5
+ # colour is driven by `.pk-native-select-icon`'s `color` token (native_select.css).
6
+ class NativeSelectIcon < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&block)
12
+ span(**mix({ class: "pk-native-select-icon" }, @attrs)) do
13
+ block ? yield : chevron
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def chevron
20
+ svg(
21
+ xmlns: "http://www.w3.org/2000/svg",
22
+ viewbox: "0 0 24 24",
23
+ fill: "none",
24
+ stroke: "currentColor",
25
+ "stroke-width": "2",
26
+ "stroke-linecap": "round",
27
+ "stroke-linejoin": "round",
28
+ "aria-hidden": "true"
29
+ ) { |s| s.path(d: "m6 9 6 6 6-6") }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # A single <option> inside PhlexKit::NativeSelect. Plain pass-through wrapper (no
3
+ # base class); `value:`/`selected:`/`disabled:` ride through to the element.
4
+ # See native_select.rb.
5
+ class NativeSelectOption < BaseComponent
6
+ def initialize(**attrs)
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template(&block)
11
+ option(**@attrs, &block)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,67 @@
1
+ /* Co-located with navigation_menu.rb — UI::NavigationMenu, shadcn parity
2
+ (Radix replaced by the shared phlex-kit--menubar controller, hover mode). */
3
+ .pk-navigation-menu { position: relative; display: flex; }
4
+ .pk-navigation-menu-list {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: .25rem;
8
+ margin: 0;
9
+ padding: 0;
10
+ list-style: none;
11
+ }
12
+ .pk-navigation-menu-item { position: relative; }
13
+ .pk-navigation-menu-trigger, .pk-navigation-menu-link {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ gap: .25rem;
17
+ height: 2.25rem;
18
+ border: 0;
19
+ border-radius: calc(var(--pk-radius) - 2px);
20
+ background: transparent;
21
+ color: var(--pk-text);
22
+ padding: .5rem .75rem;
23
+ font: inherit;
24
+ font-size: .875rem;
25
+ font-weight: 500;
26
+ text-decoration: none;
27
+ cursor: pointer;
28
+ outline: none;
29
+ transition: background-color .15s ease;
30
+ }
31
+ .pk-navigation-menu-trigger:hover, .pk-navigation-menu-link:hover,
32
+ .pk-navigation-menu-trigger[aria-expanded="true"] { background: var(--pk-accent); }
33
+ .pk-navigation-menu-chevron { width: .75rem; height: .75rem; transition: transform .2s ease; }
34
+ .pk-navigation-menu-trigger[aria-expanded="true"] .pk-navigation-menu-chevron { transform: rotate(180deg); }
35
+ .pk-navigation-menu-content {
36
+ position: absolute;
37
+ /* invisible bridge over the gap so hovering toward the panel doesn't
38
+ mouseleave the nav and close it */
39
+ --pk-nav-gap: .375rem;
40
+ top: 100%;
41
+ left: 0;
42
+ z-index: 50;
43
+ min-width: 16rem;
44
+ margin-top: var(--pk-nav-gap);
45
+ border: 1px solid var(--pk-border);
46
+ border-radius: var(--pk-radius);
47
+ background: var(--pk-surface);
48
+ color: var(--pk-text);
49
+ padding: .5rem;
50
+ box-shadow: 0 8px 24px rgb(0 0 0 / .3);
51
+ }
52
+ .pk-navigation-menu-content .pk-navigation-menu-link {
53
+ display: flex;
54
+ height: auto;
55
+ width: 100%;
56
+ flex-direction: column;
57
+ align-items: flex-start;
58
+ gap: .25rem;
59
+ }
60
+ .pk-navigation-menu-content::before {
61
+ content: "";
62
+ position: absolute;
63
+ top: calc(-1 * var(--pk-nav-gap) - 1px);
64
+ left: 0;
65
+ right: 0;
66
+ height: calc(var(--pk-nav-gap) + 1px);
67
+ }
@@ -0,0 +1,23 @@
1
+ module PhlexKit
2
+ # Site navigation with hoverable panels, ported from shadcn/ui's
3
+ # NavigationMenu. Shares the phlex-kit--menubar controller in hover-open mode
4
+ # (data-hover-open). Compose NavigationMenu > NavigationMenuList >
5
+ # NavigationMenuItem > (NavigationMenuTrigger + NavigationMenuContent |
6
+ # NavigationMenuLink). `.pk-navigation-menu*` (navigation_menu.css).
7
+ class NavigationMenu < BaseComponent
8
+ def initialize(**attrs)
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&)
13
+ nav(**mix({
14
+ class: "pk-navigation-menu",
15
+ data: {
16
+ controller: "phlex-kit--menubar",
17
+ hover_open: true,
18
+ action: "click@window->phlex-kit--menubar#onClickOutside keydown.esc->phlex-kit--menubar#close mouseleave->phlex-kit--menubar#close"
19
+ }
20
+ }, @attrs), &)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module PhlexKit
2
+ # Hover panel of a NavigationMenuItem. See navigation_menu.rb.
3
+ class NavigationMenuContent < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&)
6
+ div(**mix({ class: "pk-navigation-menu-content pk-hidden", role: "menu" }, @attrs), &)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module PhlexKit
2
+ # See navigation_menu.rb.
3
+ class NavigationMenuItem < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&)
6
+ li(**mix({ class: "pk-navigation-menu-item", data: { phlex_kit__menubar_target: "menu" } }, @attrs), &)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # Plain link item (top-level or inside a panel). See navigation_menu.rb.
3
+ class NavigationMenuLink < BaseComponent
4
+ def initialize(href: "#", **attrs)
5
+ @href = href
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&)
10
+ a(**mix({ class: "pk-navigation-menu-link", href: @href }, @attrs), &)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See navigation_menu.rb.
3
+ class NavigationMenuList < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = ul(**mix({ class: "pk-navigation-menu-list" }, @attrs), &)
6
+ end
7
+ end