poetry-ui 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitattributes +6 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/THIRD_PARTY_NOTICES.md +201 -0
- data/app/components/poetry/ui/accordion/component.html.erb +3 -0
- data/app/components/poetry/ui/accordion/component.rb +197 -0
- data/app/components/poetry/ui/accordion/preview.rb +39 -0
- data/app/components/poetry/ui/accordion/style.rb +37 -0
- data/app/components/poetry/ui/alert/component.html.erb +6 -0
- data/app/components/poetry/ui/alert/component.rb +61 -0
- data/app/components/poetry/ui/alert/preview.rb +35 -0
- data/app/components/poetry/ui/alert/style.rb +24 -0
- data/app/components/poetry/ui/alert_dialog/component.html.erb +17 -0
- data/app/components/poetry/ui/alert_dialog/component.rb +208 -0
- data/app/components/poetry/ui/alert_dialog/preview.rb +48 -0
- data/app/components/poetry/ui/alert_dialog/style.rb +46 -0
- data/app/components/poetry/ui/aspect_ratio/component.rb +63 -0
- data/app/components/poetry/ui/aspect_ratio/preview.rb +29 -0
- data/app/components/poetry/ui/aspect_ratio/style.rb +13 -0
- data/app/components/poetry/ui/attachment/component.html.erb +13 -0
- data/app/components/poetry/ui/attachment/component.rb +139 -0
- data/app/components/poetry/ui/attachment/preview.rb +83 -0
- data/app/components/poetry/ui/attachment/style.rb +59 -0
- data/app/components/poetry/ui/autocomplete/component.html.erb +16 -0
- data/app/components/poetry/ui/autocomplete/component.rb +193 -0
- data/app/components/poetry/ui/autocomplete/preview.rb +39 -0
- data/app/components/poetry/ui/autocomplete/style.rb +18 -0
- data/app/components/poetry/ui/avatar/component.rb +103 -0
- data/app/components/poetry/ui/avatar/group_preview.html.erb +11 -0
- data/app/components/poetry/ui/avatar/preview.rb +50 -0
- data/app/components/poetry/ui/avatar/style.rb +34 -0
- data/app/components/poetry/ui/badge/component.rb +73 -0
- data/app/components/poetry/ui/badge/preview.rb +54 -0
- data/app/components/poetry/ui/badge/style.rb +35 -0
- data/app/components/poetry/ui/breadcrumb/component.html.erb +32 -0
- data/app/components/poetry/ui/breadcrumb/component.rb +102 -0
- data/app/components/poetry/ui/breadcrumb/dropdown_crumb_preview.html.erb +21 -0
- data/app/components/poetry/ui/breadcrumb/preview.rb +45 -0
- data/app/components/poetry/ui/breadcrumb/style.rb +25 -0
- data/app/components/poetry/ui/bubble/component.html.erb +4 -0
- data/app/components/poetry/ui/bubble/component.rb +106 -0
- data/app/components/poetry/ui/bubble/preview.rb +62 -0
- data/app/components/poetry/ui/bubble/style.rb +47 -0
- data/app/components/poetry/ui/button/component.html.erb +14 -0
- data/app/components/poetry/ui/button/component.rb +168 -0
- data/app/components/poetry/ui/button/preview.rb +94 -0
- data/app/components/poetry/ui/button/style.rb +41 -0
- data/app/components/poetry/ui/button_group/component.rb +70 -0
- data/app/components/poetry/ui/button_group/popup_triggers_preview.html.erb +21 -0
- data/app/components/poetry/ui/button_group/preview.rb +46 -0
- data/app/components/poetry/ui/button_group/style.rb +27 -0
- data/app/components/poetry/ui/button_group/text_prefix_preview.html.erb +7 -0
- data/app/components/poetry/ui/calendar/component.html.erb +72 -0
- data/app/components/poetry/ui/calendar/component.rb +366 -0
- data/app/components/poetry/ui/calendar/preview.rb +55 -0
- data/app/components/poetry/ui/calendar/style.rb +55 -0
- data/app/components/poetry/ui/card/component.html.erb +11 -0
- data/app/components/poetry/ui/card/component.rb +90 -0
- data/app/components/poetry/ui/card/preview.rb +29 -0
- data/app/components/poetry/ui/card/style.rb +23 -0
- data/app/components/poetry/ui/carousel/component.html.erb +17 -0
- data/app/components/poetry/ui/carousel/component.rb +179 -0
- data/app/components/poetry/ui/carousel/preview.rb +67 -0
- data/app/components/poetry/ui/carousel/style.rb +39 -0
- data/app/components/poetry/ui/checkbox/component.html.erb +22 -0
- data/app/components/poetry/ui/checkbox/component.rb +179 -0
- data/app/components/poetry/ui/checkbox/preview.rb +68 -0
- data/app/components/poetry/ui/checkbox/style.rb +28 -0
- data/app/components/poetry/ui/clipboard_text/component.html.erb +15 -0
- data/app/components/poetry/ui/clipboard_text/component.rb +154 -0
- data/app/components/poetry/ui/clipboard_text/preview.rb +24 -0
- data/app/components/poetry/ui/clipboard_text/style.rb +23 -0
- data/app/components/poetry/ui/code_block/component.html.erb +13 -0
- data/app/components/poetry/ui/code_block/component.rb +141 -0
- data/app/components/poetry/ui/code_block/preview.rb +40 -0
- data/app/components/poetry/ui/code_block/style.rb +38 -0
- data/app/components/poetry/ui/collapsible/component.html.erb +4 -0
- data/app/components/poetry/ui/collapsible/component.rb +134 -0
- data/app/components/poetry/ui/collapsible/preview.rb +24 -0
- data/app/components/poetry/ui/combobox/component.html.erb +49 -0
- data/app/components/poetry/ui/combobox/component.rb +1086 -0
- data/app/components/poetry/ui/combobox/preview.rb +164 -0
- data/app/components/poetry/ui/combobox/style.rb +148 -0
- data/app/components/poetry/ui/command/component.html.erb +20 -0
- data/app/components/poetry/ui/command/component.rb +526 -0
- data/app/components/poetry/ui/command/dialog_component.html.erb +14 -0
- data/app/components/poetry/ui/command/dialog_component.rb +206 -0
- data/app/components/poetry/ui/command/dialog_preview.rb +39 -0
- data/app/components/poetry/ui/command/preview.rb +121 -0
- data/app/components/poetry/ui/command/style.rb +78 -0
- data/app/components/poetry/ui/composable_trigger.rb +63 -0
- data/app/components/poetry/ui/context_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/context_menu/component.rb +358 -0
- data/app/components/poetry/ui/context_menu/preview.rb +111 -0
- data/app/components/poetry/ui/context_menu/style.rb +59 -0
- data/app/components/poetry/ui/data_table/component.html.erb +78 -0
- data/app/components/poetry/ui/data_table/component.rb +277 -0
- data/app/components/poetry/ui/data_table/preview.rb +68 -0
- data/app/components/poetry/ui/data_table/state.rb +115 -0
- data/app/components/poetry/ui/data_table/style.rb +42 -0
- data/app/components/poetry/ui/date_field/component.html.erb +7 -0
- data/app/components/poetry/ui/date_field/component.rb +209 -0
- data/app/components/poetry/ui/date_field/preview.rb +42 -0
- data/app/components/poetry/ui/date_field/style.rb +35 -0
- data/app/components/poetry/ui/date_picker/component.html.erb +23 -0
- data/app/components/poetry/ui/date_picker/component.rb +242 -0
- data/app/components/poetry/ui/date_picker/preview.rb +31 -0
- data/app/components/poetry/ui/date_picker/style.rb +25 -0
- data/app/components/poetry/ui/deferred/component.html.erb +16 -0
- data/app/components/poetry/ui/deferred/component.rb +90 -0
- data/app/components/poetry/ui/deferred/preview.rb +24 -0
- data/app/components/poetry/ui/dialog/component.html.erb +21 -0
- data/app/components/poetry/ui/dialog/component.rb +194 -0
- data/app/components/poetry/ui/dialog/preview.rb +33 -0
- data/app/components/poetry/ui/dialog/style.rb +30 -0
- data/app/components/poetry/ui/drawer/component.html.erb +16 -0
- data/app/components/poetry/ui/drawer/component.rb +207 -0
- data/app/components/poetry/ui/drawer/preview.rb +69 -0
- data/app/components/poetry/ui/drawer/style.rb +105 -0
- data/app/components/poetry/ui/dropdown_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/dropdown_menu/component.rb +350 -0
- data/app/components/poetry/ui/dropdown_menu/preview.rb +101 -0
- data/app/components/poetry/ui/dropdown_menu/style.rb +60 -0
- data/app/components/poetry/ui/empty/component.html.erb +10 -0
- data/app/components/poetry/ui/empty/component.rb +79 -0
- data/app/components/poetry/ui/empty/preview.rb +27 -0
- data/app/components/poetry/ui/empty/style.rb +27 -0
- data/app/components/poetry/ui/family_identity.rb +52 -0
- data/app/components/poetry/ui/field/component.html.erb +19 -0
- data/app/components/poetry/ui/field/component.rb +183 -0
- data/app/components/poetry/ui/field/preview.html.erb +30 -0
- data/app/components/poetry/ui/field/preview.rb +72 -0
- data/app/components/poetry/ui/field/style.rb +68 -0
- data/app/components/poetry/ui/field_group/component.rb +61 -0
- data/app/components/poetry/ui/field_group/preview.html.erb +30 -0
- data/app/components/poetry/ui/field_group/preview.rb +20 -0
- data/app/components/poetry/ui/field_group/style.rb +25 -0
- data/app/components/poetry/ui/field_separator/component.rb +70 -0
- data/app/components/poetry/ui/field_separator/preview.rb +18 -0
- data/app/components/poetry/ui/field_separator/style.rb +20 -0
- data/app/components/poetry/ui/fieldset/component.rb +90 -0
- data/app/components/poetry/ui/fieldset/preview.html.erb +22 -0
- data/app/components/poetry/ui/fieldset/preview.rb +25 -0
- data/app/components/poetry/ui/fieldset/style.rb +18 -0
- data/app/components/poetry/ui/file_input/component.html.erb +14 -0
- data/app/components/poetry/ui/file_input/component.rb +188 -0
- data/app/components/poetry/ui/file_input/preview.rb +30 -0
- data/app/components/poetry/ui/file_input/style.rb +35 -0
- data/app/components/poetry/ui/hover_card/component.html.erb +6 -0
- data/app/components/poetry/ui/hover_card/component.rb +201 -0
- data/app/components/poetry/ui/hover_card/preview.rb +60 -0
- data/app/components/poetry/ui/hover_card/style.rb +17 -0
- data/app/components/poetry/ui/icon/component.rb +127 -0
- data/app/components/poetry/ui/icon/preview.rb +21 -0
- data/app/components/poetry/ui/input/component.rb +80 -0
- data/app/components/poetry/ui/input/preview.rb +35 -0
- data/app/components/poetry/ui/input/style.rb +18 -0
- data/app/components/poetry/ui/input_group/button_preview.html.erb +12 -0
- data/app/components/poetry/ui/input_group/component.rb +69 -0
- data/app/components/poetry/ui/input_group/preview.rb +23 -0
- data/app/components/poetry/ui/input_group/search_preview.html.erb +13 -0
- data/app/components/poetry/ui/input_group/style.rb +40 -0
- data/app/components/poetry/ui/input_group/textarea_preview.html.erb +10 -0
- data/app/components/poetry/ui/input_group_field.rb +61 -0
- data/app/components/poetry/ui/input_otp/component.html.erb +25 -0
- data/app/components/poetry/ui/input_otp/component.rb +250 -0
- data/app/components/poetry/ui/input_otp/preview.rb +91 -0
- data/app/components/poetry/ui/input_otp/style.rb +36 -0
- data/app/components/poetry/ui/item/component.html.erb +13 -0
- data/app/components/poetry/ui/item/component.rb +99 -0
- data/app/components/poetry/ui/item/group_preview.html.erb +15 -0
- data/app/components/poetry/ui/item/preview.rb +52 -0
- data/app/components/poetry/ui/item/style.rb +45 -0
- data/app/components/poetry/ui/kbd/component.rb +47 -0
- data/app/components/poetry/ui/kbd/group_preview.html.erb +4 -0
- data/app/components/poetry/ui/kbd/preview.rb +23 -0
- data/app/components/poetry/ui/kbd/style.rb +13 -0
- data/app/components/poetry/ui/label/component.rb +46 -0
- data/app/components/poetry/ui/label/preview.html.erb +5 -0
- data/app/components/poetry/ui/label/preview.rb +16 -0
- data/app/components/poetry/ui/label/style.rb +13 -0
- data/app/components/poetry/ui/link/component.rb +62 -0
- data/app/components/poetry/ui/link/preview.rb +31 -0
- data/app/components/poetry/ui/link/style.rb +20 -0
- data/app/components/poetry/ui/marker/component.html.erb +4 -0
- data/app/components/poetry/ui/marker/component.rb +81 -0
- data/app/components/poetry/ui/marker/preview.rb +43 -0
- data/app/components/poetry/ui/marker/style.rb +24 -0
- data/app/components/poetry/ui/menubar/component.html.erb +3 -0
- data/app/components/poetry/ui/menubar/component.rb +493 -0
- data/app/components/poetry/ui/menubar/preview.rb +164 -0
- data/app/components/poetry/ui/menubar/style.rb +71 -0
- data/app/components/poetry/ui/menus.rb +386 -0
- data/app/components/poetry/ui/message/component.html.erb +8 -0
- data/app/components/poetry/ui/message/component.rb +64 -0
- data/app/components/poetry/ui/message/preview.rb +33 -0
- data/app/components/poetry/ui/message/style.rb +29 -0
- data/app/components/poetry/ui/message_scroller/component.html.erb +15 -0
- data/app/components/poetry/ui/message_scroller/component.rb +163 -0
- data/app/components/poetry/ui/message_scroller/preview.rb +33 -0
- data/app/components/poetry/ui/message_scroller/style.rb +39 -0
- data/app/components/poetry/ui/metadata_list/component.rb +88 -0
- data/app/components/poetry/ui/metadata_list/preview.rb +55 -0
- data/app/components/poetry/ui/metadata_list/style.rb +35 -0
- data/app/components/poetry/ui/meter/component.rb +117 -0
- data/app/components/poetry/ui/meter/preview.rb +28 -0
- data/app/components/poetry/ui/meter/style.rb +16 -0
- data/app/components/poetry/ui/native_select/component.rb +124 -0
- data/app/components/poetry/ui/native_select/labelled_preview.html.erb +7 -0
- data/app/components/poetry/ui/native_select/preview.rb +20 -0
- data/app/components/poetry/ui/native_select/style.rb +22 -0
- data/app/components/poetry/ui/navigation_menu/bar_preview.html.erb +14 -0
- data/app/components/poetry/ui/navigation_menu/component.html.erb +25 -0
- data/app/components/poetry/ui/navigation_menu/component.rb +254 -0
- data/app/components/poetry/ui/navigation_menu/preview.rb +20 -0
- data/app/components/poetry/ui/navigation_menu/style.rb +73 -0
- data/app/components/poetry/ui/navigation_menu/viewport_preview.html.erb +23 -0
- data/app/components/poetry/ui/number_field/component.html.erb +19 -0
- data/app/components/poetry/ui/number_field/component.rb +263 -0
- data/app/components/poetry/ui/number_field/preview.rb +66 -0
- data/app/components/poetry/ui/number_field/style.rb +14 -0
- data/app/components/poetry/ui/pagination/component.html.erb +40 -0
- data/app/components/poetry/ui/pagination/component.rb +182 -0
- data/app/components/poetry/ui/pagination/preview.rb +47 -0
- data/app/components/poetry/ui/pagination/style.rb +27 -0
- data/app/components/poetry/ui/popover/component.html.erb +17 -0
- data/app/components/poetry/ui/popover/component.rb +203 -0
- data/app/components/poetry/ui/popover/preview.html.erb +29 -0
- data/app/components/poetry/ui/popover/preview.rb +66 -0
- data/app/components/poetry/ui/popover/style.rb +26 -0
- data/app/components/poetry/ui/popper_consumer.rb +69 -0
- data/app/components/poetry/ui/progress/component.rb +95 -0
- data/app/components/poetry/ui/progress/preview.rb +22 -0
- data/app/components/poetry/ui/progress/style.rb +19 -0
- data/app/components/poetry/ui/questionnaire/component.html.erb +80 -0
- data/app/components/poetry/ui/questionnaire/component.rb +396 -0
- data/app/components/poetry/ui/questionnaire/preview.rb +99 -0
- data/app/components/poetry/ui/questionnaire/style.rb +53 -0
- data/app/components/poetry/ui/radio_group/component.html.erb +4 -0
- data/app/components/poetry/ui/radio_group/component.rb +323 -0
- data/app/components/poetry/ui/radio_group/preview.rb +109 -0
- data/app/components/poetry/ui/radio_group/style.rb +53 -0
- data/app/components/poetry/ui/resizable/component.html.erb +10 -0
- data/app/components/poetry/ui/resizable/component.rb +178 -0
- data/app/components/poetry/ui/resizable/preview.rb +42 -0
- data/app/components/poetry/ui/resizable/style.rb +30 -0
- data/app/components/poetry/ui/scroll_area/component.rb +76 -0
- data/app/components/poetry/ui/scroll_area/preview.rb +35 -0
- data/app/components/poetry/ui/scroll_area/style.rb +19 -0
- data/app/components/poetry/ui/search_field/component.html.erb +15 -0
- data/app/components/poetry/ui/search_field/component.rb +137 -0
- data/app/components/poetry/ui/search_field/preview.rb +23 -0
- data/app/components/poetry/ui/search_field/style.rb +18 -0
- data/app/components/poetry/ui/select/component.html.erb +15 -0
- data/app/components/poetry/ui/select/component.rb +639 -0
- data/app/components/poetry/ui/select/preview.rb +113 -0
- data/app/components/poetry/ui/select/style.rb +58 -0
- data/app/components/poetry/ui/sensitive_input/component.html.erb +29 -0
- data/app/components/poetry/ui/sensitive_input/component.rb +274 -0
- data/app/components/poetry/ui/sensitive_input/preview.rb +40 -0
- data/app/components/poetry/ui/sensitive_input/style.rb +49 -0
- data/app/components/poetry/ui/separator/component.rb +57 -0
- data/app/components/poetry/ui/separator/preview.rb +19 -0
- data/app/components/poetry/ui/separator/style.rb +15 -0
- data/app/components/poetry/ui/sheet/component.html.erb +21 -0
- data/app/components/poetry/ui/sheet/component.rb +97 -0
- data/app/components/poetry/ui/sheet/preview.rb +57 -0
- data/app/components/poetry/ui/sheet/style.rb +47 -0
- data/app/components/poetry/ui/sidebar/component.html.erb +20 -0
- data/app/components/poetry/ui/sidebar/component.rb +249 -0
- data/app/components/poetry/ui/sidebar/preview.rb +21 -0
- data/app/components/poetry/ui/sidebar/shell_preview.html.erb +47 -0
- data/app/components/poetry/ui/sidebar/style.rb +139 -0
- data/app/components/poetry/ui/skeleton/component.rb +39 -0
- data/app/components/poetry/ui/skeleton/preview.rb +22 -0
- data/app/components/poetry/ui/skeleton/style.rb +13 -0
- data/app/components/poetry/ui/slider/component.html.erb +13 -0
- data/app/components/poetry/ui/slider/component.rb +360 -0
- data/app/components/poetry/ui/slider/preview.rb +73 -0
- data/app/components/poetry/ui/slider/style.rb +55 -0
- data/app/components/poetry/ui/spinner/component.rb +58 -0
- data/app/components/poetry/ui/spinner/preview.rb +22 -0
- data/app/components/poetry/ui/spinner/style.rb +12 -0
- data/app/components/poetry/ui/stat/component.html.erb +13 -0
- data/app/components/poetry/ui/stat/component.rb +102 -0
- data/app/components/poetry/ui/stat/preview.rb +47 -0
- data/app/components/poetry/ui/stat/style.rb +29 -0
- data/app/components/poetry/ui/switch/component.html.erb +15 -0
- data/app/components/poetry/ui/switch/component.rb +136 -0
- data/app/components/poetry/ui/switch/preview.rb +66 -0
- data/app/components/poetry/ui/switch/style.rb +22 -0
- data/app/components/poetry/ui/table/component.html.erb +3 -0
- data/app/components/poetry/ui/table/component.rb +93 -0
- data/app/components/poetry/ui/table/preview.rb +96 -0
- data/app/components/poetry/ui/table/style.rb +26 -0
- data/app/components/poetry/ui/tabs/component.html.erb +10 -0
- data/app/components/poetry/ui/tabs/component.rb +253 -0
- data/app/components/poetry/ui/tabs/preview.rb +43 -0
- data/app/components/poetry/ui/tabs/style.rb +49 -0
- data/app/components/poetry/ui/tag_group/component.html.erb +8 -0
- data/app/components/poetry/ui/tag_group/component.rb +203 -0
- data/app/components/poetry/ui/tag_group/preview.rb +43 -0
- data/app/components/poetry/ui/tag_group/style.rb +29 -0
- data/app/components/poetry/ui/textarea/component.rb +64 -0
- data/app/components/poetry/ui/textarea/preview.rb +75 -0
- data/app/components/poetry/ui/textarea/style.rb +18 -0
- data/app/components/poetry/ui/time_field/component.html.erb +6 -0
- data/app/components/poetry/ui/time_field/component.rb +85 -0
- data/app/components/poetry/ui/time_field/preview.rb +40 -0
- data/app/components/poetry/ui/time_field/style.rb +30 -0
- data/app/components/poetry/ui/timeline/component.rb +134 -0
- data/app/components/poetry/ui/timeline/preview.rb +61 -0
- data/app/components/poetry/ui/timeline/style.rb +54 -0
- data/app/components/poetry/ui/toast/component.html.erb +24 -0
- data/app/components/poetry/ui/toast/component.rb +178 -0
- data/app/components/poetry/ui/toast/preview.rb +69 -0
- data/app/components/poetry/ui/toast/style.rb +33 -0
- data/app/components/poetry/ui/toast_trigger/addressed_preview.html.erb +12 -0
- data/app/components/poetry/ui/toast_trigger/component.html.erb +3 -0
- data/app/components/poetry/ui/toast_trigger/component.rb +82 -0
- data/app/components/poetry/ui/toast_trigger/default_preview.html.erb +11 -0
- data/app/components/poetry/ui/toast_trigger/preview.rb +19 -0
- data/app/components/poetry/ui/toaster/component.html.erb +1 -0
- data/app/components/poetry/ui/toaster/component.rb +99 -0
- data/app/components/poetry/ui/toaster/positions_preview.html.erb +24 -0
- data/app/components/poetry/ui/toaster/preview.rb +46 -0
- data/app/components/poetry/ui/toaster/stamp_preview.html.erb +15 -0
- data/app/components/poetry/ui/toaster/style.rb +26 -0
- data/app/components/poetry/ui/toggle/component.rb +120 -0
- data/app/components/poetry/ui/toggle/preview.rb +79 -0
- data/app/components/poetry/ui/toggle/style.rb +33 -0
- data/app/components/poetry/ui/toggle_group/component.html.erb +4 -0
- data/app/components/poetry/ui/toggle_group/component.rb +252 -0
- data/app/components/poetry/ui/toggle_group/preview.rb +143 -0
- data/app/components/poetry/ui/toggle_group/style.rb +33 -0
- data/app/components/poetry/ui/toolbar/component.rb +125 -0
- data/app/components/poetry/ui/toolbar/preview.rb +41 -0
- data/app/components/poetry/ui/toolbar/style.rb +20 -0
- data/app/components/poetry/ui/tooltip/component.html.erb +20 -0
- data/app/components/poetry/ui/tooltip/component.rb +185 -0
- data/app/components/poetry/ui/tooltip/preview.rb +74 -0
- data/app/components/poetry/ui/tooltip/previews/provider_row.html.erb +25 -0
- data/app/components/poetry/ui/tooltip/style.rb +24 -0
- data/app/components/poetry/ui/tree/component.html.erb +32 -0
- data/app/components/poetry/ui/tree/component.rb +233 -0
- data/app/components/poetry/ui/tree/preview.rb +55 -0
- data/app/components/poetry/ui/tree/style.rb +29 -0
- data/app/components/poetry/ui/typeset/component.rb +73 -0
- data/app/components/poetry/ui/typeset/preview.rb +45 -0
- data/app/components/poetry/ui/typeset/style.rb +15 -0
- data/app/controllers/poetry/ui/llms_controller.rb +28 -0
- data/app/helpers/poetry/ui/components_helper.rb +1794 -0
- data/app/helpers/poetry/ui/form_builder/type_inference.rb +117 -0
- data/app/helpers/poetry/ui/form_builder.rb +916 -0
- data/app/helpers/poetry/ui/optimistic_form_builder.rb +55 -0
- data/app/helpers/poetry/ui/toast_stream_actions.rb +27 -0
- data/config/component_descriptions.yml +96 -0
- data/config/component_registry.yml +12257 -0
- data/config/locales/en.yml +93 -0
- data/config/routes.rb +8 -0
- data/config/template_classes.txt +113 -0
- data/lib/generators/poetry/add/add_generator.rb +218 -0
- data/lib/generators/poetry/agent_rules/agent_rules_generator.rb +91 -0
- data/lib/generators/poetry/agents/agents_generator.rb +24 -0
- data/lib/generators/poetry/agents_section.rb +148 -0
- data/lib/generators/poetry/block/block_generator.rb +67 -0
- data/lib/generators/poetry/block/templates/action_bar.html.erb +45 -0
- data/lib/generators/poetry/block/templates/app_shell.html.erb +139 -0
- data/lib/generators/poetry/block/templates/data_index.html.erb +85 -0
- data/lib/generators/poetry/block/templates/destructive_panel.html.erb +26 -0
- data/lib/generators/poetry/block/templates/page_header.html.erb +25 -0
- data/lib/generators/poetry/block/templates/section_card.html.erb +42 -0
- data/lib/generators/poetry/block/templates/stepper.html.erb +79 -0
- data/lib/generators/poetry/block/templates/top_nav.html.erb +44 -0
- data/lib/generators/poetry/diff/diff_generator.rb +135 -0
- data/lib/generators/poetry/editor/editor_generator.rb +344 -0
- data/lib/generators/poetry/editor/templates/poetry-check-hook +81 -0
- data/lib/generators/poetry/install/install_generator.rb +364 -0
- data/lib/generators/poetry/pagination/pagination_generator.rb +76 -0
- data/lib/generators/poetry/pagination/templates/kaminari_paginator.html.erb +29 -0
- data/lib/generators/poetry/pagination/templates/poetry_link_renderer.rb +34 -0
- data/lib/generators/poetry/pagination/templates/poetry_pagy_helper.rb +28 -0
- data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js +131 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/controllers/orders_controller.rb +15 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/views/orders/index.html.erb +7 -0
- data/lib/generators/poetry/recipes/screen_data_index/test/system/orders_index_test.rb +16 -0
- data/lib/generators/poetry/recipes/screen_settings/app/controllers/settings_controller.rb +14 -0
- data/lib/generators/poetry/recipes/screen_settings/app/views/settings/show.html.erb +9 -0
- data/lib/generators/poetry/recipes/screen_settings/test/system/settings_show_test.rb +16 -0
- data/lib/generators/poetry/scaffold_templates/scaffold_templates_generator.rb +60 -0
- data/lib/generators/poetry/scaffold_templates/templates/_form.html.erb.tt +77 -0
- data/lib/generators/poetry/scaffold_templates/templates/controller.rb.tt +78 -0
- data/lib/generators/poetry/scaffold_templates/templates/edit.html.erb.tt +14 -0
- data/lib/generators/poetry/scaffold_templates/templates/index.html.erb.tt +24 -0
- data/lib/generators/poetry/scaffold_templates/templates/new.html.erb.tt +11 -0
- data/lib/generators/poetry/scaffold_templates/templates/partial.html.erb.tt +13 -0
- data/lib/generators/poetry/scaffold_templates/templates/show.html.erb.tt +18 -0
- data/lib/generators/poetry/skill/skill_generator.rb +34 -0
- data/lib/generators/poetry/skill/templates/poetry-component/SKILL.md +58 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/anatomy.md +152 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/checklist.md +67 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/documentation.md +137 -0
- data/lib/generators/poetry/skill/templates/poetry-design/SKILL.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/audit.md +123 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/compose.md +73 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/figma.md +68 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/paper.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/study.md +47 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/theme.md +70 -0
- data/lib/generators/poetry/skills_section.rb +58 -0
- data/lib/poetry/ui/chat.rb +283 -0
- data/lib/poetry/ui/code_block_highlighter.rb +60 -0
- data/lib/poetry/ui/engine.rb +55 -0
- data/lib/poetry/ui/recipes.rb +157 -0
- data/lib/poetry/ui/testing/combobox.rb +167 -0
- data/lib/poetry/ui/testing/dialog.rb +73 -0
- data/lib/poetry/ui/testing/menu.rb +127 -0
- data/lib/poetry/ui/testing/registration.rb +58 -0
- data/lib/poetry/ui/testing/select.rb +147 -0
- data/lib/poetry/ui/testing/tester.rb +130 -0
- data/lib/poetry/ui/testing.rb +130 -0
- data/lib/poetry/ui/themes.rb +109 -0
- data/lib/poetry/ui/version.rb +8 -0
- data/lib/poetry/ui/webmcp.rb +91 -0
- data/lib/poetry/ui.rb +285 -0
- data/lib/poetry-ui.rb +5 -0
- data/lib/tasks/poetry/check.rake +73 -0
- data/lib/tasks/poetry/design.rake +293 -0
- data/lib/tasks/poetry/verify.rake +36 -0
- data/themes/default.css +1793 -0
- data/themes/luma.css +1764 -0
- data/themes/lyra.css +1768 -0
- data/themes/maia.css +1768 -0
- data/themes/mira.css +1760 -0
- data/themes/nova.css +1755 -0
- data/themes/rhea.css +1771 -0
- data/themes/sera.css +1757 -0
- data/themes/vega.css +1775 -0
- data/typeset/typeset.css +499 -0
- metadata +502 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Dated event sequences as ordered lists.
|
|
6
|
+
module Timeline
|
|
7
|
+
# A sequence of dated events rendered as a real ordered list
|
|
8
|
+
# (<ol>/<li>) - activity feeds, order status, deploy history. Each
|
|
9
|
+
# item carries a decorative indicator (a dot, or icon:'s glyph) on
|
|
10
|
+
# a connector rail; completed: marks progress and recolors the
|
|
11
|
+
# item's indicator and rail segment. orientation: :horizontal lays
|
|
12
|
+
# the steps left-to-right (an order tracker); the vertical default
|
|
13
|
+
# reads as a feed.
|
|
14
|
+
#
|
|
15
|
+
# A Timeline records history - for steps the user advances through,
|
|
16
|
+
# use Stepper. Styling is utility-only: the data-slot names are the
|
|
17
|
+
# restyle seam.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# render Poetry::Ui::Timeline::Component.new do |timeline|
|
|
21
|
+
# timeline.with_item(title: "Order placed", time: "Mar 15", completed: true)
|
|
22
|
+
# timeline.with_item(title: "In transit") { "Estimated delivery Thursday." }
|
|
23
|
+
# end
|
|
24
|
+
class Component < Poetry::Core::Component
|
|
25
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
26
|
+
AGENT_RULES = [
|
|
27
|
+
"A sequence of dated events (activity feed, order status, deploy history) is a " \
|
|
28
|
+
"Timeline - never a hand-rolled stack of dots and left borders (this is the <ol> " \
|
|
29
|
+
"the sequence owes its readers).",
|
|
30
|
+
"Each with_item takes title:, optional time: (renders a <time>), optional icon: " \
|
|
31
|
+
"(swaps the dot for a glyph), completed: for progress - the description is the block.",
|
|
32
|
+
"completed: colors the item's indicator and its rail segment - mark every step up to " \
|
|
33
|
+
"the current one, not just the latest.",
|
|
34
|
+
"orientation: :horizontal lays the steps left-to-right (an order tracker); the " \
|
|
35
|
+
"vertical default reads as a feed. For steps the USER advances through, use " \
|
|
36
|
+
"Stepper - a Timeline records, it never navigates."
|
|
37
|
+
].freeze
|
|
38
|
+
|
|
39
|
+
# The same fact the before_render raise enforces, stated
|
|
40
|
+
# statically: poetry check flags the omission without rendering.
|
|
41
|
+
REQUIRED_SLOTS = { item: "at least one item (title:, with the description as its block)" }.freeze
|
|
42
|
+
|
|
43
|
+
renders_many :items,
|
|
44
|
+
doc: "Declares one event: title:, optional time: (renders a <time>), optional icon: (replaces " \
|
|
45
|
+
"the dot), completed: for progress - the description is the block.",
|
|
46
|
+
renders: lambda { |title:, time: nil, icon: nil, completed: false,
|
|
47
|
+
**options, &block|
|
|
48
|
+
# class: merges through the dictionary (caller classes win on
|
|
49
|
+
# conflicts) - a plain hash merge would REPLACE css(:item).
|
|
50
|
+
attrs = { "data-slot" => "timeline-item", class: css(:item, class: options.delete(:class)) }
|
|
51
|
+
attrs["data-completed"] = "" if completed
|
|
52
|
+
content_tag(:li, attrs.merge(options)) do
|
|
53
|
+
body = [item_indicator(icon), item_separator, item_header(title, time)]
|
|
54
|
+
if block
|
|
55
|
+
body << content_tag(:div, { "data-slot" => "timeline-content",
|
|
56
|
+
class: css(:content) }, &block)
|
|
57
|
+
end
|
|
58
|
+
safe_join(body)
|
|
59
|
+
end
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
style :orientation, default: :vertical, variants: %i[vertical horizontal],
|
|
63
|
+
doc: "The layout axis: :vertical reads as a feed, :horizontal as a step tracker."
|
|
64
|
+
|
|
65
|
+
part "timeline", "The <ol> root - the event sequence",
|
|
66
|
+
states: {
|
|
67
|
+
"data-orientation" => { condition: "always - the layout axis",
|
|
68
|
+
values: %w[vertical horizontal] }
|
|
69
|
+
}
|
|
70
|
+
part "timeline-item", "One event (<li>): indicator + rail segment + header + description",
|
|
71
|
+
states: {
|
|
72
|
+
"data-completed" => "the step is done - recolors its indicator and rail segment"
|
|
73
|
+
}
|
|
74
|
+
part "timeline-indicator", "The decorative marker on the rail - a dot, or icon:'s glyph " \
|
|
75
|
+
"(aria-hidden; the sequence lives in the list semantics)"
|
|
76
|
+
part "timeline-separator", "The decorative rail segment toward the next item - hidden " \
|
|
77
|
+
"on the last"
|
|
78
|
+
part "timeline-header", "The title/time row"
|
|
79
|
+
part "timeline-title", "The event's name"
|
|
80
|
+
part "timeline-time", "The event's <time> - muted, small"
|
|
81
|
+
part "timeline-content", "Muted description under the header (the item's block)"
|
|
82
|
+
|
|
83
|
+
# @api private
|
|
84
|
+
def before_render
|
|
85
|
+
raise ArgumentError, "Timeline requires at least one with_item" unless items?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @api private
|
|
89
|
+
def call
|
|
90
|
+
content_tag(:ol, safe_join(items.map(&:to_s)), **root_attributes.to_attributes)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @api private
|
|
94
|
+
def root_attributes
|
|
95
|
+
html_attributes.merge_if_not_set(
|
|
96
|
+
{ "data-slot" => "timeline", "data-orientation" => orientation }
|
|
97
|
+
.merge(component_data_attributes)
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
# Decorative twins (the Tree toggle-spacer rule): indicator and
|
|
104
|
+
# rail carry no semantics - the sequence is the <ol>, progress is
|
|
105
|
+
# data-completed.
|
|
106
|
+
def item_indicator(icon)
|
|
107
|
+
content_tag(:div, "data-slot" => "timeline-indicator", "aria-hidden" => "true",
|
|
108
|
+
"class" => css(:indicator)) do
|
|
109
|
+
if icon
|
|
110
|
+
render(Icon::Component.new(name: icon, class: css(:indicator_icon)))
|
|
111
|
+
else
|
|
112
|
+
content_tag(:span, nil, class: css(:indicator_dot))
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def item_separator
|
|
118
|
+
content_tag(:div, nil, "data-slot" => "timeline-separator", "aria-hidden" => "true",
|
|
119
|
+
"class" => css(:separator))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def item_header(title, time)
|
|
123
|
+
content_tag(:div, "data-slot" => "timeline-header", class: css(:header)) do
|
|
124
|
+
parts = [content_tag(:div, title, "data-slot" => "timeline-title", class: css(:title))]
|
|
125
|
+
parts << content_tag(:time, time, "data-slot" => "timeline-time", class: css(:time)) if time
|
|
126
|
+
safe_join(parts)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private :root_attributes
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Timeline
|
|
6
|
+
class Preview < Poetry::Core::Preview::Base
|
|
7
|
+
def default
|
|
8
|
+
render_component do |timeline|
|
|
9
|
+
timeline.with_item(title: "Order placed", time: "Mar 15, 09:12", completed: true) do
|
|
10
|
+
"Payment authorized and inventory reserved."
|
|
11
|
+
end
|
|
12
|
+
timeline.with_item(title: "Packed", time: "Mar 15, 14:03", completed: true) do
|
|
13
|
+
"Two parcels sealed at dock B."
|
|
14
|
+
end
|
|
15
|
+
timeline.with_item(title: "In transit", time: "Mar 16") do
|
|
16
|
+
"Handed to the carrier - estimated delivery Thursday."
|
|
17
|
+
end
|
|
18
|
+
timeline.with_item(title: "Delivered")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The order tracker: steps left-to-right, progress recoloring the
|
|
23
|
+
# rail up to the current step.
|
|
24
|
+
def horizontal
|
|
25
|
+
render_component(orientation: :horizontal) do |timeline|
|
|
26
|
+
timeline.with_item(title: "Ordered", time: "Mar 15", completed: true)
|
|
27
|
+
timeline.with_item(title: "Packed", time: "Mar 15", completed: true)
|
|
28
|
+
timeline.with_item(title: "Shipped", time: "Mar 16")
|
|
29
|
+
timeline.with_item(title: "Delivered")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# icon: swaps the dot for a glyph - the indicator stays decorative.
|
|
34
|
+
def with_icons
|
|
35
|
+
render_component do |timeline|
|
|
36
|
+
timeline.with_item(title: "Repository created", time: "Jun 2",
|
|
37
|
+
icon: :"git-branch", completed: true) do
|
|
38
|
+
"poetry-ui scaffolded from the gem template."
|
|
39
|
+
end
|
|
40
|
+
timeline.with_item(title: "First deploy", time: "Jun 4",
|
|
41
|
+
icon: :rocket, completed: true) do
|
|
42
|
+
"Shipped to staging behind the feature flag."
|
|
43
|
+
end
|
|
44
|
+
timeline.with_item(title: "Incident opened", time: "Jun 9", icon: :"triangle-alert") do
|
|
45
|
+
"Elevated 5xx rate on checkout - rolled back in 12 minutes."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Titles alone: the anatomy holds without times or descriptions.
|
|
51
|
+
def titles_only
|
|
52
|
+
render_component do |timeline|
|
|
53
|
+
timeline.with_item(title: "Draft", completed: true)
|
|
54
|
+
timeline.with_item(title: "In review")
|
|
55
|
+
timeline.with_item(title: "Published")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Timeline
|
|
6
|
+
# Utility-only (the Separator/Spinner rule): no upstream cn hook
|
|
7
|
+
# exists for a Timeline, so the surface is structural utilities on
|
|
8
|
+
# semantic tokens - data-slot selectors are the restyle seam. Both
|
|
9
|
+
# orientations live in ONE dictionary via the named-group pattern:
|
|
10
|
+
# the root carries group/timeline + data-orientation, each item
|
|
11
|
+
# group/timeline-item + data-completed, and every element restyles
|
|
12
|
+
# itself off those two markers - so one attribute flip re-lays the
|
|
13
|
+
# whole sequence.
|
|
14
|
+
class Style < Poetry::Core::Style
|
|
15
|
+
base "group/timeline flex"
|
|
16
|
+
|
|
17
|
+
variant :orientation, {
|
|
18
|
+
vertical: "flex-col",
|
|
19
|
+
horizontal: "w-full flex-row"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
element :item, "group/timeline-item relative flex flex-1 flex-col gap-0.5 " \
|
|
23
|
+
"group-data-[orientation=vertical]/timeline:ps-9 " \
|
|
24
|
+
"group-data-[orientation=vertical]/timeline:not-last:pb-7 " \
|
|
25
|
+
"group-data-[orientation=horizontal]/timeline:pt-9 " \
|
|
26
|
+
"group-data-[orientation=horizontal]/timeline:not-last:pe-8"
|
|
27
|
+
element :indicator, "absolute top-0 left-0 flex size-6 items-center justify-center " \
|
|
28
|
+
"rounded-full border border-border bg-background " \
|
|
29
|
+
"text-muted-foreground " \
|
|
30
|
+
"group-data-[completed]/timeline-item:border-primary " \
|
|
31
|
+
"group-data-[completed]/timeline-item:text-primary"
|
|
32
|
+
element :indicator_dot, "size-1.5 rounded-full bg-current"
|
|
33
|
+
element :indicator_icon, "size-3.5"
|
|
34
|
+
element :separator, "absolute bg-border group-last/timeline-item:hidden " \
|
|
35
|
+
"group-data-[completed]/timeline-item:bg-primary " \
|
|
36
|
+
"group-data-[orientation=vertical]/timeline:top-7 " \
|
|
37
|
+
"group-data-[orientation=vertical]/timeline:left-3 " \
|
|
38
|
+
"group-data-[orientation=vertical]/timeline:h-[calc(100%-2rem)] " \
|
|
39
|
+
"group-data-[orientation=vertical]/timeline:w-px " \
|
|
40
|
+
"group-data-[orientation=vertical]/timeline:-translate-x-1/2 " \
|
|
41
|
+
"group-data-[orientation=horizontal]/timeline:top-3 " \
|
|
42
|
+
"group-data-[orientation=horizontal]/timeline:left-7 " \
|
|
43
|
+
"group-data-[orientation=horizontal]/timeline:h-px " \
|
|
44
|
+
"group-data-[orientation=horizontal]/timeline:w-[calc(100%-2rem)] " \
|
|
45
|
+
"group-data-[orientation=horizontal]/timeline:-translate-y-1/2"
|
|
46
|
+
element :header, "flex flex-wrap items-baseline gap-x-2 gap-y-0.5 " \
|
|
47
|
+
"group-data-[orientation=vertical]/timeline:min-h-6"
|
|
48
|
+
element :title, "text-sm font-medium"
|
|
49
|
+
element :time, "text-xs font-medium text-muted-foreground"
|
|
50
|
+
element :content, "text-sm text-muted-foreground"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%= content_tag(:li, **root_attributes.to_attributes) do %>
|
|
2
|
+
<% if variant_icon %>
|
|
3
|
+
<span data-slot="toast-icon" aria-hidden="true" class="<%= css(:icon) %>">
|
|
4
|
+
<%= render Poetry::Ui::Icon::Component.new(name: variant_icon,
|
|
5
|
+
class: ("animate-spin" if variant == :loading)) %>
|
|
6
|
+
</span>
|
|
7
|
+
<% end %>
|
|
8
|
+
<div class="<%= css(:body) %>">
|
|
9
|
+
<div data-slot="toast-title" class="<%= css(:title) %>"><%= title %></div>
|
|
10
|
+
<% if description? %>
|
|
11
|
+
<div data-slot="toast-description" class="<%= css(:description) %>"><%= description %></div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<%= content %>
|
|
14
|
+
</div>
|
|
15
|
+
<% if action? %><span class="<%= css(:action) %>"><%= action %></span><% end %>
|
|
16
|
+
<% if show_close_button %>
|
|
17
|
+
<%= render Poetry::Ui::Button::Component.new(variant: :ghost, size: :"icon-sm",
|
|
18
|
+
label: t("poetry.toast.close"),
|
|
19
|
+
class: css(:close),
|
|
20
|
+
**close_button_attributes) do %>
|
|
21
|
+
<%= render Poetry::Ui::Icon::Component.new(name: :x) %>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Transient notifications.
|
|
6
|
+
module Toast
|
|
7
|
+
# The closed vocabulary for the variant axis.
|
|
8
|
+
VARIANTS = %i[default success info warning destructive loading].freeze
|
|
9
|
+
# The closed vocabulary for announcement politeness.
|
|
10
|
+
POLITENESS = %i[polite assertive].freeze
|
|
11
|
+
|
|
12
|
+
# One notification, rendered inside a Toaster region. The item
|
|
13
|
+
# itself is role=status with aria-live off: it never announces
|
|
14
|
+
# itself - on arrival the controller speaks it exactly once through
|
|
15
|
+
# a shared live region at the variant's politeness (:destructive
|
|
16
|
+
# announces assertively), so duplicating the announcement by hand
|
|
17
|
+
# is a regression.
|
|
18
|
+
#
|
|
19
|
+
# The auto-dismiss timer pauses on hover, focus-within, window
|
|
20
|
+
# blur, and hidden tabs; duration <= 0 means persistent - and an
|
|
21
|
+
# action-bearing toast defaults to persistent (a missable undo is a
|
|
22
|
+
# bug). Toasts never steal focus on show; the toaster's hotkey
|
|
23
|
+
# (F8 by default) reaches them.
|
|
24
|
+
#
|
|
25
|
+
# @example An undo toast (persistent because it carries an action)
|
|
26
|
+
# render Poetry::Ui::Toast::Component.new(variant: :success) do |toast|
|
|
27
|
+
# toast.with_title { "Message archived" }
|
|
28
|
+
# toast.with_action { "Undo" }
|
|
29
|
+
# end
|
|
30
|
+
class Component < Poetry::Core::Component
|
|
31
|
+
# The icon each variant renders (:default ships none; :loading spins).
|
|
32
|
+
VARIANT_ICONS = {
|
|
33
|
+
success: :"circle-check", info: :info, warning: :"triangle-alert",
|
|
34
|
+
destructive: :"octagon-x", loading: :"loader-circle"
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
38
|
+
AGENT_RULES = [
|
|
39
|
+
"Server-side toasts go through turbo_stream.poetry_toast / the flash recipe - never " \
|
|
40
|
+
"hand-append into #poetry-toaster.",
|
|
41
|
+
"Undo/consequence toasts MUST carry an action slot - action-bearing toasts default to " \
|
|
42
|
+
"persistent (duration nil); give an explicit duration only when missing the action is safe.",
|
|
43
|
+
"variant: :destructive is for failures the user must hear about (it announces ASSERTIVELY); " \
|
|
44
|
+
"do not use it for styling.",
|
|
45
|
+
"Never put required interactions in a toast (toasts are missable) - that is AlertDialog.",
|
|
46
|
+
"Toasts are supplementary: never the only place an outcome is recorded."
|
|
47
|
+
].freeze
|
|
48
|
+
|
|
49
|
+
# The same facts the before_render raise enforces, stated statically:
|
|
50
|
+
# poetry check flags the omission without rendering.
|
|
51
|
+
REQUIRED_SLOTS = { title: "the message" }.freeze
|
|
52
|
+
|
|
53
|
+
# The component behind the forwarding slot: with_action renders a Button.
|
|
54
|
+
SLOT_RENDERS = { action: Button::Component }.freeze
|
|
55
|
+
|
|
56
|
+
renders_one :title, doc: "The message (REQUIRED - the announced payload's first line)."
|
|
57
|
+
|
|
58
|
+
renders_one :description, doc: "Supporting copy under the title."
|
|
59
|
+
|
|
60
|
+
renders_one :action,
|
|
61
|
+
doc: "Typed Button slot (undo / view / retry): clicking it dismisses the toast with reason " \
|
|
62
|
+
"\"action\". Its presence makes the toast persistent by default.",
|
|
63
|
+
renders: lambda { |**options, &block|
|
|
64
|
+
wiring = { "data-slot" => "toast-action" }.merge(stimulus_attributes_for(:action))
|
|
65
|
+
# Caller attribute keys merge WITH the wiring (stimulus concat)
|
|
66
|
+
# instead of replacing it at the kwargs splat; component options
|
|
67
|
+
# (variant: etc.) stay plain kwargs.
|
|
68
|
+
attr_keys = options.keys.select { |k| k == :data || k.to_s.start_with?("data-", "aria-") }
|
|
69
|
+
caller_attrs = attr_keys.to_h { |k| [k, options.delete(k)] }
|
|
70
|
+
merged = Poetry::Core::HTML::Attributes.merged(wiring, caller_attrs)
|
|
71
|
+
Button::Component.new(variant: :outline, size: :sm, **merged.symbolize_keys, **options, &block)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# APG timing wiring: hover and focus-within hold the timer (the
|
|
75
|
+
# window-blur / tab-hidden holds are wired by the controller).
|
|
76
|
+
use_stimulus do
|
|
77
|
+
on :root do
|
|
78
|
+
controller :toast do
|
|
79
|
+
register
|
|
80
|
+
value :duration, from: :effective_duration
|
|
81
|
+
value :politeness
|
|
82
|
+
action :pause, on: %i[mouseenter focusin]
|
|
83
|
+
action :resume, on: %i[mouseleave focusout]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
on :action do
|
|
87
|
+
controller :toast do
|
|
88
|
+
target :action
|
|
89
|
+
action :dismiss, on: :click
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
on :close do
|
|
93
|
+
controller :toast do
|
|
94
|
+
target :close
|
|
95
|
+
action :dismiss, on: :click
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
style :variant, default: :default, required: true, variants: VARIANTS,
|
|
101
|
+
doc: "The intent axis: it picks the icon, and :destructive announces assertively while " \
|
|
102
|
+
":loading defaults to persistent."
|
|
103
|
+
|
|
104
|
+
option :duration, :integer,
|
|
105
|
+
doc: "nil = derived: 5000ms, or PERSISTENT when an action slot is present (the missable-undo guard). " \
|
|
106
|
+
"<= 0 = persistent."
|
|
107
|
+
option :politeness, :symbol, default: -> { variant == :destructive ? :assertive : :polite },
|
|
108
|
+
doc: "Derived from the variant: destructive announces assertively."
|
|
109
|
+
option :show_close_button, :boolean, default: true,
|
|
110
|
+
doc: "The corner dismiss button - named as the dialog family names it."
|
|
111
|
+
|
|
112
|
+
validates :politeness, inclusion: { in: POLITENESS }
|
|
113
|
+
|
|
114
|
+
part "toast", "The notification item itself (<li>, role=status) - variant, open state, " \
|
|
115
|
+
"and the toaster's stack facts all ride here",
|
|
116
|
+
states: {
|
|
117
|
+
"data-open" => "toast is showing (the server-rendered state; the dismiss exit " \
|
|
118
|
+
"flips the pair before removal)",
|
|
119
|
+
"data-closed" => "toast is animating out",
|
|
120
|
+
"data-variant" => { condition: "always - the resolved variant",
|
|
121
|
+
values: VARIANTS.map(&:to_s) },
|
|
122
|
+
"data-queued" => "the toaster holds it hidden past the visible limit " \
|
|
123
|
+
"(timer paused until a slot frees up)"
|
|
124
|
+
},
|
|
125
|
+
vars: {
|
|
126
|
+
"--poetry-toast-index" => "stack position written by the toaster's reflow " \
|
|
127
|
+
"(newest visible toast = 0)"
|
|
128
|
+
}
|
|
129
|
+
part "toast-icon", "The variant's icon well (aria-hidden; the default variant " \
|
|
130
|
+
"renders none)"
|
|
131
|
+
part "toast-title", "The message - the announced payload's first line (required slot)"
|
|
132
|
+
part "toast-description", "Supporting copy under the title"
|
|
133
|
+
|
|
134
|
+
# @api private
|
|
135
|
+
def before_render
|
|
136
|
+
raise ArgumentError, "Toast requires with_title (the message)" unless title?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @api private
|
|
140
|
+
def effective_duration
|
|
141
|
+
return duration unless duration.nil?
|
|
142
|
+
|
|
143
|
+
# Action-bearing toasts are persistent (a missable undo is a
|
|
144
|
+
# bug); so are loading toasts - the promise recipe REPLACES
|
|
145
|
+
# them (turbo_stream.replace on the toast id) when the job
|
|
146
|
+
# settles, and an auto-dismissed loading state is a lie.
|
|
147
|
+
action? || variant == :loading ? 0 : 5000
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @api private
|
|
151
|
+
def variant_icon
|
|
152
|
+
VARIANT_ICONS[variant]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @api private
|
|
156
|
+
def root_attributes
|
|
157
|
+
html_attributes.merge_if_not_set(
|
|
158
|
+
{
|
|
159
|
+
"data-slot" => "toast", "data-variant" => variant, "data-open" => "",
|
|
160
|
+
# aria-live=off ON PURPOSE: the announce singleton does the
|
|
161
|
+
# talking, exactly once (an item that is itself a live
|
|
162
|
+
# region would announce a second time).
|
|
163
|
+
"role" => "status", "aria-live" => "off", "aria-atomic" => "true",
|
|
164
|
+
"tabindex" => "0"
|
|
165
|
+
}.merge(stimulus_attributes_for(:root)).merge(component_data_attributes)
|
|
166
|
+
)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @api private
|
|
170
|
+
def close_button_attributes
|
|
171
|
+
stimulus_attributes_for(:close).merge("data-slot" => "toast-close")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private :effective_duration, :variant_icon, :root_attributes, :close_button_attributes
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Toast
|
|
6
|
+
# The Toast preview matrix: every variant (each with its icon +
|
|
7
|
+
# politeness derivation), the description pairing, the
|
|
8
|
+
# action-bearing (persistent) undo shape, and a persistent error.
|
|
9
|
+
class Preview < Poetry::Core::Preview::Base
|
|
10
|
+
def default
|
|
11
|
+
toast_example(:default, "Event scheduled", "Friday, July 10 at 5:00 PM")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success
|
|
15
|
+
toast_example(:success, "Changes saved", "Your profile has been updated.")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def info
|
|
19
|
+
toast_example(:info, "Heads up", "A new version is available.")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def warning
|
|
23
|
+
toast_example(:warning, "Storage almost full", "You have used 90% of your quota.")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Destructive announces ASSERTIVELY (politeness derives from the
|
|
27
|
+
# variant) - failures the user must hear about.
|
|
28
|
+
def destructive
|
|
29
|
+
toast_example(:destructive, "Payment failed", "Your card was declined.")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# An action-bearing toast defaults to PERSISTENT (duration nil ->
|
|
33
|
+
# 0): a missable undo is a bug.
|
|
34
|
+
# The promise-lifecycle opener: a spinning persistent toast the
|
|
35
|
+
# job later REPLACES (turbo_stream.replace on its id) with the
|
|
36
|
+
# settled success/destructive toast.
|
|
37
|
+
def loading
|
|
38
|
+
render_component(variant: :loading) do |toast|
|
|
39
|
+
toast.with_title { "Creating event…" }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def with_action
|
|
44
|
+
render_component do |toast|
|
|
45
|
+
toast.with_title { "Message deleted" }
|
|
46
|
+
toast.with_action { "Undo" }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Explicit duration: 0 keeps a plain toast on screen until closed.
|
|
51
|
+
def persistent
|
|
52
|
+
render_component(variant: :destructive, duration: 0) do |toast|
|
|
53
|
+
toast.with_title { "Sync failed" }
|
|
54
|
+
toast.with_description { "Changes will retry when you're back online." }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def toast_example(variant, title, description)
|
|
61
|
+
render_component(variant: variant) do |toast|
|
|
62
|
+
toast.with_title { title }
|
|
63
|
+
toast.with_description { description }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Toast
|
|
6
|
+
# Re-expressed through the cn-* theme layer - poetry's own visual
|
|
7
|
+
# (no upstream treatment to mirror). The corner-aware slide chains
|
|
8
|
+
# ride the theme with the rest of the treatment (they key on the
|
|
9
|
+
# TOASTER's data-position through group/toaster at runtime); the
|
|
10
|
+
# default variant stays an empty string (the base IS the default -
|
|
11
|
+
# nothing to name).
|
|
12
|
+
class Style < Poetry::Core::Style
|
|
13
|
+
base "cn-toast pointer-events-auto relative flex w-full items-start outline-hidden"
|
|
14
|
+
|
|
15
|
+
variant :variant, {
|
|
16
|
+
default: "",
|
|
17
|
+
success: "cn-toast-variant-success",
|
|
18
|
+
info: "cn-toast-variant-info",
|
|
19
|
+
warning: "cn-toast-variant-warning",
|
|
20
|
+
destructive: "cn-toast-variant-destructive"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
element :icon, "cn-toast-icon flex shrink-0 items-center justify-center " \
|
|
24
|
+
"[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none"
|
|
25
|
+
element :body, "cn-toast-body flex min-w-0 flex-1 flex-col"
|
|
26
|
+
element :title, "cn-toast-title font-medium text-sm"
|
|
27
|
+
element :description, "cn-toast-description text-muted-foreground text-sm"
|
|
28
|
+
element :action, "shrink-0 self-start"
|
|
29
|
+
element :close, "shrink-0 self-start"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="p-4">
|
|
2
|
+
<%= render Poetry::Ui::ToastTrigger::Component.new(template: "addressed-preview-toast",
|
|
3
|
+
toaster: "preview-region") do %>
|
|
4
|
+
Show in region
|
|
5
|
+
<% end %>
|
|
6
|
+
</div>
|
|
7
|
+
<template id="addressed-preview-toast">
|
|
8
|
+
<%= poetry_toast(duration: 4000) do |toast| %>
|
|
9
|
+
<% toast.with_title { "Addressed stamp" } %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</template>
|
|
12
|
+
<%= poetry_toaster(id: "preview-region") %>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Client-side toast delivery.
|
|
6
|
+
module ToastTrigger
|
|
7
|
+
# A button that shows a toast without a server round-trip: on press
|
|
8
|
+
# the toaster stamps a clone of an addressed <template>'s toast
|
|
9
|
+
# into its region. The template holds one fully rendered
|
|
10
|
+
# poetry_toast - exactly what a Turbo Stream would append - so the
|
|
11
|
+
# visible limit, queueing, and stacking behave the same on either
|
|
12
|
+
# path. For purely client-side moments: copied, undone, queued.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# <%= render Poetry::Ui::ToastTrigger::Component.new(template: "copied-toast") do %>
|
|
16
|
+
# Copy link
|
|
17
|
+
# <% end %>
|
|
18
|
+
# <template id="copied-toast">
|
|
19
|
+
# <%= poetry_toast(duration: 4000) do |toast| %>
|
|
20
|
+
# <% toast.with_title { "Copied" } %>
|
|
21
|
+
# <% end %>
|
|
22
|
+
# </template>
|
|
23
|
+
class Component < Poetry::Core::Component
|
|
24
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
25
|
+
AGENT_RULES = [
|
|
26
|
+
"template: names a <template> element id holding ONE rendered poetry_toast - " \
|
|
27
|
+
"the trigger stamps a clone into the toaster on press.",
|
|
28
|
+
"toaster: scopes the stamp to one region id on multi-toaster pages; omit it for " \
|
|
29
|
+
"the page's toaster.",
|
|
30
|
+
"Give the templated toast a duration (auto-dismiss) - repeated presses stack " \
|
|
31
|
+
"persistent toasts.",
|
|
32
|
+
"Server round-trips keep using turbo_stream.poetry_toast - this trigger is for " \
|
|
33
|
+
"purely client-side moments (copied, undone, queued)."
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
use_stimulus do
|
|
37
|
+
on :root do
|
|
38
|
+
controller :"toast-trigger" do
|
|
39
|
+
register
|
|
40
|
+
value :template
|
|
41
|
+
value :toaster, if: -> { toaster.present? }
|
|
42
|
+
action :fire, on: :click
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
option :template, :string, required: true,
|
|
48
|
+
doc: "The id of the <template> element holding the rendered toast to stamp."
|
|
49
|
+
option :toaster, :string,
|
|
50
|
+
doc: "Scopes the stamp to one toaster region id on multi-toaster pages; omit for the page's toaster."
|
|
51
|
+
option :variant, :symbol, default: :outline, doc: "The Button variant the trigger renders at."
|
|
52
|
+
option :size, :symbol, default: :default, doc: "The Button size the trigger renders at."
|
|
53
|
+
|
|
54
|
+
part "toast-trigger", "The stamping button - press clones the template's toast " \
|
|
55
|
+
"into the toaster region",
|
|
56
|
+
states: {
|
|
57
|
+
"data-variant" => "always - the Button variant the trigger renders at",
|
|
58
|
+
"data-size" => "always - the Button size the trigger renders at"
|
|
59
|
+
}
|
|
60
|
+
# The trigger IS a poetry Button (no wrapper element), so Button's
|
|
61
|
+
# inner anatomy lands in this component's data-component scope.
|
|
62
|
+
part "label", "The Button's label span (the trigger renders AS a poetry Button)"
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def button_options
|
|
66
|
+
wiring = { data: { slot: "toast-trigger" } }
|
|
67
|
+
.merge(stimulus_attributes_for(:root))
|
|
68
|
+
.merge(component_data_attributes)
|
|
69
|
+
|
|
70
|
+
# Caller attributes never clobber the trigger's wiring: both sides
|
|
71
|
+
# flow through Attributes, so a host data-controller/action
|
|
72
|
+
# concatenates instead of silently killing fire-on-click.
|
|
73
|
+
{ variant: variant, size: size }
|
|
74
|
+
.merge(Poetry::Core::HTML::Attributes.merged(wiring, html_attributes))
|
|
75
|
+
.symbolize_keys
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private :button_options
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|