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,310 @@
1
+ /* Co-located with combobox.rb — UI::Combobox. ruby_ui's structure + Stimulus
2
+ kept; the @floating-ui/native-popover positioning replaced with plain CSS
3
+ (panel below the trigger), and Tailwind with vanilla CSS on the palette
4
+ tokens. Geometry from ruby_ui (h-9 trigger, rounded-lg panel, p-1 list,
5
+ px-2 py-1 label rows, group labels via ::before content(attr(label))).
6
+ Theme tokens come from the global stylesheet. */
7
+ .pk-combobox { position: relative; width: 100%; }
8
+
9
+ /* Closed-state trigger (mirrors .pk-select-trigger geometry). */
10
+ .pk-combobox-trigger {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: space-between;
14
+ width: 100%;
15
+ height: 2rem;
16
+ white-space: nowrap;
17
+ border-radius: var(--pk-radius);
18
+ border: 1px solid var(--pk-input);
19
+ background: transparent;
20
+ color: var(--pk-text);
21
+ padding: .5rem 1rem;
22
+ font: inherit;
23
+ font-size: .875rem;
24
+ cursor: pointer;
25
+ transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
26
+ }
27
+ .pk-combobox-trigger:hover { background: var(--pk-accent); }
28
+ .pk-combobox-trigger:focus-visible {
29
+ outline: none;
30
+ border-color: var(--pk-brand);
31
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
32
+ }
33
+ .pk-combobox-trigger:disabled,
34
+ .pk-combobox-trigger[aria-disabled="true"] { pointer-events: none; opacity: .5; }
35
+ .pk-combobox-trigger-content { overflow: hidden; text-overflow: ellipsis; }
36
+ .pk-combobox-trigger-icon { flex: none; width: 1rem; height: 1rem; margin-left: .5rem; opacity: .5; }
37
+
38
+ /* Dropdown panel — opens below the trigger (no floating-ui); the controller
39
+ toggles .pk-hidden and matches its width to the trigger. */
40
+ .pk-combobox-popover {
41
+ position: absolute;
42
+ top: 100%;
43
+ left: 0;
44
+ z-index: 50;
45
+ margin-top: .25rem;
46
+ border: 1px solid var(--pk-border);
47
+ border-radius: var(--pk-radius);
48
+ background: var(--pk-surface);
49
+ color: var(--pk-text);
50
+ box-shadow: 0 4px 16px rgba(0, 0, 0, .4);
51
+ }
52
+
53
+ /* Search row. */
54
+ .pk-combobox-search {
55
+ display: flex;
56
+ align-items: center;
57
+ padding: 0 .75rem;
58
+ border-bottom: 1px solid var(--pk-border);
59
+ color: var(--pk-muted);
60
+ }
61
+ .pk-combobox-search-icon { flex: none; width: 1rem; height: 1rem; margin-right: .5rem; opacity: .5; }
62
+ .pk-combobox-search-input {
63
+ width: 100%;
64
+ height: 2rem;
65
+ padding: .75rem 0;
66
+ border: none;
67
+ outline: none;
68
+ background: transparent;
69
+ color: var(--pk-text);
70
+ font: inherit;
71
+ font-size: .875rem;
72
+ }
73
+ .pk-combobox-search-input::placeholder { color: var(--pk-muted); }
74
+ .pk-combobox-search-input:disabled { cursor: not-allowed; opacity: .5; }
75
+
76
+ /* Option list. */
77
+ .pk-combobox-list {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: .25rem;
81
+ padding: .25rem;
82
+ max-height: 18rem;
83
+ overflow-y: auto;
84
+ color: var(--pk-text);
85
+ }
86
+
87
+ /* Labelled group — the label renders from the `label` attribute, and the group
88
+ hides itself (pure CSS) when filtering leaves no visible item inside. */
89
+ .pk-combobox-group { display: none; }
90
+ .pk-combobox-group:has(label:not(.pk-hidden)) {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: .25rem;
94
+ padding: .25rem 0;
95
+ border-bottom: 1px solid var(--pk-border);
96
+ }
97
+ .pk-combobox-group::before {
98
+ content: attr(label);
99
+ padding: .375rem .5rem;
100
+ font-size: .75rem;
101
+ font-weight: 500;
102
+ font-style: normal;
103
+ color: var(--pk-muted);
104
+ }
105
+
106
+ /* Option row (a <label> around the input + text + indicator). */
107
+ .pk-combobox-item {
108
+ display: flex;
109
+ flex-direction: row;
110
+ align-items: center;
111
+ gap: .5rem;
112
+ width: 100%;
113
+ border-radius: calc(var(--pk-radius) - 4px);
114
+ padding: .5rem;
115
+ font-size: .875rem;
116
+ cursor: pointer;
117
+ user-select: none;
118
+ outline: none;
119
+ transition: background-color .12s ease;
120
+ }
121
+ .pk-combobox-item:hover,
122
+ .pk-combobox-item:has(:checked) { background: var(--pk-accent); }
123
+ .pk-combobox-item[aria-current="true"] {
124
+ background: var(--pk-accent);
125
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
126
+ }
127
+ .pk-combobox-item:has(:disabled) { opacity: .5; cursor: not-allowed; }
128
+ .pk-combobox-item > span, .pk-combobox-item > div {
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ }
132
+ .pk-combobox-item > svg { pointer-events: none; flex: none; width: 1rem; height: 1rem; }
133
+
134
+ /* The real inputs that carry the selection. */
135
+ .pk-combobox-checkbox, .pk-combobox-radio {
136
+ flex: none;
137
+ width: 1rem;
138
+ height: 1rem;
139
+ margin: 0;
140
+ accent-color: var(--pk-brand);
141
+ }
142
+ .pk-combobox-checkbox { border-radius: .125rem; }
143
+ .pk-combobox-checkbox:disabled, .pk-combobox-radio:disabled { cursor: not-allowed; opacity: .5; }
144
+ .pk-combobox-checkbox:focus-visible, .pk-combobox-radio:focus-visible {
145
+ outline: none;
146
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
147
+ }
148
+
149
+ /* Trailing ✓, visible while the row's input is checked. */
150
+ .pk-combobox-item-indicator {
151
+ margin-left: auto;
152
+ width: 1rem;
153
+ height: 1rem;
154
+ flex: none;
155
+ opacity: 0;
156
+ }
157
+ .pk-combobox-item :checked ~ .pk-combobox-item-indicator { opacity: 1; }
158
+
159
+ /* Filter empty state. */
160
+ .pk-combobox-empty {
161
+ padding: 1.5rem 0;
162
+ text-align: center;
163
+ font-size: .875rem;
164
+ }
165
+
166
+ /* Input trigger — the field doubles as the filter (autocomplete style). */
167
+ .pk-combobox-input-trigger {
168
+ display: flex;
169
+ align-items: center;
170
+ width: 100%;
171
+ height: 2rem;
172
+ border: 1px solid var(--pk-input);
173
+ border-radius: var(--pk-radius);
174
+ background: transparent;
175
+ padding: 0 .75rem;
176
+ font-size: .875rem;
177
+ cursor: text;
178
+ transition: border-color .15s ease, box-shadow .15s ease;
179
+ }
180
+ .pk-combobox-input-trigger:focus-within {
181
+ border-color: var(--pk-brand);
182
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
183
+ }
184
+ .pk-combobox-input-trigger-field {
185
+ flex: 1;
186
+ min-width: 2rem;
187
+ border: 0;
188
+ outline: none;
189
+ padding: 0;
190
+ background: transparent;
191
+ color: var(--pk-text);
192
+ font: inherit;
193
+ font-size: .875rem;
194
+ }
195
+ .pk-combobox-input-trigger-field::placeholder { color: var(--pk-muted); }
196
+ .pk-combobox-input-trigger-field:disabled { cursor: not-allowed; }
197
+ .pk-combobox-input-trigger-icon {
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: center;
201
+ flex: none;
202
+ width: 1.5rem;
203
+ height: 1.5rem;
204
+ border-radius: calc(var(--pk-radius) - 4px);
205
+ color: var(--pk-muted);
206
+ transition: background-color .15s ease, color .15s ease;
207
+ }
208
+ .pk-combobox-input-trigger-icon:hover { background: var(--pk-accent); color: var(--pk-text); }
209
+ .pk-combobox-input-trigger-icon svg { pointer-events: none; width: 1rem; height: 1rem; }
210
+
211
+ /* Badge trigger — checked options render as removable chips; the controller
212
+ toggles .has-badges so the row grows past one line. */
213
+ .pk-combobox-badge-trigger {
214
+ display: flex;
215
+ flex-wrap: wrap;
216
+ align-items: center;
217
+ gap: .25rem;
218
+ width: 100%;
219
+ height: 2rem;
220
+ border: 1px solid var(--pk-input);
221
+ border-radius: var(--pk-radius);
222
+ background: transparent;
223
+ padding: 0 .75rem;
224
+ font-size: .875rem;
225
+ cursor: text;
226
+ transition: border-color .15s ease, box-shadow .15s ease;
227
+ }
228
+ .pk-combobox-badge-trigger:focus-within {
229
+ border-color: var(--pk-brand);
230
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
231
+ }
232
+ .pk-combobox-badge-trigger.has-badges {
233
+ height: auto;
234
+ min-height: 2rem;
235
+ padding-top: .375rem;
236
+ padding-bottom: .375rem;
237
+ }
238
+ .pk-combobox-badge-container {
239
+ display: flex;
240
+ flex-wrap: wrap;
241
+ gap: .25rem;
242
+ }
243
+ .pk-combobox-badge-input {
244
+ flex: 1;
245
+ min-width: 2rem;
246
+ border: 0;
247
+ outline: none;
248
+ padding: 0;
249
+ background: transparent;
250
+ color: var(--pk-text);
251
+ font: inherit;
252
+ font-size: .875rem;
253
+ }
254
+ .pk-combobox-badge-input::placeholder { color: var(--pk-muted); }
255
+
256
+ /* One selected-value chip. */
257
+ .pk-combobox-badge {
258
+ display: inline-flex;
259
+ align-items: center;
260
+ gap: .25rem;
261
+ border: 1px solid var(--pk-border);
262
+ border-radius: calc(var(--pk-radius) - 2px);
263
+ background: var(--pk-surface-2);
264
+ padding: .125rem .375rem;
265
+ font-size: .75rem;
266
+ font-weight: 500;
267
+ color: var(--pk-text);
268
+ }
269
+ .pk-combobox-badge-remove {
270
+ display: inline-flex;
271
+ align-items: center;
272
+ justify-content: center;
273
+ width: .875rem;
274
+ height: .875rem;
275
+ border: 0;
276
+ border-radius: .125rem;
277
+ padding: 0;
278
+ background: transparent;
279
+ color: var(--pk-muted);
280
+ font: inherit;
281
+ line-height: 1;
282
+ cursor: pointer;
283
+ transition: color .15s ease, background-color .15s ease;
284
+ }
285
+ .pk-combobox-badge-remove:hover {
286
+ color: var(--pk-text);
287
+ background: color-mix(in oklab, var(--pk-text) 10%, transparent);
288
+ }
289
+
290
+ /* × clearing the whole selection (hidden until something is checked). */
291
+ .pk-combobox-clear-button {
292
+ display: flex;
293
+ align-items: center;
294
+ justify-content: center;
295
+ flex: none;
296
+ margin-left: auto;
297
+ border: 0;
298
+ border-radius: .125rem;
299
+ padding: 0;
300
+ background: transparent;
301
+ color: var(--pk-muted);
302
+ cursor: pointer;
303
+ transition: color .15s ease;
304
+ }
305
+ .pk-combobox-clear-button:hover { color: var(--pk-text); }
306
+ .pk-combobox-clear-button:focus-visible {
307
+ outline: none;
308
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
309
+ }
310
+ .pk-combobox-clear-button svg { width: .875rem; height: .875rem; }
@@ -0,0 +1,33 @@
1
+ module PhlexKit
2
+ # Combobox (searchable multi/single select), ported from ruby_ui's
3
+ # RubyUI::Combobox. Selection state lives in real checkbox/radio inputs, so the
4
+ # value submits with the form; the trigger label, filtering, keyboard nav and
5
+ # toggle-all live in the phlex-kit--combobox controller. Like PhlexKit::Select, the
6
+ # @floating-ui/dom + native-popover positioning is replaced with CSS (panel
7
+ # anchored below the trigger, click-outside via window action). Compose
8
+ # Combobox(term:) > trigger + Popover(SearchInput + List(ListGroup >
9
+ # Item(Checkbox|Radio + text + ItemIndicator)) + EmptyState [+
10
+ # ToggleAllCheckbox]). Three trigger flavours: ComboboxTrigger (button +
11
+ # popover search), ComboboxInputTrigger (the field is the filter), and
12
+ # ComboboxBadgeTrigger (multi-select chips + ComboboxClearButton) — the
13
+ # latter two shipped unfinished in ruby_ui (no controller support) and are
14
+ # completed here. Tailwind → vanilla `.pk-combobox*` (combobox.css).
15
+ class Combobox < BaseComponent
16
+ def initialize(term: nil, **attrs)
17
+ @term = term
18
+ @attrs = attrs
19
+ end
20
+
21
+ def view_template(&)
22
+ div(**mix({
23
+ role: "combobox",
24
+ class: "pk-combobox",
25
+ data: {
26
+ controller: "phlex-kit--combobox",
27
+ phlex_kit__combobox_term_value: @term,
28
+ action: "turbo:morph@window->phlex-kit--combobox#updateTriggerContent click@window->phlex-kit--combobox#onClickOutside"
29
+ }
30
+ }, @attrs), &)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module PhlexKit
2
+ # Selected-value chip, ported from ruby_ui's RubyUI::ComboboxBadge. The
3
+ # controller builds chips with this class into the ComboboxBadgeTrigger's
4
+ # container client-side; render the component directly when server-rendering
5
+ # an initial selection. See combobox.rb.
6
+ class ComboboxBadge < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&)
12
+ span(**mix({ class: "pk-combobox-badge" }, @attrs), &)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ module PhlexKit
2
+ # Multi-select trigger for PhlexKit::Combobox that shows each checked option as
3
+ # a removable chip: the controller renders ComboboxBadge-style chips into the
4
+ # badge container, backspace in the empty field removes the last one, and
5
+ # `clear_button: true` adds a ComboboxClearButton. Ported from ruby_ui's
6
+ # RubyUI::ComboboxBadgeTrigger; upstream shipped the markup without controller
7
+ # support, so the badge rendering / backspace-removal / clear-all behaviour is
8
+ # PhlexKit's completion of it. Keyboard actions live here because focus stays
9
+ # in this field (see ComboboxInputTrigger). See combobox.rb.
10
+ class ComboboxBadgeTrigger < BaseComponent
11
+ def initialize(placeholder: "", clear_button: false, **attrs)
12
+ @placeholder = placeholder
13
+ @clear_button = clear_button
14
+ @attrs = attrs
15
+ end
16
+
17
+ def view_template
18
+ div(**mix({
19
+ class: "pk-combobox-badge-trigger",
20
+ aria: { haspopup: "listbox", expanded: "false" },
21
+ data: {
22
+ placeholder: @placeholder,
23
+ phlex_kit__combobox_target: "trigger",
24
+ action: [
25
+ "click->phlex-kit--combobox#openPopover",
26
+ "focusin->phlex-kit--combobox#openPopover",
27
+ "keydown.down->phlex-kit--combobox#keyDownPressed",
28
+ "keydown.up->phlex-kit--combobox#keyUpPressed",
29
+ "keydown.enter->phlex-kit--combobox#keyEnterPressed",
30
+ "keydown.esc->phlex-kit--combobox#closePopover:prevent"
31
+ ].join(" ")
32
+ }
33
+ }, @attrs)) do
34
+ div(class: "pk-combobox-badge-container pk-hidden", data: { phlex_kit__combobox_target: "badgeContainer" })
35
+ input(
36
+ type: :text,
37
+ placeholder: @placeholder,
38
+ autocomplete: "off",
39
+ autocorrect: "off",
40
+ spellcheck: "false",
41
+ class: "pk-combobox-badge-input",
42
+ data: {
43
+ phlex_kit__combobox_target: "badgeInput",
44
+ action: [
45
+ "keyup->phlex-kit--combobox#filterItems",
46
+ "input->phlex-kit--combobox#filterItems",
47
+ "keydown.backspace->phlex-kit--combobox#handleBadgeInputBackspace"
48
+ ].join(" ")
49
+ }
50
+ )
51
+ render ComboboxClearButton.new if @clear_button
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module PhlexKit
2
+ # Multi-select input inside a PhlexKit::ComboboxItem. A real checkbox (pass
3
+ # `name:`/`value:`/`checked:`), so the selection submits with the form.
4
+ # See combobox.rb.
5
+ class ComboboxCheckbox < BaseComponent
6
+ def initialize(**attrs)
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template
11
+ input(**mix({
12
+ type: :checkbox,
13
+ class: "pk-combobox-checkbox",
14
+ data: {
15
+ phlex_kit__combobox_target: "input",
16
+ action: "phlex-kit--combobox#inputChanged"
17
+ }
18
+ }, @attrs))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ module PhlexKit
2
+ # × button clearing the whole combobox selection — hidden until something is
3
+ # checked. Ported from ruby_ui's RubyUI::ComboboxClearButton; upstream
4
+ # referenced a clearAll action its controller never defined, so the behaviour
5
+ # is PhlexKit's completion of it. See combobox.rb.
6
+ class ComboboxClearButton < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template
12
+ button(**mix({
13
+ type: :button,
14
+ class: "pk-combobox-clear-button pk-hidden",
15
+ aria: { label: "Clear selection" },
16
+ data: {
17
+ phlex_kit__combobox_target: "clearButton",
18
+ action: "phlex-kit--combobox#clearAll"
19
+ }
20
+ }, @attrs)) do
21
+ svg(
22
+ xmlns: "http://www.w3.org/2000/svg",
23
+ width: "24",
24
+ height: "24",
25
+ viewbox: "0 0 24 24",
26
+ fill: "none",
27
+ stroke: "currentColor",
28
+ "stroke-width": "2",
29
+ "stroke-linecap": "round",
30
+ "stroke-linejoin": "round",
31
+ "aria-hidden": "true"
32
+ ) do |s|
33
+ s.path(d: "M18 6 6 18")
34
+ s.path(d: "m6 6 12 12")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ module PhlexKit
2
+ # "No results" placeholder — hidden until filtering empties the list.
3
+ # See combobox.rb.
4
+ class ComboboxEmptyState < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&)
10
+ div(**mix({
11
+ role: "presentation",
12
+ class: "pk-combobox-empty pk-hidden",
13
+ data: { phlex_kit__combobox_target: "emptyState" }
14
+ }, @attrs), &)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ module PhlexKit
2
+ # Single-field trigger for PhlexKit::Combobox — the input doubles as the filter
3
+ # (autocomplete style): focusing or clicking opens the popover, typing
4
+ # filters, arrows/enter pick, and the chosen option's text becomes the field
5
+ # value. Ported from ruby_ui's RubyUI::ComboboxInputTrigger; upstream shipped
6
+ # the markup without controller support, so the behaviour (open-on-focus,
7
+ # keyboard nav from the field, selection reflection) is PhlexKit's completion
8
+ # of it. Keyboard actions live here because focus stays in this field — the
9
+ # popover's own bindings only fire for the button+search layout.
10
+ # See combobox.rb.
11
+ class ComboboxInputTrigger < BaseComponent
12
+ def initialize(placeholder: "", **attrs)
13
+ @placeholder = placeholder
14
+ @attrs = attrs
15
+ end
16
+
17
+ def view_template
18
+ div(**mix({
19
+ class: "pk-combobox-input-trigger",
20
+ aria: { haspopup: "listbox", expanded: "false" },
21
+ data: {
22
+ placeholder: @placeholder,
23
+ phlex_kit__combobox_target: "trigger",
24
+ action: [
25
+ "click->phlex-kit--combobox#openPopover",
26
+ "focusin->phlex-kit--combobox#openPopover",
27
+ "keydown.down->phlex-kit--combobox#keyDownPressed",
28
+ "keydown.up->phlex-kit--combobox#keyUpPressed",
29
+ "keydown.enter->phlex-kit--combobox#keyEnterPressed",
30
+ "keydown.esc->phlex-kit--combobox#closePopover:prevent"
31
+ ].join(" ")
32
+ }
33
+ }, @attrs)) do
34
+ input(
35
+ type: :text,
36
+ placeholder: @placeholder,
37
+ autocomplete: "off",
38
+ autocorrect: "off",
39
+ spellcheck: "false",
40
+ class: "pk-combobox-input-trigger-field",
41
+ data: {
42
+ phlex_kit__combobox_target: "inputTrigger",
43
+ action: "keyup->phlex-kit--combobox#filterItems input->phlex-kit--combobox#filterItems"
44
+ }
45
+ )
46
+ chevron_icon
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def chevron_icon
53
+ span(class: "pk-combobox-input-trigger-icon") do
54
+ svg(
55
+ xmlns: "http://www.w3.org/2000/svg",
56
+ width: "24",
57
+ height: "24",
58
+ viewbox: "0 0 24 24",
59
+ fill: "none",
60
+ stroke: "currentColor",
61
+ "stroke-width": "2",
62
+ "stroke-linecap": "round",
63
+ "stroke-linejoin": "round",
64
+ "aria-hidden": "true"
65
+ ) { |s| s.path(d: "m6 9 6 6 6-6") }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ module PhlexKit
2
+ # One option row — a <label> wrapping a ComboboxCheckbox/ComboboxRadio, the
3
+ # option text, and optionally a ComboboxItemIndicator. Being a label, clicking
4
+ # anywhere toggles the input; the controller hides it (.pk-hidden) when
5
+ # filtered out. See combobox.rb.
6
+ class ComboboxItem < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&)
12
+ label(**mix({
13
+ class: "pk-combobox-item",
14
+ role: "option",
15
+ data: { phlex_kit__combobox_target: "item" }
16
+ }, @attrs), &)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module PhlexKit
2
+ # Trailing ✓ in a PhlexKit::ComboboxItem, visible (pure CSS) while the item's
3
+ # input is checked. See combobox.rb.
4
+ class ComboboxItemIndicator < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template
10
+ svg(**mix({
11
+ xmlns: "http://www.w3.org/2000/svg",
12
+ width: "24",
13
+ height: "24",
14
+ viewbox: "0 0 24 24",
15
+ fill: "none",
16
+ stroke: "currentColor",
17
+ "stroke-width": "2",
18
+ "stroke-linecap": "round",
19
+ "stroke-linejoin": "round",
20
+ class: "pk-combobox-item-indicator",
21
+ "aria-hidden": "true"
22
+ }, @attrs)) do |s|
23
+ s.path(d: "M20 6 9 17l-5-5")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Scrollable option list in a PhlexKit::ComboboxPopover. See combobox.rb.
3
+ class ComboboxList < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&)
9
+ div(**mix({ class: "pk-combobox-list", role: "listbox" }, @attrs), &)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module PhlexKit
2
+ # Labelled option group. Pass `label:` as a plain attribute — CSS renders it via
3
+ # ::before content(attr(label)), and the group auto-hides (pure CSS :has) when
4
+ # filtering leaves it with no visible items. See combobox.rb.
5
+ class ComboboxListGroup < BaseComponent
6
+ def initialize(**attrs)
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template(&)
11
+ div(**mix({ class: "pk-combobox-group", role: "group" }, @attrs), &)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module PhlexKit
2
+ # The combobox dropdown panel. Upstream uses the native Popover API +
3
+ # @floating-ui; here it's a CSS-positioned panel toggled with .pk-hidden (the
4
+ # keyboard nav actions ride on it, since focus sits in the search input
5
+ # inside). See combobox.rb.
6
+ class ComboboxPopover < BaseComponent
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&)
12
+ div(**mix({
13
+ class: "pk-combobox-popover pk-hidden",
14
+ data: {
15
+ phlex_kit__combobox_target: "popover",
16
+ action: [
17
+ "keydown.down->phlex-kit--combobox#keyDownPressed",
18
+ "keydown.up->phlex-kit--combobox#keyUpPressed",
19
+ "keydown.enter->phlex-kit--combobox#keyEnterPressed",
20
+ "keydown.esc->phlex-kit--combobox#closePopover:prevent",
21
+ "resize@window->phlex-kit--combobox#updatePopoverWidth"
22
+ ].join(" ")
23
+ }
24
+ }, @attrs), &)
25
+ end
26
+ end
27
+ end