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
+ module PhlexKit
2
+ # Footer of a PhlexKit::AlertDialog (Cancel + the destructive submit). See alert_dialog.rb.
3
+ class AlertDialogFooter < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ div(**mix({ class: "pk-alert-dialog-footer" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Header region of a PhlexKit::AlertDialog (Title + Description). See alert_dialog.rb.
3
+ class AlertDialogHeader < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ div(**mix({ class: "pk-alert-dialog-header" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Title of a PhlexKit::AlertDialog. See alert_dialog.rb.
3
+ class AlertDialogTitle < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ h2(**mix({ class: "pk-alert-dialog-title" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # Opens a PhlexKit::AlertDialog (wrap a PhlexKit::Button). See alert_dialog.rb.
3
+ class AlertDialogTrigger < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ div(**mix({ class: "pk-alert-dialog-trigger", data: { action: "click->phlex-kit--alert-dialog#open" } }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ /* Co-located with aspect_ratio.rb — UI::AspectRatio. ruby_ui's padding-bottom
2
+ ratio box; the inner layer fills it and an <img> child covers it. Vanilla CSS;
3
+ theme tokens come from the global stylesheet. */
4
+ .pk-aspect-ratio { position: relative; width: 100%; }
5
+ .pk-aspect-ratio-inner { position: absolute; inset: 0; }
6
+ .pk-aspect-ratio-inner > img {
7
+ position: absolute;
8
+ inset: 0;
9
+ height: 100%;
10
+ width: 100%;
11
+ object-fit: cover;
12
+ }
@@ -0,0 +1,27 @@
1
+ module PhlexKit
2
+ # Constrains content (typically an <img>) to a fixed aspect ratio, ported from
3
+ # ruby_ui's RubyUI::AspectRatio. `aspect_ratio:` is a "w/h" string (e.g. "16/9",
4
+ # "1/1"); the classic padding-bottom box keeps the ratio. Tailwind → vanilla
5
+ # `.pk-aspect-ratio*` (aspect_ratio.css).
6
+ class AspectRatio < BaseComponent
7
+ def initialize(aspect_ratio: "16/9", **attrs)
8
+ unless aspect_ratio.is_a?(String) && aspect_ratio.include?("/")
9
+ raise ArgumentError, %(aspect_ratio must be a "w/h" string (e.g. "16/9"))
10
+ end
11
+ @aspect_ratio = aspect_ratio
12
+ @attrs = attrs
13
+ end
14
+
15
+ def view_template(&block)
16
+ div(class: "pk-aspect-ratio", style: "padding-bottom: #{padding_bottom}%") do
17
+ div(**mix({ class: "pk-aspect-ratio-inner" }, @attrs), &block)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def padding_bottom
24
+ @aspect_ratio.split("/").map(&:to_i).reverse.reduce(&:fdiv) * 100
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ /* Co-located with attachment.rb — UI::Attachment, shadcn parity: bordered
2
+ file chip (icon/preview tile, name + meta, trailing actions). */
3
+ .pk-attachment {
4
+ display: flex;
5
+ align-items: center;
6
+ gap: .5rem;
7
+ max-width: 100%;
8
+ border: 1px solid var(--pk-border);
9
+ border-radius: calc(var(--pk-radius) + 4px);
10
+ background: var(--pk-surface);
11
+ padding: .5rem;
12
+ font-size: .875rem;
13
+ }
14
+ .pk-attachment-media {
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ flex: none;
19
+ width: 2.5rem;
20
+ height: 2.5rem;
21
+ border-radius: var(--pk-radius);
22
+ background: var(--pk-surface-2);
23
+ color: var(--pk-muted);
24
+ overflow: hidden;
25
+ }
26
+ .pk-attachment-media svg { width: 1rem; height: 1rem; }
27
+ .pk-attachment-media img { width: 100%; height: 100%; object-fit: cover; }
28
+ .pk-attachment-content {
29
+ display: flex;
30
+ flex: 1;
31
+ min-width: 0;
32
+ flex-direction: column;
33
+ gap: .125rem;
34
+ }
35
+ .pk-attachment-title {
36
+ font-weight: 500;
37
+ white-space: nowrap;
38
+ overflow: hidden;
39
+ text-overflow: ellipsis;
40
+ }
41
+ .pk-attachment-description { color: var(--pk-muted); font-size: .75rem; }
42
+ .pk-attachment-actions { display: flex; align-items: center; gap: .25rem; flex: none; }
43
+ .pk-attachment-action {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ width: 1.5rem;
48
+ height: 1.5rem;
49
+ padding: 0;
50
+ border: 0;
51
+ border-radius: calc(var(--pk-radius) - 4px);
52
+ background: transparent;
53
+ color: var(--pk-muted);
54
+ cursor: pointer;
55
+ transition: background-color .15s ease, color .15s ease;
56
+ }
57
+ .pk-attachment-action:hover { background: var(--pk-accent); color: var(--pk-text); }
58
+ .pk-attachment-action:focus-visible {
59
+ outline: none;
60
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
61
+ }
62
+ .pk-attachment-action svg { width: .875rem; height: .875rem; }
@@ -0,0 +1,16 @@
1
+ module PhlexKit
2
+ # File chip, ported from shadcn/ui's Attachment (an AI-chat-era addition, not
3
+ # in ruby_ui): a bordered row of AttachmentMedia (icon or image preview) +
4
+ # AttachmentContent(AttachmentTitle + AttachmentDescription) +
5
+ # AttachmentActions(AttachmentAction — e.g. a remove ×).
6
+ # `.pk-attachment*` (attachment.css).
7
+ class Attachment < BaseComponent
8
+ def initialize(**attrs)
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&)
13
+ div(**mix({ class: "pk-attachment" }, @attrs), &)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module PhlexKit
2
+ # Small ghost icon button in an Attachment (remove/download). Give it an
3
+ # aria-label; block content is the icon (defaults to ×). See attachment.rb.
4
+ class AttachmentAction < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&block)
10
+ button(**mix({ type: :button, class: "pk-attachment-action" }, @attrs)) do
11
+ if block
12
+ yield
13
+ else
14
+ svg(xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 24 24", fill: "none",
15
+ stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round",
16
+ "stroke-linejoin": "round", "aria-hidden": "true") do |s|
17
+ s.path(d: "M18 6 6 18")
18
+ s.path(d: "m6 6 12 12")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See attachment.rb.
3
+ class AttachmentActions < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-attachment-actions" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See attachment.rb.
3
+ class AttachmentContent < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-attachment-content" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See attachment.rb.
3
+ class AttachmentDescription < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-attachment-description" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module PhlexKit
2
+ # Square preview tile of an Attachment — a file-type icon or an <img>.
3
+ # See attachment.rb.
4
+ class AttachmentMedia < BaseComponent
5
+ def initialize(**attrs) = (@attrs = attrs)
6
+ def view_template(&) = div(**mix({ class: "pk-attachment-media" }, @attrs), &)
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # See attachment.rb.
3
+ class AttachmentTitle < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-attachment-title" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ /* Co-located with avatar.rb — UI::Avatar (+ Image / Fallback). ruby_ui's Tailwind
2
+ → vanilla CSS on the palette tokens. The controller toggles `.hidden` between
3
+ image and fallback. Theme tokens come from the global stylesheet. */
4
+ .pk-avatar {
5
+ position: relative;
6
+ display: inline-flex;
7
+ flex-shrink: 0;
8
+ overflow: hidden;
9
+ border-radius: 999px;
10
+ height: 2rem;
11
+ width: 2rem;
12
+ font-size: .875rem;
13
+ }
14
+ .pk-avatar.xs { height: 1rem; width: 1rem; font-size: .5rem; }
15
+ .pk-avatar.sm { height: 1.5rem; width: 1.5rem; font-size: .75rem; }
16
+ .pk-avatar.lg { height: 2.5rem; width: 2.5rem; font-size: 1rem; }
17
+ .pk-avatar.xl { height: 5rem; width: 5rem; font-size: 1.875rem; }
18
+
19
+ .pk-avatar-image { aspect-ratio: 1 / 1; height: 100%; width: 100%; object-fit: cover; }
20
+ .pk-avatar-fallback {
21
+ display: flex;
22
+ height: 100%;
23
+ width: 100%;
24
+ align-items: center;
25
+ justify-content: center;
26
+ border-radius: 999px;
27
+ background: var(--pk-surface-2);
28
+ font-weight: 500;
29
+ }
30
+ .pk-avatar-image.hidden, .pk-avatar-fallback.hidden { display: none; }
31
+
32
+ /* Overlapping row (shadcn's -space-x-2 grouped avatars). */
33
+ .pk-avatar-group { display: flex; }
34
+ .pk-avatar-group .pk-avatar { box-shadow: 0 0 0 2px var(--pk-bg); }
35
+ .pk-avatar-group .pk-avatar:not(:first-child) { margin-left: -.5rem; }
@@ -0,0 +1,25 @@
1
+ module PhlexKit
2
+ # Avatar, ported from ruby_ui's RubyUI::Avatar — a round image with a fallback
3
+ # (initials). Keeps ruby_ui's `phlex-kit--avatar` Stimulus controller (shows the
4
+ # image once it loads, else the fallback). Compose AvatarImage + AvatarFallback;
5
+ # fallback-only is fine (the controller no-ops without an image). `SIZES.fetch`
6
+ # fails loud. Tailwind → vanilla `.pk-avatar*` (avatar.css).
7
+ class Avatar < BaseComponent
8
+ SIZES = { xs: "xs", sm: "sm", md: nil, lg: "lg", xl: "xl" }.freeze
9
+
10
+ def initialize(size: :md, **attrs)
11
+ @size = size.to_sym
12
+ @attrs = attrs
13
+ end
14
+
15
+ def view_template(&block)
16
+ span(**mix({ class: classes, data: { controller: "phlex-kit--avatar" } }, @attrs), &block)
17
+ end
18
+
19
+ private
20
+
21
+ def classes
22
+ [ "pk-avatar", SIZES.fetch(@size) ].compact.join(" ")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # The fallback shown when a PhlexKit::Avatar has no image (or it fails to load) —
3
+ # typically the user's initials. See avatar.rb.
4
+ class AvatarFallback < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&block)
10
+ span(**mix({ class: "pk-avatar-fallback", data: { phlex_kit__avatar_target: "fallback" } }, @attrs), &block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module PhlexKit
2
+ # Overlapping row of Avatars (shadcn's grouped avatars). See avatar.rb.
3
+ class AvatarGroup < BaseComponent
4
+ def initialize(**attrs) = (@attrs = attrs)
5
+ def view_template(&) = div(**mix({ class: "pk-avatar-group" }, @attrs), &)
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ module PhlexKit
2
+ # The image inside a PhlexKit::Avatar. NOT lazy-loaded on purpose (the controller
3
+ # hides a not-yet-loaded image so the fallback shows; a lazy image that never
4
+ # paints would never fire `load`). See avatar.rb.
5
+ class AvatarImage < BaseComponent
6
+ def initialize(src:, alt: "", **attrs)
7
+ @src = src
8
+ @alt = alt
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template
13
+ img(**mix({
14
+ src: @src,
15
+ alt: @alt,
16
+ class: "pk-avatar-image",
17
+ data: {
18
+ phlex_kit__avatar_target: "image",
19
+ action: "load->phlex-kit--avatar#showImage error->phlex-kit--avatar#showFallback"
20
+ }
21
+ }, @attrs))
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ /* Co-located with badge.rb — UI::Badge. ruby_ui's Tailwind replaced with vanilla
2
+ CSS on the palette tokens, at variant/size parity. Geometry from ruby_ui
3
+ (inline-flex, rounded-md, ring-1 ring-inset → 1px border, bg tint, px-2 py-1
4
+ text-xs). Theme tokens come from the global stylesheet. */
5
+ .pk-badge {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ border-radius: calc(var(--pk-radius) - 2px);
9
+ padding: .25rem .5rem;
10
+ font-size: .75rem;
11
+ font-weight: 500;
12
+ line-height: 1;
13
+ border: 1px solid transparent;
14
+ }
15
+
16
+ /* Sizes (md is the base above). */
17
+ .pk-badge.sm { padding: .125rem .375rem; font-size: .7rem; }
18
+ .pk-badge.lg { padding: .25rem .75rem; font-size: .875rem; }
19
+
20
+ /* Variants — colored text over a faint tint of the same hue + matching ring. */
21
+ .pk-badge.primary {
22
+ color: var(--pk-brand);
23
+ background: color-mix(in oklab, var(--pk-brand) 8%, transparent);
24
+ border-color: color-mix(in oklab, var(--pk-brand) 25%, transparent);
25
+ }
26
+ .pk-badge.secondary {
27
+ color: var(--pk-muted);
28
+ background: color-mix(in oklab, var(--pk-muted) 12%, transparent);
29
+ border-color: color-mix(in oklab, var(--pk-muted) 25%, transparent);
30
+ }
31
+ .pk-badge.outline {
32
+ color: var(--pk-text);
33
+ background: transparent;
34
+ border-color: var(--pk-border);
35
+ }
36
+ .pk-badge.destructive {
37
+ color: var(--pk-red);
38
+ background: color-mix(in oklab, var(--pk-red) 10%, transparent);
39
+ border-color: color-mix(in oklab, var(--pk-red) 25%, transparent);
40
+ }
41
+ .pk-badge.success {
42
+ color: var(--pk-green);
43
+ background: color-mix(in oklab, var(--pk-green) 10%, transparent);
44
+ border-color: color-mix(in oklab, var(--pk-green) 25%, transparent);
45
+ }
46
+ .pk-badge.warning {
47
+ color: var(--pk-amber);
48
+ background: color-mix(in oklab, var(--pk-amber) 10%, transparent);
49
+ border-color: color-mix(in oklab, var(--pk-amber) 25%, transparent);
50
+ }
@@ -0,0 +1,39 @@
1
+ module PhlexKit
2
+ # Pill/label badge, ported from ruby_ui's RubyUI::Badge to variant/size parity.
3
+ # Same kit shape as PhlexKit::Button — a `variant:`/`size:` selector plus attrs/block
4
+ # passed through to the <span>. ruby_ui's semantic variants are ported; its long
5
+ # raw-colour palette (red/blue/teal/…) is omitted as unused. Tailwind → vanilla
6
+ # `.pk-badge` CSS (badge.css). `VARIANTS.fetch` fails loud.
7
+ class Badge < BaseComponent
8
+ VARIANTS = {
9
+ primary: "primary",
10
+ secondary: "secondary",
11
+ outline: "outline",
12
+ destructive: "destructive",
13
+ success: "success",
14
+ warning: "warning"
15
+ }.freeze
16
+
17
+ SIZES = {
18
+ sm: "sm",
19
+ md: nil,
20
+ lg: "lg"
21
+ }.freeze
22
+
23
+ def initialize(variant: :primary, size: :md, **attrs)
24
+ @variant = variant.to_sym
25
+ @size = size.to_sym
26
+ @attrs = attrs
27
+ end
28
+
29
+ def view_template(&block)
30
+ span(**mix({ class: classes }, @attrs), &block)
31
+ end
32
+
33
+ private
34
+
35
+ def classes
36
+ [ "pk-badge", VARIANTS.fetch(@variant), SIZES.fetch(@size) ].compact.join(" ")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ /* Co-located with breadcrumb.rb. Theme tokens from the global stylesheet. */
2
+ .pk-breadcrumb-list {
3
+ display: flex; flex-wrap: wrap; align-items: center;
4
+ gap: .375rem; word-break: break-word;
5
+ font-size: .875rem; color: var(--pk-muted);
6
+ }
7
+ .pk-breadcrumb-item { display: inline-flex; align-items: center; gap: .375rem; }
8
+ .pk-breadcrumb-link { color: inherit; text-decoration: none; transition: color .15s ease; }
9
+ .pk-breadcrumb-link:hover { color: var(--pk-text); }
10
+ .pk-breadcrumb-page { font-weight: 400; color: var(--pk-text); }
11
+ .pk-breadcrumb-separator { display: inline-flex; }
12
+ .pk-breadcrumb-separator > svg { width: .875rem; height: .875rem; }
13
+ .pk-breadcrumb-ellipsis { display: flex; height: 2.25rem; width: 2.25rem; align-items: center; justify-content: center; }
14
+ .pk-breadcrumb-ellipsis > svg { width: 1rem; height: 1rem; }
15
+ .pk-sr-only {
16
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
17
+ overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
18
+ }
@@ -0,0 +1,8 @@
1
+ module PhlexKit
2
+ # Navigational hierarchy trail. Ported from ruby_ui's RubyUI::Breadcrumb. Compose
3
+ # Breadcrumb > BreadcrumbList > BreadcrumbItem (+ Link/Page/Separator/Ellipsis).
4
+ class Breadcrumb < BaseComponent
5
+ def initialize(**attrs) = (@attrs = attrs)
6
+ def view_template(&) = nav(**mix({ class: "pk-breadcrumb", aria: { label: "breadcrumb" } }, @attrs), &)
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module PhlexKit
2
+ class BreadcrumbEllipsis < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template
5
+ span(**mix({ class: "pk-breadcrumb-ellipsis", role: "presentation", aria: { hidden: true } }, @attrs)) do
6
+ svg(xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 24 24", fill: "none", stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round") do |s|
7
+ s.circle(cx: "12", cy: "12", r: "1")
8
+ s.circle(cx: "19", cy: "12", r: "1")
9
+ s.circle(cx: "5", cy: "12", r: "1")
10
+ end
11
+ span(class: "pk-sr-only") { "More" }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class BreadcrumbItem < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = li(**mix({ class: "pk-breadcrumb-item" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module PhlexKit
2
+ class BreadcrumbLink < BaseComponent
3
+ def initialize(href: "#", **attrs)
4
+ @href = href
5
+ @attrs = attrs
6
+ end
7
+ def view_template(&) = a(**mix({ href: @href, class: "pk-breadcrumb-link" }, @attrs), &)
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class BreadcrumbList < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = ol(**mix({ class: "pk-breadcrumb-list" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module PhlexKit
2
+ class BreadcrumbPage < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&)
5
+ span(**mix({ class: "pk-breadcrumb-page", role: "link", aria: { disabled: true, current: "page" } }, @attrs), &)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module PhlexKit
2
+ class BreadcrumbSeparator < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&block)
5
+ li(**mix({ class: "pk-breadcrumb-separator", role: "presentation", aria: { hidden: true } }, @attrs)) do
6
+ if block then yield else svg(xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 24 24", fill: "none", stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round") { |s| s.path(d: "m9 18 6-6-6-6") } end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ /* Co-located with bubble.rb. Theme tokens from the global stylesheet. The variant
2
+ colours the child [data-slot=bubble-content]. */
3
+ .pk-bubble {
4
+ position: relative; display: flex; width: fit-content; min-width: 0;
5
+ flex-direction: column; gap: .25rem; max-width: 80%;
6
+ }
7
+ .pk-bubble[data-align="end"] { align-self: flex-end; }
8
+ .pk-bubble-content {
9
+ width: fit-content; max-width: 100%; min-width: 0; overflow: hidden;
10
+ overflow-wrap: break-word; border: 1px solid transparent; border-radius: 1.5rem;
11
+ padding: .625rem .75rem; font-size: .875rem; line-height: 1.625;
12
+ }
13
+ .pk-bubble[data-align="end"] > .pk-bubble-content { align-self: flex-end; }
14
+ /* variants */
15
+ .pk-bubble[data-variant="default"] > .pk-bubble-content { background: var(--pk-brand); color: var(--pk-brand-ink); }
16
+ .pk-bubble[data-variant="secondary"] > .pk-bubble-content { background: var(--pk-surface-2); color: var(--pk-text); }
17
+ .pk-bubble[data-variant="muted"] > .pk-bubble-content { background: var(--pk-surface-2); color: var(--pk-text); }
18
+ .pk-bubble[data-variant="tinted"] > .pk-bubble-content { background: color-mix(in oklab, var(--pk-brand) 18%, transparent); color: var(--pk-text); }
19
+ .pk-bubble[data-variant="outline"] > .pk-bubble-content { background: var(--pk-bg); border-color: var(--pk-border); color: var(--pk-text); }
20
+ .pk-bubble[data-variant="ghost"] > .pk-bubble-content { background: transparent; border-radius: 0; padding: 0; }
21
+ .pk-bubble[data-variant="destructive"] > .pk-bubble-content { background: color-mix(in oklab, var(--pk-red) 12%, transparent); color: var(--pk-red); }
22
+ .pk-bubble-group { display: flex; min-width: 0; flex-direction: column; gap: .5rem; }
23
+ .pk-bubble-reactions {
24
+ position: absolute; z-index: 10; display: flex; width: fit-content;
25
+ align-items: center; justify-content: center; border-radius: 9999px;
26
+ box-shadow: 0 0 0 3px var(--pk-surface); background: var(--pk-surface-2);
27
+ flex: none; gap: .25rem; padding: .125rem .375rem; font-size: .875rem;
28
+ }
29
+ .pk-bubble-reactions[data-side="bottom"] { bottom: 0; transform: translateY(75%); }
30
+ .pk-bubble-reactions[data-side="top"] { top: 0; transform: translateY(-75%); }
31
+ .pk-bubble-reactions[data-align="end"] { right: .75rem; }
32
+ .pk-bubble-reactions[data-align="start"] { left: .75rem; }
@@ -0,0 +1,17 @@
1
+ module PhlexKit
2
+ # Chat message bubble. Ported from ruby_ui's RubyUI::Bubble. The variant colours
3
+ # the child BubbleContent via a data-slot selector; align: :end flips it to the
4
+ # trailing edge. Compose Bubble > BubbleContent (+ BubbleReactions).
5
+ class Bubble < BaseComponent
6
+ VARIANTS = %i[default secondary muted tinted outline ghost destructive].freeze
7
+ def initialize(variant: :default, align: :start, **attrs)
8
+ @variant = variant.to_sym
9
+ raise KeyError, "unknown Bubble variant #{@variant}" unless VARIANTS.include?(@variant)
10
+ @align = align.to_sym
11
+ @attrs = attrs
12
+ end
13
+ def view_template(&)
14
+ div(**mix({ class: "pk-bubble", data: { slot: "bubble", variant: @variant, align: @align } }, @attrs), &)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module PhlexKit
2
+ class BubbleContent < BaseComponent
3
+ def initialize(as: :div, **attrs)
4
+ @as = as
5
+ @attrs = attrs
6
+ end
7
+ def view_template(&)
8
+ send(@as, **mix({ class: "pk-bubble-content", data: { slot: "bubble-content" } }, @attrs), &)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class BubbleGroup < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-bubble-group", data: { slot: "bubble-group" } }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ class BubbleReactions < BaseComponent
3
+ def initialize(side: :bottom, align: :end, **attrs)
4
+ @side = side.to_sym
5
+ @align = align.to_sym
6
+ @attrs = attrs
7
+ end
8
+ def view_template(&)
9
+ div(**mix({ class: "pk-bubble-reactions", data: { slot: "bubble-reactions", side: @side, align: @align } }, @attrs), &)
10
+ end
11
+ end
12
+ end