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,35 @@
1
+ module PhlexKit
2
+ # Data table, ported from ruby_ui's RubyUI::Table. Presentational, no JS.
3
+ # Multi-part, like PhlexKit::Card — the lead Table wraps a <table> in an overflow
4
+ # container; the rest map 1:1 onto the table elements:
5
+ #
6
+ # render PhlexKit::Table.new do
7
+ # render PhlexKit::TableHeader.new do
8
+ # render PhlexKit::TableRow.new do
9
+ # render PhlexKit::TableHead.new { "Name" }
10
+ # render PhlexKit::TableHead.new { "Email" }
11
+ # end
12
+ # end
13
+ # render PhlexKit::TableBody.new do
14
+ # @users.each do |u|
15
+ # render PhlexKit::TableRow.new do
16
+ # render PhlexKit::TableCell.new { u.name }
17
+ # render PhlexKit::TableCell.new { u.email }
18
+ # end
19
+ # end
20
+ # end
21
+ # end
22
+ #
23
+ # Tailwind → vanilla `.pk-table-*` (table.css), palette from the global tokens.
24
+ class Table < BaseComponent
25
+ def initialize(**attrs)
26
+ @attrs = attrs
27
+ end
28
+
29
+ def view_template(&block)
30
+ div(class: "pk-table-wrapper") do
31
+ table(**mix({ class: "pk-table" }, @attrs), &block)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # The <tbody> of a PhlexKit::Table. See table.rb.
3
+ class TableBody < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ tbody(**mix({ class: "pk-table-body" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # The <caption> of a PhlexKit::Table (muted, below the table). See table.rb.
3
+ class TableCaption < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ caption(**mix({ class: "pk-table-caption" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # A body cell (<td>) in a PhlexKit::TableRow. See table.rb.
3
+ class TableCell < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ td(**mix({ class: "pk-table-cell" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # The <tfoot> of a PhlexKit::Table (e.g. a totals row). See table.rb.
3
+ class TableFooter < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ tfoot(**mix({ class: "pk-table-footer" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # A header cell (<th>) in a PhlexKit::TableHeader row. See table.rb.
3
+ class TableHead < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ th(**mix({ class: "pk-table-head" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # The <thead> of a PhlexKit::Table. See table.rb.
3
+ class TableHeader < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ thead(**mix({ class: "pk-table-header" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module PhlexKit
2
+ # A <tr> in a PhlexKit::Table — bottom border + hover highlight. See table.rb.
3
+ class TableRow < BaseComponent
4
+ def initialize(**attrs)
5
+ @attrs = attrs
6
+ end
7
+
8
+ def view_template(&block)
9
+ tr(**mix({ class: "pk-table-row" }, @attrs), &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ /* Co-located with tabs.rb. Theme tokens from the global stylesheet. */
2
+ .pk-tabs-list {
3
+ display: inline-flex; height: 2.25rem; align-items: center; justify-content: center;
4
+ border-radius: var(--pk-radius); background: var(--pk-surface-2); padding: .25rem; color: var(--pk-muted);
5
+ }
6
+ .pk-tabs-trigger {
7
+ display: inline-flex; align-items: center; justify-content: center; white-space: nowrap;
8
+ border-radius: calc(var(--pk-radius) - 2px); padding: .25rem .75rem; font-size: .875rem; font-weight: 500;
9
+ background: none; border: 0; cursor: pointer; color: inherit; transition: all .15s ease;
10
+ }
11
+ .pk-tabs-trigger[data-state="active"] { background: var(--pk-bg); color: var(--pk-text); box-shadow: 0 1px 2px rgb(0 0 0 / .1); }
12
+ .pk-tabs-trigger:disabled { pointer-events: none; opacity: .5; }
13
+ .pk-tabs-content { margin-top: .5rem; }
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # Tabbed panels. Ported from ruby_ui's RubyUI::Tabs. Compose Tabs(default:) >
3
+ # (TabsList > TabsTrigger(value:)) + TabsContent(value:). phlex-kit--tabs.
4
+ class Tabs < BaseComponent
5
+ def initialize(default: nil, **attrs)
6
+ @default = default
7
+ @attrs = attrs
8
+ end
9
+ def view_template(&)
10
+ div(**mix({ class: "pk-tabs", data: { controller: "phlex-kit--tabs", phlex_kit__tabs_active_value: @default } }, @attrs), &)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module PhlexKit
2
+ class TabsContent < BaseComponent
3
+ def initialize(value:, **attrs)
4
+ @value = value
5
+ @attrs = attrs
6
+ end
7
+ def view_template(&)
8
+ div(**mix({ class: "pk-tabs-content pk-hidden", role: "tabpanel", data: { phlex_kit__tabs_target: "content", value: @value } }, @attrs), &)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module PhlexKit
2
+ class TabsList < BaseComponent
3
+ def initialize(**attrs) = (@attrs = attrs)
4
+ def view_template(&) = div(**mix({ class: "pk-tabs-list", role: "tablist" }, @attrs), &)
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ module PhlexKit
2
+ class TabsTrigger < BaseComponent
3
+ def initialize(value:, as: :button, **attrs)
4
+ @value = value
5
+ @as = as.to_sym
6
+ @attrs = attrs
7
+ end
8
+ def view_template(&)
9
+ base = { class: "pk-tabs-trigger", role: "tab", data: { phlex_kit__tabs_target: "trigger", action: "click->phlex-kit--tabs#show", value: @value } }
10
+ if @as == :a
11
+ a(**mix(base, @attrs), &)
12
+ else
13
+ button(**mix(base.merge(type: :button), @attrs), &)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ /* Co-located with textarea.rb — UI::Textarea. Same vanilla treatment as
2
+ .pk-input (input.css): ruby_ui's geometry (rounded-md / px-3 / text-sm /
3
+ shadow / ring-on-focus), our palette tokens, height left to the `rows`
4
+ attribute exactly as ruby_ui does. Theme tokens come from the global
5
+ stylesheet. */
6
+ .pk-textarea {
7
+ width: 100%;
8
+ border-radius: calc(var(--pk-radius) - 2px);
9
+ border: 1px solid var(--pk-input);
10
+ background: var(--pk-bg);
11
+ color: var(--pk-text);
12
+ padding: .5rem .75rem;
13
+ font: inherit;
14
+ font-size: .875rem;
15
+ transition: color .15s ease, border-color .15s ease, box-shadow .15s ease;
16
+ }
17
+ .pk-textarea::placeholder { color: var(--pk-muted); }
18
+ .pk-textarea:focus-visible {
19
+ outline: none;
20
+ border-color: var(--pk-brand);
21
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
22
+ }
23
+ .pk-textarea:disabled { cursor: not-allowed; opacity: .5; }
24
+ .pk-textarea[aria-invalid="true"] {
25
+ border-color: var(--pk-red);
26
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 30%, transparent);
27
+ }
@@ -0,0 +1,24 @@
1
+ module PhlexKit
2
+ # Multi-line text input, ported from ruby_ui's RubyUI::Textarea. Same plain-CSS
3
+ # treatment (and form-field wiring) as PhlexKit::Input — `rows:` sets the initial height,
4
+ # the yielded block is the textarea's content (an edit form's current value),
5
+ # and every other attr (`name:`, `id:`, `placeholder:`, `**on(...)`) passes
6
+ # through via `mix`. No VARIANTS. Styled by `.pk-textarea` (textarea.css).
7
+ class Textarea < BaseComponent
8
+ def initialize(rows: 4, **attrs)
9
+ @rows = rows
10
+ @attrs = attrs
11
+ end
12
+
13
+ def view_template(&block)
14
+ textarea(**mix({
15
+ rows: @rows,
16
+ class: "pk-textarea",
17
+ data: {
18
+ phlex_kit__form_field_target: "input",
19
+ action: "input->phlex-kit--form-field#onInput invalid->phlex-kit--form-field#onInvalid"
20
+ }
21
+ }, @attrs), &block)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module PhlexKit
2
+ # Light/dark theme switch built on Toggle. Ported from ruby_ui's RubyUI::
3
+ # ThemeToggle. The sibling phlex-kit--theme-toggle controller listens for the
4
+ # toggle's change event and flips :root[data-theme] (matching the token system).
5
+ class ThemeToggle < BaseComponent
6
+ def initialize(**attrs) = (@attrs = attrs)
7
+ def view_template(&block)
8
+ render PhlexKit::Toggle.new(
9
+ aria: { label: "Toggle theme" },
10
+ wrapper: { data: { controller: "phlex-kit--theme-toggle", action: "phlex-kit--toggle:change->phlex-kit--theme-toggle#apply" } },
11
+ **@attrs, &block
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,163 @@
1
+ /* Co-located with toast_region.rb — UI::Toast (region / item / parts). ruby_ui's
2
+ sonner-style toaster kept (phlex-kit--toaster clones <template> skeletons and
3
+ stacks toasts via inline --y-offset/--scale/--opacity; phlex-kit--toast runs the
4
+ timer + swipe); Tailwind replaced with vanilla CSS on the palette tokens.
5
+ Geometry from ruby_ui (356px stack, rounded-lg, p-4, 13px text, positions via
6
+ [data-position=…]). Theme tokens come from the global stylesheet. */
7
+ .pk-toast-region {
8
+ position: fixed;
9
+ z-index: 100;
10
+ padding: 1.5rem;
11
+ pointer-events: none;
12
+ }
13
+ .pk-toast-region[data-position="top-left"] { top: 0; left: 0; }
14
+ .pk-toast-region[data-position="top-center"] { top: 0; left: 50%; transform: translateX(-50%); }
15
+ .pk-toast-region[data-position="top-right"] { top: 0; right: 0; }
16
+ .pk-toast-region[data-position="bottom-left"] { bottom: 0; left: 0; }
17
+ .pk-toast-region[data-position="bottom-center"] { bottom: 0; left: 50%; transform: translateX(-50%); }
18
+ .pk-toast-region[data-position="bottom-right"] { bottom: 0; right: 0; }
19
+
20
+ /* The <ol> stack. The toaster sets its min-height as toasts come and go. */
21
+ .pk-toast-list {
22
+ position: relative;
23
+ margin: 0;
24
+ padding: 0;
25
+ list-style: none;
26
+ width: 356px;
27
+ max-width: 100%;
28
+ pointer-events: auto;
29
+ }
30
+
31
+ /* One toast. The stacking transform/opacity/z-index are inline (set by the
32
+ toaster's reflow); this only styles the card and the state transitions. */
33
+ .pk-toast {
34
+ position: absolute;
35
+ left: 0;
36
+ right: 0;
37
+ display: flex;
38
+ align-items: center;
39
+ gap: .375rem;
40
+ width: 356px;
41
+ max-width: 100%;
42
+ border: 1px solid var(--pk-border);
43
+ border-radius: var(--pk-radius);
44
+ background: var(--pk-surface);
45
+ color: var(--pk-text);
46
+ padding: 1rem;
47
+ font-size: 13px;
48
+ box-shadow: 0 4px 12px rgba(0, 0, 0, .1);
49
+ transition: transform .3s ease-out, opacity .3s ease-out;
50
+ will-change: transform;
51
+ opacity: var(--opacity, 1);
52
+ }
53
+ .pk-toast[data-state="pending"], .pk-toast[data-state="closing"] { opacity: 0; }
54
+ .pk-toast[data-swipe="move"] { transition: none; }
55
+
56
+ /* Room for the corner close button (region-wide, or per-spawned-toast). */
57
+ .pk-toast-region[data-close-button="true"] .pk-toast,
58
+ .pk-toast.pk-toast-with-close { padding-right: 2.5rem; }
59
+
60
+ .pk-toast-icon {
61
+ position: relative;
62
+ flex: none;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: flex-start;
66
+ width: 1rem;
67
+ height: 1rem;
68
+ margin-left: -3px;
69
+ margin-right: .25rem;
70
+ color: var(--pk-text);
71
+ }
72
+ .pk-toast-icon-svg { width: 1rem; height: 1rem; margin-left: -1px; }
73
+ .pk-toast-icon-svg.spin { animation: pk-toast-spin 1s linear infinite; }
74
+ @keyframes pk-toast-spin { to { transform: rotate(360deg); } }
75
+
76
+ /* Title + description column in the skeletons. */
77
+ .pk-toast-body {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: .125rem;
81
+ flex: 1;
82
+ min-width: 0;
83
+ }
84
+ .pk-toast-title { font-weight: 500; line-height: 1.5; }
85
+ .pk-toast-title:empty { display: none; }
86
+ .pk-toast-description { font-weight: 400; line-height: 1.4; color: var(--pk-muted); }
87
+ .pk-toast-description:empty { display: none; }
88
+
89
+ /* Filled action button ("Undo"). */
90
+ .pk-toast-action {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ flex: none;
95
+ height: 1.5rem;
96
+ margin-left: auto;
97
+ border: 0;
98
+ border-radius: calc(var(--pk-radius) - 4px);
99
+ padding: 0 .5rem;
100
+ font: inherit;
101
+ font-size: .75rem;
102
+ font-weight: 500;
103
+ cursor: pointer;
104
+ background: var(--pk-text);
105
+ color: var(--pk-bg);
106
+ transition: opacity .15s ease;
107
+ }
108
+ .pk-toast-action:hover { opacity: .9; }
109
+ .pk-toast-action:focus {
110
+ outline: none;
111
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
112
+ }
113
+ .pk-toast-action:disabled { pointer-events: none; opacity: .5; }
114
+
115
+ /* Subtle cancel button. */
116
+ .pk-toast-cancel {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ flex: none;
121
+ height: 1.5rem;
122
+ margin-left: auto;
123
+ border: 0;
124
+ border-radius: calc(var(--pk-radius) - 4px);
125
+ padding: 0 .5rem;
126
+ font: inherit;
127
+ font-size: .75rem;
128
+ font-weight: 500;
129
+ cursor: pointer;
130
+ background: color-mix(in oklab, var(--pk-text) 10%, transparent);
131
+ color: var(--pk-text);
132
+ transition: background-color .15s ease;
133
+ }
134
+ .pk-toast-cancel:hover { background: color-mix(in oklab, var(--pk-text) 15%, transparent); }
135
+ .pk-toast-cancel:focus {
136
+ outline: none;
137
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
138
+ }
139
+
140
+ /* Corner × close. */
141
+ .pk-toast-close {
142
+ position: absolute;
143
+ right: .5rem;
144
+ top: .5rem;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: 1.5rem;
149
+ height: 1.5rem;
150
+ padding: 0;
151
+ border: 0;
152
+ border-radius: calc(var(--pk-radius) - 2px);
153
+ background: transparent;
154
+ cursor: pointer;
155
+ color: color-mix(in oklab, var(--pk-text) 60%, transparent);
156
+ transition: background-color .15s ease, color .15s ease;
157
+ }
158
+ .pk-toast-close:hover { background: var(--pk-accent); color: var(--pk-text); }
159
+ .pk-toast-close:focus {
160
+ outline: none;
161
+ box-shadow: 0 0 0 1px color-mix(in oklab, var(--pk-ring) 50%, transparent);
162
+ }
163
+ .pk-toast-close svg { width: .875rem; height: .875rem; }
@@ -0,0 +1,21 @@
1
+ module PhlexKit
2
+ # Flash-key → toast-variant mapping, ported from ruby_ui's RubyUI::Toast.
3
+ # Used by PhlexKit::ToastRegion when rendering `flash:` server-side:
4
+ # `PhlexKit::Toast.flash_variant(:notice) #=> :info`. Unknown keys fall back
5
+ # to :default (flash keys are app-defined, so this map is deliberately
6
+ # permissive — unlike the component VARIANTS, which fail loud).
7
+ module Toast
8
+ FLASH_VARIANTS = {
9
+ "notice" => :info,
10
+ "alert" => :warning,
11
+ "success" => :success,
12
+ "error" => :error,
13
+ "warning" => :warning,
14
+ "info" => :info
15
+ }.freeze
16
+
17
+ def self.flash_variant(key)
18
+ FLASH_VARIANTS[key.to_s] || :default
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module PhlexKit
2
+ # Filled action button in a toast (e.g. "Undo"). Pass `on:` a Stimulus action
3
+ # string for server-rendered toasts; client-spawned toasts get their onClick
4
+ # wired by the toaster when it clones the action slot template.
5
+ # See toast_region.rb.
6
+ class ToastAction < BaseComponent
7
+ def initialize(label:, on: nil, **attrs)
8
+ @label = label
9
+ @on = on
10
+ @attrs = attrs
11
+ end
12
+
13
+ def view_template
14
+ data = { slot: "action" }
15
+ data[:action] = @on if @on
16
+ button(**mix({ type: :button, class: "pk-toast-action", data: data }, @attrs)) { @label }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module PhlexKit
2
+ # Subtle dismiss button in a toast (the "Cancel" counterpart to ToastAction).
3
+ # Dismisses via the phlex-kit--toast controller. See toast_region.rb.
4
+ class ToastCancel < BaseComponent
5
+ def initialize(label:, **attrs)
6
+ @label = label
7
+ @attrs = attrs
8
+ end
9
+
10
+ def view_template
11
+ button(**mix({
12
+ type: :button,
13
+ class: "pk-toast-cancel",
14
+ data: { slot: "cancel", action: "click->phlex-kit--toast#dismiss" }
15
+ }, @attrs)) { @label }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ module PhlexKit
2
+ # The ×-in-the-corner close button, shown when the region has close_button:
3
+ # true (or a spawn asks for one). See toast_region.rb.
4
+ class ToastClose < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template
10
+ button(**mix({
11
+ type: :button,
12
+ class: "pk-toast-close",
13
+ aria: { label: "Close toast" },
14
+ data: { slot: "close", action: "click->phlex-kit--toast#dismiss" }
15
+ }, @attrs)) do
16
+ svg(
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ width: "14",
19
+ height: "14",
20
+ viewbox: "0 0 24 24",
21
+ fill: "none",
22
+ stroke: "currentColor",
23
+ "stroke-width": "2",
24
+ "stroke-linecap": "round",
25
+ "stroke-linejoin": "round",
26
+ "aria-hidden": "true"
27
+ ) do |s|
28
+ s.path(d: "M18 6 6 18")
29
+ s.path(d: "m6 6 12 12")
30
+ end
31
+ span(class: "pk-sr-only") { "Close" }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module PhlexKit
2
+ # Muted secondary line under a PhlexKit::ToastTitle. The toaster removes it from a
3
+ # cloned skeleton when the spawn has no description. See toast_region.rb.
4
+ class ToastDescription < BaseComponent
5
+ def initialize(**attrs)
6
+ @attrs = attrs
7
+ end
8
+
9
+ def view_template(&)
10
+ div(**mix({ class: "pk-toast-description", data: { slot: "description" } }, @attrs), &)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ module PhlexKit
2
+ # Leading status icon for a PhlexKit::ToastItem. Renders nothing for :default /
3
+ # nil (matching ruby_ui — only the status-ful variants get an icon); the
4
+ # toaster swaps its innerHTML when a toast mutates (e.g. loading → success).
5
+ # See toast_region.rb.
6
+ class ToastIcon < BaseComponent
7
+ def initialize(variant: nil, **attrs)
8
+ @variant = variant&.to_sym
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template
13
+ return unless renderable?
14
+ span(**mix({ class: "pk-toast-icon", data: { slot: "icon" } }, @attrs)) do
15
+ svg(
16
+ xmlns: "http://www.w3.org/2000/svg",
17
+ width: "16",
18
+ height: "16",
19
+ viewbox: "0 0 24 24",
20
+ fill: "none",
21
+ stroke: "currentColor",
22
+ "stroke-width": "2",
23
+ "stroke-linecap": "round",
24
+ "stroke-linejoin": "round",
25
+ class: svg_classes,
26
+ "aria-hidden": "true"
27
+ ) { |s| paths(s) }
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def renderable?
34
+ %i[success error warning info loading].include?(@variant)
35
+ end
36
+
37
+ def svg_classes
38
+ [ "pk-toast-icon-svg", ("spin" if @variant == :loading) ].compact.join(" ")
39
+ end
40
+
41
+ def paths(s)
42
+ case @variant
43
+ when :success
44
+ s.circle(cx: "12", cy: "12", r: "10")
45
+ s.path(d: "m9 12 2 2 4-4")
46
+ when :error
47
+ s.path(d: "M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z")
48
+ s.path(d: "m15 9-6 6")
49
+ s.path(d: "m9 9 6 6")
50
+ when :warning
51
+ s.path(d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3")
52
+ s.path(d: "M12 9v4")
53
+ s.path(d: "M12 17h.01")
54
+ when :info
55
+ s.circle(cx: "12", cy: "12", r: "10")
56
+ s.path(d: "M12 16v-4")
57
+ s.path(d: "M12 8h.01")
58
+ when :loading
59
+ s.path(d: "M21 12a9 9 0 1 1-6.219-8.56")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,70 @@
1
+ module PhlexKit
2
+ # A single toast <li> — rendered server-side (flash) or cloned from a skeleton
3
+ # template by the toaster. The phlex-kit--toast controller runs the auto-close
4
+ # timer (pause on hover), swipe-to-dismiss, and Escape. The toaster stacks it
5
+ # via inline --y-offset/--scale/--opacity. See toast_region.rb.
6
+ class ToastItem < BaseComponent
7
+ VARIANTS = {
8
+ default: "default",
9
+ success: "success",
10
+ error: "error",
11
+ warning: "warning",
12
+ info: "info",
13
+ loading: "loading"
14
+ }.freeze
15
+
16
+ # Variants announced assertively (role="alert" instead of "status").
17
+ ALERT_VARIANTS = %i[error].freeze
18
+
19
+ def initialize(
20
+ variant: :default,
21
+ id: nil,
22
+ duration: nil,
23
+ dismissible: true,
24
+ invert: false,
25
+ on_dismiss: nil,
26
+ on_auto_close: nil,
27
+ **attrs
28
+ )
29
+ @variant = variant.to_sym
30
+ @id = id
31
+ @duration = duration
32
+ @dismissible = dismissible
33
+ @invert = invert
34
+ @on_dismiss = on_dismiss
35
+ @on_auto_close = on_auto_close
36
+ @attrs = attrs
37
+ end
38
+
39
+ def view_template(&)
40
+ li(**mix(item_attrs, @attrs), &)
41
+ end
42
+
43
+ private
44
+
45
+ def item_attrs
46
+ data = {
47
+ variant: VARIANTS.fetch(@variant),
48
+ state: "pending",
49
+ swipe: "none",
50
+ controller: "phlex-kit--toast",
51
+ phlex_kit__toaster_target: "toast",
52
+ phlex_kit__toast_dismissible_value: @dismissible.to_s,
53
+ phlex_kit__toast_invert_value: @invert.to_s
54
+ }
55
+ data[:phlex_kit__toast_duration_value] = @duration.to_s if @duration
56
+ data[:phlex_kit__toast_on_dismiss_value] = @on_dismiss if @on_dismiss
57
+ data[:phlex_kit__toast_on_auto_close_value] = @on_auto_close if @on_auto_close
58
+
59
+ attrs = {
60
+ role: ALERT_VARIANTS.include?(@variant) ? "alert" : "status",
61
+ tabindex: "0",
62
+ class: "pk-toast",
63
+ aria: { atomic: "true" },
64
+ data: data
65
+ }
66
+ attrs[:id] = @id if @id
67
+ attrs
68
+ end
69
+ end
70
+ end